2009-11-24 199 views
3

我有这样的事情:Java集合过滤

public class Foo { 
    public String id; 
} 

Vector<Foo> foos; 

我需要通过ID来获得从集合对象。

在C#中,我会做这样的:foos.Where(o => o.id = 7)

怎样做,在Java中的最佳方式是什么?

+1

你正在寻找适应序列或任何集合类型?如果只有序列,你能否假定序列被排序?适合每种算法和时间复杂性成本的算法是不同的。将一个解决方案应用于所有类型的集合将会对其中至少一个集合造成损害。 – seh 2009-11-24 12:39:31

+0

是的,ids排序他们来了像1,2,3,虽然他们是字符串 – Omu 2009-11-24 12:47:04

回答

5

您可能希望将您的数据存储在地图<整数,Foo>而不是列表< Foo>中。例如,一个TreeMap将按顺序排列所有内容。

1
​​

你传递你的集合和关键字(一个id或其他),然后方法返回你的对象。你的对象的类必须实现Comparable接口。

注:集合必须调用binarySearchCollections.sort(..)

+1

*和*列表将需要排序。 – 2009-11-24 12:18:58

+1

只有当'Vector'按'id'排序时,它才会起作用吗? – abyx 2009-11-24 12:19:17

+0

是的,补充说。 – Bozho 2009-11-24 12:24:17

11

一开始之前进行排序,我建议使用ArrayList<Foo>,而不是Vector<Foo> - ArrayList几乎总是最好Vector

使用Google Collections API,特别是Iterables.filter。现在它非常笨重 - 由于缺少lambda表达式,您需要预先设置谓词,或者使用匿名内部类。另外,Java没有扩展方法,所以你可以调用Iterables.filter(collection, predicate)而不是collection.filter(predicate)。这两项将在Java 7中

注有所简化,使用filter会发现一个Iterable<Foo> - 如果你只需要在第一场比赛中,使用Iterables.find相反,这是Enumerable.First<T>(Func<T, bool>)在LINQ的等价物。

+0

过滤函数的问题在于迭代器遍历集合的所有元素,而不管您正在搜索的对象在哪里。只要找到对象,就想立即退出循环。 – 2017-09-11 11:10:53

+0

@LucaFagioli:与更现代的Java流相比,Iterables现在有点不受欢迎,但我的理解是'Iterables.filter'仍然是懒惰的 - 您可以只返回返回的iterable的第一个元素,那就没问题了。 – 2017-09-11 11:15:39

+0

是的,你可以,但我的意思是说,要返回迭代器,该功能仍然需要检查集合中的所有元素。从[文档](https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Iterables.html#filter-java.lang.Iterable-java.lang。 Class-):_返回包含所有类型为'desiredType'的元素的未过滤的视图。这导致_average_个案的复杂度为O(n),而手动搜索对象时_worst_个案复杂度为O (N)。 – 2017-09-11 11:36:59

3

首先,不使用Vector,使用ArrayList

ArrayList<Widget> widgets = ... 

Widget found = null; 

for (Widget o : widgets) 
{ 
    if (o.id == 7) 
    { 
    found = o; 
    break; 
    } 

} 
+2

因此,这只扫描第一个项目? (你的'break'是错误的)。 – BalusC 2009-11-24 12:32:16

+0

你可能是想把'break'放在'if'语句里面...... – 2009-11-24 12:32:57

+0

我已经被告知Vectors是线程安全的,并且ArrayList不是 – Omu 2009-11-24 12:33:08

1

如果你有一个ArrayList(或类似的 - 即从Collection的图书馆的东西),然后Apache Commons Collections有很多的设施,为过滤/迭代等

注意,与乔恩的回答中引用的谷歌集合库中,有一个为泛型的支持。

1

我认为,在Java中,传统的方式是通过列表进行迭代,并与你看了(复杂度为O(n))的ID搜索富。如果这样慢,你可能会考虑使用HashMap结构,将foo映射到它的索引。

人们可以“隐藏”查找通过继承集合类:

public class ListOfFoos extends ArrayList<Foo> { 

    public Foo getFooByIndex(String index) { 
    // do your lookup here 
    } 

} 

,并使用ListOfFoos而不是ArrayList的从现在起一个新的集合类型,允许直接存取权限由它的索引号富。

0

以下类型提供了对序列的过滤。这个解决方案是通用的,但不适用于集合或排序的序列,每个序列都提供更有效的方法来查找和删除与某些示例匹配的元素。

首先,定义一个Iterator型这真是懒发电机适配器:

abstract class IteratorHusk<T> implements Iterator<T> 
{ 
    @SuppressWarnings("unchecked") 
    protected IteratorHusk() 
    { 
    value_ = nil(); 
    } 


    @SuppressWarnings("unchecked") 
    protected T nil() 
    { 
    return (T) NIL; 
    } 


    protected abstract T yield(); 


    private boolean tryPop() 
    { 
    value_ = yield(); 
    return NIL != value_; 
    } 


    @SuppressWarnings("unchecked") 
    private T take() 
    { 
    final T current = value_; 
    value_ = (T) NIL; 
    return current; 
    } 


    public final boolean hasNext() 
    { 
    return NIL != value_ || tryPop(); 
    } 


    public final T next() 
    { 
    if (NIL == value_ && !tryPop()) 
    { 
     throw new NoSuchElementException(); 
    } 
    return take(); 
    } 


    public void remove() 
    { 
    throw new UnsupportedOperationException(); 
    } 


    // We want to tolerate null as a possibly valid value. 
    private static final Object NIL = new Object(); 
    private T value_; 
} 

这是2009年和Java仍然缺乏倒闭和一流的功能,所以我们不好意思地介绍这个家庭:

interface UnaryFunction<T, U> 
{ 
    T eval(U argument); 
} 

现在,围绕一元谓词包裹一个生成器来构建一个序列过滤器:

class FilteringIterator<T> extends IteratorHusk<T> 
{ 
    public FilteringIterator(Iterator<? extends T> iter, 
          UnaryFunction<Boolean, ? super T> pred) 
    { 
    iter_ = iter; 
    pred_ = pred; 
    } 


    @Override 
    protected T yield() 
    { 
    while (iter_.hasNext()) 
    { 
     final T val = iter_.next(); 
     if (!pred_.eval(val)) 
     { 
     return val; 
     } 
    } 
    return nil(); 
    } 


    private final Iterator<? extends T> iter_; 
    private final UnaryFunction<Boolean, ? super T> pred_; 
} 

现在,暴露出一个方便的功能:

public static <T> 
Iterator<T> lazyFilter(UnaryFunction<Boolean, ? super T> pred, 
         Iterator<? extends T> source) 
{ 
    return new FilteringIterator<T>(source, pred); 
} 
+2

自己编写所有的代码将是愚蠢的,然后不得不单独测试它(这比写它更难,相信我)..只是使用一个库。 – 2009-11-24 15:13:47

+0

这是我为内部使用而编写的较大型图书馆的一部分。有几个生成器与IteratorHusk类型一起使用。过滤一个包装的迭代器就是其中之一。 – seh 2009-11-24 15:41:06

6

随着Google Collections,这将是:

Lists.newArrayList(Iterables.filter(foos, new Predicate<Foo>() { 
    public boolean apply(Foo input) { 
    return input != null && "7".equals(input.id); 
    } 
})); 

Iterables.filter(和Collections2.filter,这不相同),得到你可以看到过滤后的集合,就像seh的概念一样,但代码少。为了再次创建列表,我将它传递给newArrayList Google Collection的列表实用工具类的方法。

就像其他人一样,我强烈建议不要使用Vector作为声明。相反,尽量使用可能的最通用的类​​型,例如,列表< Foo>或甚至收集< Foo>。另外,除非您需要Vector的同步功能,否则请使用ArrayList(或其他适合该问题的其他类型)。

1

看一看lambdaj。它允许以伪功能和非常可读的方式对集合进行操作,过滤,排序和聚合。

0

sweetener项目的限制类解决了这个问题。

例子:

Collection<Foo> filteredList = Collections.filter(foos, Criteria.newCriteria().add(Restrictions.equals("id", 7))); 

Other examples

0

如果您收藏已经排序,你可以把二进制搜索,让你的O最坏情况的复杂性优势(log n)的:

​​

如果您可以自由选择数据结构,请使用HashMap<String, Object>,这会给您带来O(1)的复杂性。

ps:使用ArrayList而不是Vector