【问题标题】:How to filter a list by multiple properties in Java?如何在 Java 中按多个属性过滤列表?
【发布时间】:2017-08-09 17:39:38
【问题描述】:

我正在尝试利用 Java 的函数库从列表中过滤掉一些条目。

我有一个 Book 对象列表,首先按 Book ID 排序,然后按修订号排序。下面的示例我在列表中有九个 Book 对象。

Book: ID 1, Revision Number 1
Book: ID 1, Revision Number 2
Book: ID 1, Revision Number 3
Book: ID 2, Revision Number 1
Book: ID 2, Revision Number 2
Book: ID 2, Revision Number 3
Book: ID 3, Revision Number 1
Book: ID 3, Revision Number 2
Book: ID 3, Revision Number 3

我想过滤列表,以便我只有每个图书 ID 的最高修订号。

Book: ID 1, Revision Number 3
Book: ID 2, Revision Number 3
Book: ID 3, Revision Number 3

Java 是否具有使用过滤器或比较器之类的功能来允许我根据多个字段、基于公共值(图书 ID)进行过滤?我想尽量避免编写自己的函数来遍历集合并尽可能进行过滤...有什么建议吗?

【问题讨论】:

  • 可能是 TreeSet 和一些比较器
  • 可以用merge功能采集到map,可行吗?
  • 如果您使用的是 java 8 并且事先知道要过滤的值,请参阅stackoverflow.com/questions/122105/… 基于Collection.removeIf() 的解决方案可能最适合您的需求
  • 你可以做类似books.stream().collect(groupingBy(Book::getId, maxBy(Comparator.comparing(Book::getRevision))))

标签: java sorting lambda


【解决方案1】:

这样的事情怎么样:

List<Book> books = Arrays.asList(
        new Book(1, 2),
        new Book(1, 3),
        new Book(2, 2),
        new Book(2, 3)
);

System.out.println(
    books.stream()
            .collect(
                    groupingBy(Book::getID,
                        maxBy(comparingInt(Book::getRevison))
                    )
            )

);// {1=Optional[Book{ID=1, revison=3}], 2=Optional[Book{ID=2, revison=3}]}

更新如果最后你只需要一个List&lt;Book&gt;,只需在Map&lt;Integer,Optinal&lt;Book&gt;&gt;的值上使用流

.values().stream().map(Optional::get).collect(Collectors.toList()));

完整示例:

System.out.println(books.stream()
                .collect(
                        groupingBy(Book::getID,
                                maxBy(comparingInt(Book::getRevison))
                        )
                ).values()
                .stream()
                .map(Optional::get)
                .collect(Collectors.toList())
);

【讨论】:

  • 看起来做我想做的事,虽然我应该指定,但它需要返回新的、排序的 List,这似乎用这种方法是不可能的?
  • @deanmau5,有可能请查看我的更新以获得答案。
【解决方案2】:

这不是基于多个属性的真正过滤,但可以解决您的问题:

final Map<String, Book> books = list.stream().collect(
    Collectors.toMap(
        b -> b.getId(), 
        b -> b,
        (b1, b2) -> {
            if(b1.getNumber() > b2.getNumber()) {
                return b1;
            }
            return b2;
        })
);

它将返回包含 id 作为 key 的 map,以及具有最大数字的 Book(具有唯一 id)。

toMap 收集器中,您可以添加merge function,这将决定当同一键发生冲突时该怎么办。在这种情况下,它会返回更大数量的书。

如果您想从该电话中收集values()

【讨论】:

  • groupingBy() 更适合这个。请参阅我上面的评论。
  • 同意,但是,您仍然必须为Collectors.maxBy() 创建比较器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
  • 2019-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多