2011-12-26 59 views
22

在Java程序中,我有一個我想根據特定屬性進行過濾的bean列表。使用Google Guava過濾JavaBeans的列表

例如,假設我有一個Person,一個JavaBean的列表,其中Person有很多屬性,其中包括'name'。

我也有一個名字列表。

現在我想查找名稱在名稱列表中的所有人員。

使用Google Guava執行此過濾器的最佳方法是什麼?

到目前爲止,我已經想過將番石榴和Apache beanutils結合起來,但看起來並不高雅。

我也在這裏找到了一個反射擴展庫:http://code.google.com/p/guava-reflection/,但我不確定如何使用它(幾乎沒有文檔)。

有什麼想法?

p.s.你能告訴我真的想念Python列表理解嗎?

回答

42

做它的老式的方式,沒有番石榴。 (說到作爲番石榴開發商。)

List<Person> filtered = Lists.newArrayList(); 
for(Person p : allPersons) { 
    if(acceptedNames.contains(p.getName())) { 
     filtered.add(p); 
    } 
} 

你可以用番石榴做到這一點,但Java是不是Python的,並試圖把它做成的Python只是要延續尷尬和不可讀的代碼。番石榴的功能應用應該謹慎使用,只有當它們爲代碼或性能提供具體和可衡量的好處時。

+0

這個解決方案和我的主要區別在於,這個解決方案創建了一個包含過濾人員的新列表,而我的原始列表創建了一個視圖。無論哪一個都是可取的取決於用例。 – 2011-12-27 08:04:44

+0

是的,這取決於用例 - 但我會說在90%的用例中,可讀性改進大於小性能優勢。 – 2011-12-28 17:53:54

+3

你似乎認爲只有兩個妥協方面是可讀性和性能。正確性比兩種(假定的)可讀性和性能都重要得多。如果你這樣做,你不僅要測試你的謂詞邏輯,而且要測試你的過濾邏輯。你不必要地增加了一倍(至少因爲引入了可變狀態)可能出錯的事情的數量。通過在代碼庫中進行篩選的次數乘以此數量......爲什麼要這樣工作? – 2012-01-19 02:35:46

22
Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() { 
    @Override 
    public boolean apply(Person p) { 
     return acceptedNames.contains(p.getName()); 
    } 
}); 

如果你的名單是大,你最好將其轉變爲一個集(HashSet的,preferrably),並調用包含這一套,而不是列表,因爲包含爲O一(1) HashSet和O(n)的列表。

+1

應該怎樣轉換爲一組,以過濾任何名單是接受的名稱,人員不在名單之列。這就是所謂的包含。將人員列表轉換爲集合沒有附加價值。 – 2011-12-27 10:06:57

+0

@丹尼爾對其他答案的評論提供了一些背景,說明爲什麼在某些情況下這會更可取。 – studgeek 2012-11-14 23:07:28

3

我不能同意路易和JB的答案。我不知道番石榴反思,也許LambdaJ可能是你在找什麼:

// set up 
Person me = new Person("Favio"); 
Person luca = new Person("Luca"); 
Person biagio = new Person("Biagio"); 
Person celestino = new Person("Celestino"); 
Collection<Person> meAndMyFriends = asList(me, luca, biagio, celestino); 

// magic 
Collection<Person> filtered = filter(having(on(Person.class).getName(), 
              isOneOf("Favio", "Luca")), 
            meAndMyFriends); 

// test 
assertThat(filtered, hasItems(me, luca)); 
assertEquals(2, filtered.size()); 

也許Scala中,Clojure的或Groovy,你在找什麼...

5

從解釋你的疑慮一句話:

到目前爲止,我已經想過番石榴與Apache BeanUtils的結合,但 似乎並不優雅。

的Java,儘管是如此受歡迎,缺乏first-class function支持*,什麼是subject to change in Java 8,在這裏您將能夠做到:

Iterable <Person> filtered = filter(allPersons, (Person p) -> acceptedNames.contains(p.getName())); 

隨着lambda表達式,這將是優雅。

在那之前你已經選擇之間:

  • 老派的方式(如@Louis寫)
  • 詳細番石榴過濾器(@ JB的答案)
  • 或其他功能的Java庫(@ superfav的答案)。

我還想添加到@洛伊絲的回答是Guava-way would be to create immutable collection,因爲they are better than unmodifiable,這也是在第15項,最大限度地減少可變性有效的Java由Joshua布洛赫**描述:

ImmutableList.Builder<Person> builder = ImmutableList.builder(); 
for (final Person p : allPersons) { 
    if (acceptedNames.contains(p.getName())) { 
     builder.add(p); 
    } 
} 
ImmutableList<Person> filtered = builder.build(); 

(它的實施細節ImmutableList.Builder創建臨時ArrayList)。

*:它困擾了我很多,我在Python,JavaScript和Perl的世界來了,where functions are treated better

**:番石榴和布洛赫是緊密結合在許多方面;)

2

作爲番石榴反射的開發者,我很遺憾我在這樣的早期階段放棄了這個項目(我有一份日常工作和一個妻子:-))。我的設想是這樣的:

Iterable<Object> thingsWithNames = 
    Iterables.filter(someData, 
        // this is a Predicate, obviously 
        BeanProperties.hasBeanProperty("name", String.class)); 

現有代碼爲約60%在那裏,所以如果你有興趣,請聯繫我,也許我們可以得到這樣一起完成。

0

如果你在單線程應用程序中使用LinkedList(或任何其他集合這刪除算子的研究還不是很費力的)最有效的解決辦法是:

final Iterator<User> userIterator = users.iterator(); 
while (userIterator.hasNext()) { 
    if (/* your condition for exclusion */) { 
     userIterator.remove(); 
    } 
} 
+0

哎唷!這將無法正常工作,因爲您將在並行訪問列表中運行 – 2013-07-19 13:29:53

+0

謝謝!我已經解決了我的答案。 – 2013-07-20 14:29:05

0

隨着Java8風格,你可以使用流+過濾器來實現你的目標。

persons.stream() 
      .filter(p -> names.contains(p.getName())) 
      .collect(Collectors.toList()); 
0

隨着Java8你可以使用Collection.removeIf()

List<Person> theList = ...; 
theList.removeIf(
    (Person p)->"paul".equals(p.getName()) 
); 

這當然會修改當前列表中。

0

下面是一個使用泛型利用番石榴的一個例子,使用的BeanUtils要求匹配

/** 
* Filter List 
* 
* @param inputList 
* @param requestMatch 
* @param invokeMethod 
* @return 
*/ 
public static <T> Iterable<T> predicateFilterList(List<T> inputList, final String requestMatch, 
     final String invokeMethod) { 
    Predicate<T> filtered = new Predicate<T>() { 
     @Override 
     public boolean apply(T input) { 
      boolean ok = false; 
      try { 
       ok = BeanUtils.getProperty(input, invokeMethod).equalsIgnoreCase(requestMatch); 
      } 
      catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return ok; 
     } 
    }; 
    return Iterables.filter(inputList, filtered); 
}