1. Overview
1.概述
One significant new feature in Java 8 is the Stream API. Streams allow us to process elements conveniently from different sources, such as arrays or collections.
Java 8的一个重要的新功能是流API。流允许我们方便地处理来自不同来源的元素,例如数组或集合。
Further, using the Stream.collect() method with corresponding Collectors, we can repack the elements to different data structures like Set, Map, List, and so on.
此外,使用Stream.collect()方法与相应的Collectors,我们可以将元素重新打包成不同的数据结构,如Set,Map,List等。
In this tutorial, we’ll explore how to collect elements in a Stream into a TreeSet.
在本教程中,我们将探讨如何将Stream中的元素收集到TreeSet中。
2. Collecting Into a TreeSet With Natural Ordering
2.收集成一个具有自然排序的树集
Simply put, the TreeSet is a sorted Set. The elements in a TreeSet are ordered using their natural ordering or a provided Comparator.
简单地说,TreeSet是一个排序的集合。TreeSet中的元素使用其自然排序或提供的Comparator来排序。
We’ll first look at how to collect Stream elements using their natural ordering. Then, let’s focus on collecting elements using custom Comparator cases.
我们首先看一下如何使用自然排序来收集Stream元素。然后,让我们专注于使用自定义Comparator案例来收集元素。
For simplicity, we’ll use unit test assertions to verify if we’ve got the expected TreeSet result.
为了简单起见,我们将使用单元测试断言来验证我们是否得到了预期的TreeSet结果。
2.1. Collecting Strings Into a TreeSet
2.1.将字符串收集到一个TreeSet中
Since String implements the Comparable interface, let’s first take String as an example to see how to collect them in a TreeSet:
由于String实现了Comparable接口,我们首先以String为例,看看如何在TreeSet中收集它们。
String kotlin = "Kotlin";
String java = "Java";
String python = "Python";
String ruby = "Ruby";
TreeSet<String> myTreeSet = Stream.of(ruby, java, kotlin, python).collect(Collectors.toCollection(TreeSet::new));
assertThat(myTreeSet).containsExactly(java, kotlin, python, ruby);
As the test above shows, to collect Stream elements into a TreeSet, we just pass TreeSet‘s default constructor as a method reference or a lambda expression to the Collectors.toCollection() method.
正如上面的测试所示,要将Stream元素收集到TreeSet。我们只需将TreeSet的默认构造函数作为方法引用或lambda表达式传递给Collectors。toCollection()方法。
If we execute this test, it passes.
如果我们执行这个测试,它就会通过。
Next, let’s see a similar example with a custom class.
接下来,让我们看一个类似的例子,用一个自定义类。
2.2. Collecting Players With Their Natural Ordering
2.2.以自然顺序收集玩家
First, let’s have a look at our Player class:
首先,让我们看一下我们的Player类。
public class Player implements Comparable<Player> {
private String name;
private int age;
private int numberOfPlayed;
private int numberOfWins;
public Player(String name, int age, int numberOfPlayed, int numberOfWins) {
this.name = name;
this.age = age;
this.numberOfPlayed = numberOfPlayed;
this.numberOfWins = numberOfWins;
}
@Override
public int compareTo(Player o) {
return Integer.compare(age, o.age);
}
// getters are omitted
}
As the class above shows, our Player class implements the Comparable interface. Further, we’ve defined its natural ordering in the compareTo() method: player’s age.
正如上面的类所示,我们的Player类实现了Comparable接口。此外,我们在compareTo()方法中定义了它的自然排序:播放器的年龄。。
So next, let’s create a few Player instances:
所以接下来,让我们创建几个Player实例。
/* name | age | num of played | num of wins
--------------------------------------------- */
Player kai = new Player( "Kai", 26, 28, 7);
Player eric = new Player( "Eric", 28, 30, 11);
Player saajan = new Player("Saajan", 30, 100, 66);
Player kevin = new Player( "Kevin", 24, 50, 49);
As we’ll use these four player objects for other demonstrations later, we put the code in a table-like format to easily check each player’s attribute values.
由于我们稍后将在其他演示中使用这四个球员对象,我们将代码放在类似表格的格式中,以方便检查每个球员的属性值。
Now, let’s collect them in a TreeSet with their natural order and verify if we have the expected result:
现在,让我们把它们按照自然顺序收集到一个TreeSet中,并验证我们是否有预期的结果。
TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin).collect(Collectors.toCollection(TreeSet::new));
assertThat(myTreeSet).containsExactly(kevin, kai, eric, saajan);
As we can see, the code is pretty similar to collecting strings into a TreeSet. Since Player‘s compareTo() method has specified the “age” attribute as its natural ordering, we verify the result (myTreeSet) with the players sorted by age ascending.
正如我们所看到的,该代码与收集字符串到TreeSet相当相似。由于Player的compareTo()方法已经指定了“age”属性作为其自然排序,我们验证结果(myTreeSet)是按年龄升序排序的球员。
It’s worth mentioning that we’ve used AssertJ‘s containsExactly() method to verify the TreeSet contains precisely the given elements in order and nothing else.
值得一提的是,我们使用了AssertJ的containsExactly()方法来验证TreeSet是否精确地包含了给定的元素的顺序,而不是其他。
Next, we’ll look at how to collect these players into a TreeSet using a customized Comparator.
接下来,我们将看看如何使用自定义的Comparator将这些球员收集到一个TreeSet。
3. Collecting Into a TreeSet With a Customized Comparator
3.用自定义的比较器收集到一个树集中
We’ve seen Collectors.toCollection(TreeSet::new) allows us to collect elements in a Stream to a TreeSet in their natural ordering. The TreeSet provides another constructor that accepts a Comparator object as the argument:
我们已经看到Collectors.toCollection(TreeSet::new)允许我们将Stream中的元素按照其自然顺序收集到TreeSet。TreeSet提供了另一个构造函数,接受一个Comparator对象作为参数。
public TreeSet(Comparator<? super E> comparator) { ... }
Therefore, if we want the TreeSet to apply a different ordering on the elements, we can create a Comparator object and pass it to the constructor mentioned above.
因此,如果我们想让TreeSet在元素上应用不同的排序,我们可以创建一个Comparator对象并将其传递给上面提到的构造函数。
Next, let’s collect these players in a TreeSet by their number of wins instead of their ages:
接下来,让我们按照胜场数而不是年龄来收集这些球员的TreeSet。
TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin)
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparingInt(Player::getNumberOfWins))
));
assertThat(myTreeSet).containsExactly(kai, eric, kevin, saajan);
This time, we’ve used a lambda expression to create the TreeSet instance. Moreover, we’ve passed our own Comparator using Comparator.comparingInt() to the TreeSet‘s constructor.
这一次,我们使用了一个lambda表达式来创建TreeSet实例。此外,我们使用Comparator.comparingInt()向TreeSet的构造函数传递我们自己的Comparator。
Player::getNumberOfWins references the attribute’s value we need to compare players.
Player::getNumberOfWins引用我们需要比较球员的属性值。
The test passes when we give it a run.
当我们让它运行时,测试通过了。
However, the required comparison logic is sometimes not as simple as just comparing an attribute’s value as the example shows. For example, we may need to compare the results of some additional calculations.
然而,所需的比较逻辑有时并不像例子中显示的那样只是比较一个属性的值那么简单。例如,我们可能需要比较一些额外计算的结果。
So finally, let’s collect these players in a TreeSet again. But this time, we want them to be sorted by their win rate (number of wins/number of played):
所以最后,让我们再次将这些球员收集到TreeSet中。但这一次,我们希望他们按照胜率(胜场数/出场数)进行排序。
TreeSet<Player> myTreeSet = Stream.of(saajan, eric, kai, kevin)
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(player -> BigDecimal.valueOf(player.getNumberOfWins())
.divide(BigDecimal.valueOf(player.getNumberOfPlayed()), 2, RoundingMode.HALF_UP)))));
assertThat(myTreeSet).containsExactly(kai, eric, saajan, kevin);
As the test above shows, we’ve used the Comparator.comparing(Function keyExtractor) method to specify the comparable sort key. In this example, the keyExtractor function’s a lambda expression, which calculates a player’s win rate.
正如上面的测试所示,我们使用了Comparator.comparing(Function keyExtractor)方法来指定可比的排序键。在这个例子中,keyExtractor函数是一个lambda表达式,它计算了一个玩家的胜率。
Also, if we run the test, it passes. So we’ve got the expected TreeSet.
另外,如果我们运行测试,它通过了。所以我们已经得到了预期的TreeSet。
4. Conclusion
4.总结
In this article, we’ve discussed through examples how to collect elements in a Stream into a TreeSet by their natural ordering and custom comparators.
在这篇文章中,我们通过实例讨论了如何通过自然排序和自定义比较器将Stream中的元素收集到TreeSet中。
As always, the complete source code for the examples is available over on GitHub.
一如既往,例子的完整源代码可在GitHub上获得。