Java 8 中的 Streams API-高级集合类和收集器(二)

对集合排序

流的目的不仅是在集合类之间做转换, 而且同时提供了一组处理数据的通用操作。 有些集合本身是无序的, 但可以在流中进行顺序。

 @Test
    public void lambdaCollectOrder(){
        Set numbers = new HashSet<>(Arrays.asList(4 , 3 , 2 , 1));

        List 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 numbers = new HashSet<>(Arrays.asList(4 , 3 , 2 , 1));

        List 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 members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
        List members2 = Stream.of(artist3, artist4, artist5, artist6).collect(Collectors.toList());

        List 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 artists1 = Stream.of(artist9 , artist10 , artist11);
        Optional optionalArtist1 = biggestGroup(artists1);
        System.out.println(optionalArtist1.get());

        //最小值
        Stream artists2 = Stream.of(artist9 , artist10 , artist11);
        Optional optionalArtist2 = minimumGroup(artists2);
        System.out.println(optionalArtist2.get());

    }

    public Optional biggestGroup(Stream artists) {
        Function getCount = artist -> artist.getMembers().count();
        return artists.collect(Collectors.maxBy(Comparator.comparing(getCount)));
    }
    public Optional minimumGroup(Stream artists) {
        Function 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 tracks1 = Stream.of(track1 , track2).collect(Collectors.toList());
        List tracks2 = Stream.of(track3 , track4 , track5).collect(Collectors.toList());
        List 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  albums = Stream.of(album1 , album2 , album3).collect(Collectors.toList());
        double avg = averageNumberOfTracks(albums);
        System.out.println(avg);

    }

public double averageNumberOfTracks(List 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 members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
        List 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 artists = Stream.of(artist9 , artist10 ,artist5 , artist6 , artist7 );

        Map> map = bandsAndSolo1(artists);

    }

    public Map> bandsAndSolo1(Stream artists) {
        return  artists.collect(Collectors.partitioningBy(artist -> artist.isSolo()));
    }

也可以使用方法引用代替 Lambda 表达式

   public Map> bandsAndSolo2(Stream 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 albums = Stream.of(album1 , album2 , album3 , album4);

        Map> map = albumsByArtist(albums);

    }

    public Map> albumsByArtist(Stream 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 members1 = Stream.of(artist1, artist2).collect(Collectors.toList());
        List 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 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);
    }

你可能感兴趣的:(Java 8 中的 Streams API-高级集合类和收集器(二))