对集合排序
流的目的不仅是在集合类之间做转换, 而且同时提供了一组处理数据的通用操作。 有些集合本身是无序的, 但可以在流中进行顺序。
@Test
public void lambdaCollectOrder(){
Set<Integer> numbers = new HashSet<>(Arrays.asList(4 , 3 , 2 , 1));
List<Integer> sameOrder = numbers.stream().sorted().collect(Collectors.toList());
Assert.assertEquals(asList(1 , 2 , 3 , 4) , sameOrder);
}
转换成其他集合
有一些收集器可以生成其他集合。 比如前面已经见过的 toList,生成了 java.util.List 类的实例。 还有 toSet 和 toCollection, 分别生成 Set 和 Collection 类的实例。
你希望使用一个特定的集合收集值, 而且你可以稍后指定该集合的类型。 比如, 你可能希望使用 TreeSet, 而不是由框架在背后自动为你指定一种类型的Set。 此时就可以使用 toCollection, 它接受一个函数作为参数, 来创建集合。
@Test
public void lambdaCollectOrder(){
Set<Integer> numbers = new HashSet<>(Arrays.asList(4 , 3 , 2 , 1));
List<Integer> sameOrder = numbers.stream().sorted().collect(Collectors.toCollection(ArrayList::new));
Assert.assertEquals(asList(1 , 2 , 3 , 4) , sameOrder);
}
转换成值
查找最大最小值
还可以利用收集器让流生成一个值。 maxBy 和 minBy 允许用户按某种特定的顺序生成一个最大和最小值。
@Test
public void lambdaGetValueFromCollect() {
Artist artist1 = new Artist("zzl-1", Collections.EMPTY_LIST, "china");
Artist artist2 = new Artist("zzl-2", Collections.EMPTY_LIST, "china");
Artist artist3 = new Artist("zzl-3", Collections.EMPTY_LIST, "china");
Artist artist4 = new Artist("zzl-4", Collections.EMPTY_LIST, "china");
Artist artist5 = new Artist("zzl-5", Collections.EMPTY_LIST, "china");
Artist artist6 = new Artist("zzl-6", Collections.EMPTY_LIST, "china");
Artist artist7 = new Artist("zzl-7", Collections.EMPTY_LIST, "china");
List<Artist> members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
List<Artist> members2 = Stream.of(artist3, artist4, artist5, artist6).collect(Collectors.toList());
List<Artist> members3 = Stream.of(artist7).collect(Collectors.toList());
Artist artist9 = new Artist("zzl-mid", members1, "china");
Artist artist10 = new Artist("zzl-max", members2, "china");
Artist artist11 = new Artist("zzl-min", members3, "china");
//最大值
Stream<Artist> artists1 = Stream.of(artist9 , artist10 , artist11);
Optional<Artist> optionalArtist1 = biggestGroup(artists1);
System.out.println(optionalArtist1.get());
//最小值
Stream<Artist> artists2 = Stream.of(artist9 , artist10 , artist11);
Optional<Artist> optionalArtist2 = minimumGroup(artists2);
System.out.println(optionalArtist2.get());
}
public Optional<Artist> biggestGroup(Stream<Artist> artists) {
Function<Artist, Long> getCount = artist -> artist.getMembers().count();
return artists.collect(Collectors.maxBy(Comparator.comparing(getCount)));
}
public Optional<Artist> minimumGroup(Stream<Artist> artists) {
Function<Artist, Long> getCount = artist -> artist.getMembers().count();
return artists.collect(Collectors.minBy(Comparator.comparing(getCount)));
}
求平均数
@Test
public void lambdaAverage(){
Track track1 = new Track("zzl-1" , 20);
Track track2 = new Track("zzl-2" , 30);
Track track3 = new Track("zzl-3" , 40);
Track track4 = new Track("zzl-4" , 50);
Track track5 = new Track("zzl-5" , 60);
Track track6 = new Track("zzl-6" , 70);
List<Track> tracks1 = Stream.of(track1 , track2).collect(Collectors.toList());
List<Track> tracks2 = Stream.of(track3 , track4 , track5).collect(Collectors.toList());
List<Track> tracks3 = Stream.of(track6 ).collect(Collectors.toList());
Album album1= new Album("zzl-8" ,tracks1 , Collections.EMPTY_LIST );
Album album2= new Album("zzl-9" ,tracks2 , Collections.EMPTY_LIST );
Album album3= new Album("zzl-10" ,tracks3 , Collections.EMPTY_LIST );
List<Album> albums = Stream.of(album1 , album2 , album3).collect(Collectors.toList());
double avg = averageNumberOfTracks(albums);
System.out.println(avg);
}
public double averageNumberOfTracks(List<Album> albums) {
return albums.stream()
.collect(Collectors.averagingInt(album -> album.getTrackList().size()));
}
数据分块
你可能希望将其分成两个部分,可以使用两次过滤操作, 分别过滤两种数据。
但是这样操作起来有问题。 首先, 为了执行两次过滤操作, 需要有两个流。 其次, 如果过滤操作复杂, 每个流上都要执行这样的操作, 代码也会变得冗余。
有这样一个收集器 partitioningBy, 它接受一个流, 并将其分成两部分。它使用 Predicate 对象判断一个元素应该属于哪个部分, 并根据布尔值返回一个 Map 到列表。 因此, 对于 true List 中的元素,Predicate 返回 true; 对其他 List 中的元素, Predicate 返回 false。
@Test
public void lambdaPartitioningBy(){
Artist artist1 = new Artist("zzl-1", Collections.EMPTY_LIST, "china");
Artist artist2 = new Artist("zzl-2", Collections.EMPTY_LIST, "china");
Artist artist3 = new Artist("zzl-3", Collections.EMPTY_LIST, "china");
Artist artist4 = new Artist("zzl-4", Collections.EMPTY_LIST, "china");
Artist artist5 = new Artist("zzl-5", Collections.EMPTY_LIST, "china");
Artist artist6 = new Artist("zzl-6", Collections.EMPTY_LIST, "china");
Artist artist7 = new Artist("zzl-7", Collections.EMPTY_LIST, "china");
List<Artist> members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
List<Artist> members2 = Stream.of(artist3, artist4).collect(Collectors.toList());
Artist artist9 = new Artist("zzl-9", members1, "china");
Artist artist10 = new Artist("zzl-10", members2, "china");
Stream<Artist> artists = Stream.of(artist9 , artist10 ,artist5 , artist6 , artist7 );
Map<Boolean , List<Artist>> map = bandsAndSolo1(artists);
}
public Map<Boolean, List<Artist>> bandsAndSolo1(Stream<Artist> artists) {
return artists.collect(Collectors.partitioningBy(artist -> artist.isSolo()));
}
也可以使用方法引用代替 Lambda 表达式
public Map<Boolean, List<Artist>> bandsAndSolo2(Stream<Artist> artists) {
return artists.collect(Collectors.partitioningBy(Artist::isSolo));
}
数据分组
数据分组是一种更自然的分割数据操作, 与将数据分成 ture 和 false 两部分不同, 可以使用任意值对数据分组。
@Test
public void lambdaGroupingBy() {
Artist artist1 = new Artist("zzl-1", Collections.EMPTY_LIST, "china");
Artist artist2 = new Artist("zzl-2", Collections.EMPTY_LIST, "china");
Artist artist3 = new Artist("zzl-3", Collections.EMPTY_LIST, "china");
Album album1 = new Album("zzl-8", Collections.EMPTY_LIST, Stream.of(artist1).collect(Collectors.toList()));
Album album2 = new Album("zzl-9", Collections.EMPTY_LIST, Stream.of(artist2).collect(Collectors.toList()));
Album album3 = new Album("zzl-10", Collections.EMPTY_LIST, Stream.of(artist3).collect(Collectors.toList()));
Album album4 = new Album("zzl-11", Collections.EMPTY_LIST, Stream.of(artist1).collect(Collectors.toList()));
Stream<Album> albums = Stream.of(album1 , album2 , album3 , album4);
Map<Artist, List<Album>> map = albumsByArtist(albums);
}
public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
return albums.collect(Collectors.groupingBy(Album::getMainMusician));
}
字符串
很多时候, 收集流中的数据都是为了在最后生成一个字符串。 期望的输出
为名字格式: "[George Harrison, John Lennon, Paul McCartney, Ringo Starr, The Beatles]"。
在 Java 8 还未发布前, 实现该功能的代码可能如下所示。 通过不断迭代列表, 使用一个 StringBuilder 对象来记录结果。 每一步都取出一个艺术家的名字, 追加到 StringBuilder对象
StringBuilder builder = new StringBuilder("[");
for (Artist artist : artists) {
if (builder.length() > 1)
builder.append(", ");
String name = artist.getName();
builder.append(name);
}
builder.append("]");
String result = builder.toString();
显然, 这段代码不是非常好。 如果不一步步跟踪, 很难看出这段代码是干什么的。
使用Java 8 提供的流和收集器就能写出更清晰的代码
String result =
artists.stream()
.map(Artist::getName)
.collect(Collectors.joining(", ", "[", "]"));
@Test
public void lambdaFormatterStr(){
Artist artist1 = new Artist("zzl-1", Collections.EMPTY_LIST, "china");
Artist artist2 = new Artist("zzl-2", Collections.EMPTY_LIST, "china");
Artist artist3 = new Artist("zzl-3", Collections.EMPTY_LIST, "china");
Artist artist4 = new Artist("zzl-4", Collections.EMPTY_LIST, "china");
Artist artist5 = new Artist("zzl-5", Collections.EMPTY_LIST, "china");
Artist artist6 = new Artist("zzl-6", Collections.EMPTY_LIST, "china");
Artist artist7 = new Artist("zzl-7", Collections.EMPTY_LIST, "china");
List<Artist> members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
List<Artist> members2 = Stream.of(artist3, artist4).collect(Collectors.toList());
Artist artist9 = new Artist("zzl-9", members1, "china");
Artist artist10 = new Artist("zzl-10", members2, "china");
List<Artist> artists = Stream.of(artist9, artist10, artist5, artist6, artist7).collect(Collectors.toList());
String result =
artists.stream()
.map(Artist::getName)
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(result);
}
网友评论