如何使用Java 8的Streams API和Collectors`类收集流并将其转换为Set

46 阅读4分钟

简介

一个流表示一个元素的序列,并支持不同类型的操作,以达到预期的结果。流的源头通常是一个集合或一个数组,数据从那里流出来。

流在几个方面与集合不同;最明显的是,流不是一个存储元素的数据结构。它们在本质上是功能性的,值得注意的是,对一个流的操作会产生一个结果,通常会返回另一个流,但不会修改其来源。

为了 "巩固 "这些变化,你一个流的元素收集回一个Collection

在本指南中,我们将看看如何在Java 8中收集流元素到地图中。

采集器Stream.collect()

收集器代表了Collector 接口的实现,它实现了各种有用的缩减操作,例如将元素累积到集合中,根据特定参数总结元素,等等。

所有预定义的实现都可以在Collectors 类中找到。

你也可以非常容易地实现你自己的收集器,并使用它来代替预定义的收集器--你可以用内置的收集器走得很远,因为它们涵盖了你可能想要使用它们的绝大多数情况。

为了能够在我们的代码中使用这个类,我们需要导入它。

import static java.util.stream.Collectors.*;

Stream.collect() 在流的元素上执行一个可变的还原操作。

可变还原操作在处理流中的元素时,将输入元素收集到一个可变容器中,比如Collection

*Collectors.toSet()*的指南

toSet() 方法是用来将一个流收集成一个集合。它的工作方式与toList() 方法类似,但最终会收集到一个不同的底层数据结构中,通过返回一个Collector ,将输入的元素累积到一个新的Set

值得注意的是,对返回的Set 的类型、可变性、可序列化或线程安全都没有保证。

public static <T> Collector<T,?,Set<T>> toSet()

一个Set ,不允许有重复的元素,或者用更正式的术语来说--集合不包含任何一对元素ab ,这样a.equals(b) ,而且它最多可以包含一个null 元素。

如果你把一个有重复元素的流收集到一个Set - 这是一个快速修剪掉重复元素的方法。

Stream<String> stream = 
    Stream.of("This", "forms", "forms", "a", "short", "a", "sentence", "sentence");
Set<String> sentenceSet = stream.collect(Collectors.toSet());

然而,这个例子强调了Sets填充方式的一个重要特征--元素在收集时不会像在toList() 收集器中那样保留其相对顺序。这是因为Set 的默认实现是HashSet ,它根据元素的哈希值来排序,甚至不能保证这个顺序在一段时间内的一致性。

我们将在后面的章节中看一下我们如何提供一个自定义的Set

运行这段代码的结果是

[sentence, a, This, short, forms]

由于我们很少只处理字符串或原始对象--让我们定义一个简单的类来表示一个Book

public class Book {
    private String title;
    private String author;
    private int releaseYear;
    private int soldCopies;

    // Constructor, getters and setters
}

有了它,我们就可以创建一个简单的几本书的列表(有一个重复的条目)。

List<Book> books = Arrays.asList(
    new Book("The Fellowship of the Ring", "J.R.R. Tolkien", 1954, 30),
    new Book("The Hobbit", "J.R.R. Tolkien", 1937, 40),
    new Book("Animal Farm", "George Orwell", 1945, 32),
    new Book("Nineteen Eighty-Four", "George Orwell", 1949, 50),
    new Book("Nineteen Eighty-Four", "George Orwell", 1949, 38)
);

Predicate 一个相当典型的流水线包括一个基于一些filter() ,然后再收集回一个集合。

Set<String> booksGeorgeOrwell = books.stream()
                .filter(book->book.getAuthor()
                .equals("George Orwell") && book.getCopiesSold() >= 30)
                .map(Book::getTitle).collect(Collectors.toSet());

System.out.println(booksGeorgeOrwell);

正如我们前面所讨论的,Set,不允许有重复的内容。在我们检查了我们给出的子句以及List 中的哪些书符合标准后,我们应该有以下结果。

[Animal Farm, Nineteen Eighty-Four]

效果很好!重复的条目被修剪掉了,没有明确的逻辑来修剪掉重复的条目。

结论

在本指南中,我们已经看了如何使用Java 8的Streams API和Collectors 类来收集流并将其转换为一个Set。