2009-11-24 144 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