【问题标题】:Filtering a list of JavaBeans with Google Guava使用 Google Guava 过滤 JavaBean 列表
【发布时间】:2012-01-28 00:29:28
【问题描述】:

在 Java 程序中,我有一个要根据特定属性过滤的 bean 列表。

例如,假设我有一个 Person 列表,一个 JavaBean,其中 Person 有许多属性,其中包括 'name'。

我还有一个名字列表。

现在我要查找姓名在名单中的所有人员。

使用 Google Guava 执行此过滤器的最佳方法是什么?

到目前为止,我一直在考虑将 Guava 与 Apache beanutils 结合起来,但这似乎并不优雅。

我还在这里找到了一个反射扩展库:http://code.google.com/p/guava-reflection/,但我不确定如何使用它(文档很少)。

有什么想法吗?

附言你能说我真的很怀念 Python 列表理解吗?

【问题讨论】:

    标签: java functional-programming guava


    【解决方案1】:

    这是一个使用 guava 使用泛型的示例,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);
    }
    

    【讨论】:

      【解决方案2】:

      在 Java8 中,您可以使用 Collection.removeIf()

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

      这当然会修改当前列表。

      【讨论】:

        【解决方案3】:

        使用 Java8 风格,您可以使用流 + 过滤器来实现您的目标。

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

        【讨论】:

          【解决方案4】:

          用老式的方式做,不用番石榴。 (以 Guava 开发人员的身份发言。)

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

          您可以使用 Guava 做到这一点,但 Java 不是 Python,试图将其变成 Python 只会使笨拙和不可读的代码永久存在。 Guava 的功能实用程序应谨慎使用,并且仅当它们为代码行或性能提供具体且可衡量的好处时。

          【讨论】:

          • 这个解决方案和我的主要区别在于这个解决方案创建了一个包含过滤人员的新列表,而我的解决方案创建了一个原始列表的视图。一种或另一种更可取取决于用例。
          • 是的,这取决于用例——但我想说,在 90% 的用例中,可读性的提高超过了性能上的小幅提升。
          • 您似乎认为唯一妥协的两个方面是可读性和性能。正确性远比(假定的)可读性和性能更重要。如果你这样做,你不仅要测试你的谓词逻辑,还要测试你的过滤逻辑。你已经不必要地增加了一倍(至少,因为引入了可变状态)可能出错的事情的数量。将其乘以您在代码库中进行过滤的次数……您为什么要这样工作?
          • 我同意你应该尽量减少暴露的可变性。返回 ArrayList 的 ImmutableList 副本,而不是 ArrayList 本身。但是作为一名代码审查者,我更喜欢阅读这段代码而不是其他代码。代码的显着部分更容易识别。如果这段代码返回一个 ImmutableList,我会满足于一两个测试,加上谓词测试。
          • 这是番石榴的官方立场,请注意。 Guava 用户指南(我写的)在goo.gl/TCHHz 提供了更多详细信息。我应该补充一点,随着 Java 8 和 Project Lambda 的出现,我们的位置将发生 180 度的变化……尽管到那时,我们可能会重写库的所有功能部分。 ;)
          【解决方案5】:

          如果您在单线程应用程序中使用LinkedList(或任何其他删除操作不是很费力的集合),最有效的解决方案是:

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

          【讨论】:

          • 哎哟!这将不起作用,因为您将在对列表的并发访问中运行
          • 谢谢!我已经确定了答案。
          【解决方案6】:

          作为 guava-reflection 的开发者,我很抱歉我在这么早的阶段就放弃了这个项目(我有一份日常工作和一个妻子和孩子 :-))。我的愿景是这样的:

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

          现有代码大约有 60%,所以如果您有兴趣,请联系我,也许我们可以一起完成。

          【讨论】:

            【解决方案7】:

            从句子中解释你的疑惑:

            到目前为止,我一直在考虑将 Guava 与 Apache beanutils 结合使用,但是 这似乎不太优雅。

            Java,尽管如此流行,但缺乏对first-class function 的支持*subject to change in Java 8 是什么,你可以在哪里做:

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

            使用 lambdas 会很优雅。

            在此之前,您可以选择:

            • 老派的方式(就像@Louis 写的)
            • 详细番石榴过滤器(@JB 的回答)
            • 或其他功能性 Java 库(@superfav 的回答)。

            我还想在@Lois 的回答中添加Guava-way would be to create immutable collection,因为they are better than unmodifiable,在Effective Java 中的Item 15, Minimize mutability 中也有描述 > 约书亚·布洛赫**

            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

            **:Guava 和 Bloch 在许多方面紧密耦合 ;)

            【讨论】:

              【解决方案8】:

              我非常同意 Louis 和 JB 的回答。我不知道 guava-reflection,也许 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 是您正在寻找的......

              【讨论】:

                【解决方案9】:
                Iterable<Person> filtered = Iterables.filter(allPersons, new Predicate<Person>() {
                    @Override
                    public boolean apply(Person p) {
                        return acceptedNames.contains(p.getName());
                    }
                });
                

                如果您的名称列表很大,您最好将其转换为 Set(最好是 HashSet)并在此集合上调用 contains,而不是列表,因为对于 HashSet,contains 是 O(1),而 O (n) 用于列表。

                【讨论】:

                • 应该转换成集合的是接受的名字列表,而不是人员列表。这就是包含的内容。将人员列表转换为集合没有附加值。
                • @Daniel 对另一个答案的评论提供了一些背景信息,说明为什么在某些情况下这样做更可取。
                猜你喜欢
                • 2013-10-10
                • 2015-11-01
                • 2017-01-21
                • 1970-01-01
                • 1970-01-01
                • 2011-09-08
                • 1970-01-01
                • 2020-07-04
                • 2016-05-26
                相关资源
                最近更新 更多