2011-12-26 58 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); 
}