JAVA8 Lambda表达式 高级集合类&收集器

lambda表达式提供了很多的集合类和收集器来简化编程,使之更加方便和美观,所以这里介绍一些常用的集合类和收集器来处理繁杂的代码。

github的demo地址:  https://github.com/BradenLei/lambda

1、方法引用:形如 User::getName,TreeSet::new 等价于user.getName(), new TreeSet<>();

可以引用静态方法、实例对象方法、构造方法等。

    /**
     * 方法引用
     */
    @Test
    public void refTest() {
        List<String> collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
        System.out.println(collect);
        // 等价于 User::getName
        System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));

        // TreeSet::new
        Stream<Integer> integerStream = Stream.of(1, 2, 3);
        TreeSet<Integer> integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
        System.out.println(integerTreeSet);
    }

2、流中元素的顺序,如果集合本身无序,则转换的流仍然无序,如HashSet(进入顺序)

    /**
     * 方法引用
     */
    @Test
    public void refTest() {
        List<String> collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
        System.out.println(collect);
        // 等价于 User::getName
        System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));

        // TreeSet::new
        Stream<Integer> integerStream = Stream.of(1, 2, 3);
        TreeSet<Integer> integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
        System.out.println(integerTreeSet);
    }

3、maxBy(比较器),minBy(..), 找出最大最小值

    /**
     * 转换成值
     */
    @Test
    public void biggestGroupTest() {
        Optional<Artist> optional = biggestGroup(artists.stream());
        System.out.println(optional.get().getName());  // -> ace
    }
    public Optional<Artist> biggestGroup(Stream<Artist> artists) {
        Function<Artist, Long> getCount = artist -> artist.getMembers().count();
        return artists.collect(maxBy(comparing(getCount))); // 找出最大成员数目
    }

4、averagingDouble(),求平均值

    /**
     * 找出一组专辑上曲目的平均数
     */
    @Test
    public void test() {
        System.out.println(averageNumberOfTracks(albums));
    }
    public double averageNumberOfTracks(List<Album> albums) {
        return albums.stream().collect(averagingDouble(album -> album.getTrackList().size()));
    }

5、数据分块 partitioningBy,根据true, false分成两块数据

    /**
     * 数据分块 partitioningBy, true or false值划分, 分成true or false 两部分
     */
    @Test
    public void partitioningByTest() {
        Map<Boolean, List<Artist>> booleanListMap = partitioningByNationality(artists.stream());
        List<Artist> trueList = booleanListMap.get(true);
        System.out.println(trueList);  // out -> each
        List<Artist> falseList = booleanListMap.get(false);
        System.out.println(falseList); // out -> ace
    }
    public Map<Boolean, List<Artist>> partitioningByNationality(Stream<Artist> artists) {
        // 将国籍分成中国和其他两组
        return artists.collect(Collectors.partitioningBy(artist -> "china".equals(artist.getNationality())));
    }

6、数据分组 groupingBy(),可以根据任意值进行分组

    /**
     * 数据分组 groupingBy, 任意值划分, 分成true or false 两部分
     */
    @Test
    public void groupingByTest() {
        Map<String, List<Artist>> stringListMap = groupingByNationality(artists.stream());
        List<Artist> list = stringListMap.get("china");  // out -> each
        System.out.println(list);
        List<Artist> list1 = stringListMap.get("US");  // out -> ace
        System.out.println(list1);

        Map<Artist, List<Album>> streamListMap = groupingByArtist(albums.stream());
        System.out.println(streamListMap.get(artist1));  // 专辑1, 专辑2
    }
    public Map<String, List<Artist>> groupingByNationality(Stream<Artist> artists) {
        // 按照国籍分组
        return artists.collect(Collectors.groupingBy(artist -> artist.getNationality()));
    }
    /** 按照主唱分组 */
    public Map<Artist, List<Album>> groupingByArtist(Stream<Album> albums) {
        return albums.collect(groupingBy(album -> album.getMainMusician()));
    }

7、字符串拼装  Collectors.joining, 按照指定的格式拼装字符串

    /**
     * 输出字符串
     */
    @Test
    public void joiningTest() {
        String joiningName = joiningName(artists.stream());
        System.out.println(joiningName);  // out -> [ace,each]
    }
    public String joiningName(Stream<Artist> artists) {
        return artists.map(Artist::getName).collect(Collectors.joining(",", "[", "]"));
    }

8、单一的收集器已经能实现很多基础的功能,组合方式更显强大


    // 组合收集器

    /** ===== 下游收集器: 例子中用到的第二个收集器, 用来收集最终结果的一个子集 ===== */

    /**
     * 统计某个艺术家专辑数量
     */
    @Test
    public void combineTest() {
        long braden = numberOfAlbums(albums.stream(), "bradenlei");
        System.out.println(braden);
    }
    public long numberOfAlbums(Stream<Album> albums, String artistName) {
        return albums.map(album -> album.getMusicians())
                .filter(musicians -> musicians.filter(musician -> artistName.equals(musician.getName())).count() > 0)
                .count();
    }

    /**
     * 计算每个艺术家的专辑数(counting)
     */
    @Test
    public void combineTest2() {
        Map<Artist, Long> count = count(albums.stream());
        System.out.println(count.get(artist1)); // out -> 2
    }
    public Map<Artist, Long> count(Stream<Album> albums) {
        return albums.collect(groupingBy(album -> album.getMainMusician(), counting()));
    }

    /**
     * 收集每个艺术家的专辑名称 (mapping)
     */
    @Test
    public void combineTest3() {
        Map<Artist, List<String>> artistListMap = collectName(albums.stream());
        System.out.println(artistListMap.get(artist1)); // out -> [专辑1, 专辑2]
    }
    public Map<Artist, List<String>> collectName(Stream<Album> alubms) {
        return alubms.collect(groupingBy(Album::getMainMusician, mapping(Album::getName, toList())));
    }

9、有些时候现有的收集器不能满足我们的开发需求,当然也就可以重构和定制收集器了

    /** =====重构和定制收集器===== */

    /**
     * 使用除joining之外的方法拼接字符串
     *
     * 获取艺术家姓名 (初步)
     */
    @Test
    public void jointTest() {
        StringBuilder reduced = artists.stream()
                .map(Artist::getName)
                .reduce(new StringBuilder(), (builder, name) -> {
                    if (builder.length() > 0) builder.append(", ");

                    return builder.append(name);
                }, (left, right) -> left.append(right));
        reduced.insert(0, "[");
        reduced.append("]");
        String result = reduced.toString();

        System.out.println(result); // out -> [ace, each]
    }
    /**
     * 优化(自定义StringCombiner)
     */
    @Test
    public void jointTest2() {
        StringCombiner reduced = artists.stream()
                .map(Artist::getName)
                .reduce(new StringCombiner(", ", "[", "]"),
                        StringCombiner::add,
                        StringCombiner::merge); // 这里实际没有调用merge
        String string = reduced.toString();
        System.out.println(string);  // out -> [ace, each]
    }

    /***
     * 进一步优化(使用定制收集器StringCollector收集)
     */
     @Test
    public void jointTest3() {
         String collect = artists.stream()
                 .map(Artist::getName)
                 .collect(new StringCollector(", ", "[", "]"));
         System.out.println(collect); // out -> [ace, each]
     }

    /**
     * 用reducing代替自定义收集器
     */
    @Test
    public void jointtTest4() {
        // 这种方法的缺点是第二个参数每次都需要new一个对象
        StringCombiner collect = artists.stream()
                .map(Artist::getName)
                .collect(Collectors.reducing(
                        new StringCombiner(", ", "[", "]"),
                        name -> new StringCombiner(", ", "[", "]").add(name),
                        StringCombiner::merge));  // 这里会调用merge, 每次都是两个StringCombiner对象合并, 所以修改StringCombiner.add即可得到正确格式[ace, each]
        String s = collect.toString();
        System.out.println(s);  // out -> [ace[each]
    }

自定义类:

public class StringCombiner {
    // 前缀
    private String prefix;
    // 分隔符
    private String delim;
    // 后缀
    private String suffix;

    private StringBuilder builder;

    public StringCombiner(String delim, String prefix, String suffix) {
        this.delim = delim;
        this.prefix = prefix;
        this.suffix = suffix;
        builder = new StringBuilder();
    }

    public StringCombiner add(String element) {
        if(builder.length() > 0) {
            builder.append(delim);
        } else {
            builder.insert(0, prefix);
        }
            builder.append(element);
        return this;
    }

    public StringCombiner merge(StringCombiner combiner) {
        builder.append(combiner.builder);
        return this;
    }

    @Override
    public String toString() {
        if ("".equals(suffix)) {
            return builder.toString();
        } else {
            int tmp = builder.length();
            String result = builder.append(suffix).toString();
            builder.setLength(tmp);
            return result;
        }
    }
}

其中:StringCombiner功能仿制java提供的StringJoiner

/**
 * String 待收集的元素字符串
 * StringCombiner 累加器类型
 * String 最终结果类型
 */
public class StringCollector implements Collector<String, StringCombiner, String> {
    // 前缀
    private String prefix;
    // 分隔符
    private String delim;
    // 后缀
    private String suffix;

    public StringCollector(String delim, String prefix, String suffix) {
        this.delim = delim;
        this.prefix = prefix;
        this.suffix = suffix;
    }
    @Override
    public Supplier<StringCombiner> supplier() {
        return () -> new StringCombiner(delim, prefix, suffix);
    }

    @Override
    public BiConsumer<StringCombiner, String> accumulator() {
        return StringCombiner::add;
    }

    @Override
    public BinaryOperator<StringCombiner> combiner() {
        return StringCombiner::merge;
    }

    @Override
    public Function<StringCombiner, String> finisher() {
        return StringCombiner::toString;
    }

    @Override
    public Set<Characteristics> characteristics() {
        Set<Collector.Characteristics> emptySet = Collections.emptySet();
        return emptySet;
    }
}

10、完整demo:

public class CollectorDemo {
    private static User user;
    private static List<User> list;

    @Before
    public void init() {
        if (user == null) {
            user = new User("张三", 11001);
        }
        if (list == null) {
            list = new ArrayList<>();
            list.add(new User("李四", 11002));
            list.add(new User("王五", 11003));
            list.add(new User("周庄", 11004));
        }
    }

    /**
     * 方法引用
     */
    @Test
    public void refTest() {
        List<String> collect = list.stream().map(u -> u.getName()).collect(Collectors.toList());
        System.out.println(collect);
        // 等价于 User::getName
        System.out.println(list.stream().map(User::getName).collect(Collectors.toList()));

        // TreeSet::new
        Stream<Integer> integerStream = Stream.of(1, 2, 3);
        TreeSet<Integer> integerTreeSet = integerStream.collect(Collectors.toCollection(TreeSet::new));
        System.out.println(integerTreeSet);
    }

    /**
     *  元素顺序, 如果集合本身无序, 则转换的流也是无序的如, HashSet
     */
    @Test
    public void orderTest() {
        // 进来的流无序, 则出去的流也是无序
        List<Integer> origin = Arrays.asList(1, 2, 3, 4);
        List<Integer> integerList = origin.stream().collect(Collectors.toList());
        assertEquals(origin, integerList); // ok

        Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        Set<Integer> integerSet = set.stream().collect(Collectors.toSet());
        assertEquals(set, integerSet);  // 不能保证每次通过

        List<Integer> list = Arrays.asList(2, 1, 4, 3);
        List<Integer> collect = list.stream().sorted().collect(Collectors.toList());
        assertEquals(origin, collect); // ok
    }

    private static List<Artist> artists = null;
    private static List<Artist> artists2 = null;
    private static List<Track> tracks = null;
    private static List<Track> tracks2 = null;
    private static List<Album> albums = null;
    private static Artist artist1 = null;
    private static Artist artist2 = null;
    static {
        Artist artist = new Artist("braden", "china");
        artist1 = new Artist("ace", Arrays.asList(artist), "US");
        artist2 = new Artist("each", "china");
        artists = Arrays.asList(artist1, artist2);

        tracks = Arrays.asList(new Track("love you", 3), new Track("Shape of you", 5), new Track("夜上海", 4));
        tracks2 = Arrays.asList(new Track("喜欢你", 4), new Track("不分手的恋爱", 5));

        artists2 = Arrays.asList(new Artist("bradenlei","china"), new Artist("Mr.zhang","UK"));
        albums = Arrays.asList(new Album("专辑1", tracks, artists), new Album("专辑2", tracks2, artists), new Album("专辑3", tracks, artists2));
    }
    /**
     * 转换成值
     */
    @Test
    public void biggestGroupTest() {
        Optional<Artist> optional = biggestGroup(artists.stream());
        System.out.println(optional.get().getName());  // -> ace
    }
    public Optional<Artist> biggestGroup(Stream<Artist> artists) {
        Function<Artist, Long> getCount = artist -> artist.getMembers().count();
        return artists.collect(maxBy(comparing(getCount))); // 找出最大成员数目
    }

    /**
     * 找出一组专辑上曲目的平均数
     */
    @Test
    public void test() {
        System.out.println(averageNumberOfTracks(albums));
    }
    public double averageNumberOfTracks(List<Album> albums) {
        return albums.stream().collect(averagingDouble(album -> album.getTrackList().size()));
    }

    /**
     * 数据分块 partitioningBy, true or false值划分, 分成true or false 两部分
     */
    @Test
    public void partitioningByTest() {
        Map<Boolean, List<Artist>> booleanListMap = partitioningByNationality(artists.stream());
        List<Artist> trueList = booleanListMap.get(true);
        System.out.println(trueList);  // out -> each
        List<Artist> falseList = booleanListMap.get(false);
        System.out.println(falseList); // out -> ace
    }
    public Map<Boolean, List<Artist>> partitioningByNationality(Stream<Artist> artists) {
        // 将国籍分成中国和其他两组
        return artists.collect(Collectors.partitioningBy(artist -> "china".equals(artist.getNationality())));
    }

    /**
     * 数据分组 groupingBy, 任意值划分, 分成true or false 两部分
     */
    @Test
    public void groupingByTest() {
        Map<String, List<Artist>> stringListMap = groupingByNationality(artists.stream());
        List<Artist> list = stringListMap.get("china");  // out -> each
        System.out.println(list);
        List<Artist> list1 = stringListMap.get("US");  // out -> ace
        System.out.println(list1);

        Map<Artist, List<Album>> streamListMap = groupingByArtist(albums.stream());
        System.out.println(streamListMap.get(artist1));  // 专辑1, 专辑2
    }
    public Map<String, List<Artist>> groupingByNationality(Stream<Artist> artists) {
        // 按照国籍分组
        return artists.collect(Collectors.groupingBy(artist -> artist.getNationality()));
    }
    /** 按照主唱分组 */
    public Map<Artist, List<Album>> groupingByArtist(Stream<Album> albums) {
        return albums.collect(groupingBy(album -> album.getMainMusician()));
    }

    /**
     * 输出字符串
     */
    @Test
    public void joiningTest() {
        String joiningName = joiningName(artists.stream());
        System.out.println(joiningName);  // out -> [ace,each]
    }
    public String joiningName(Stream<Artist> artists) {
        return artists.map(Artist::getName).collect(Collectors.joining(",", "[", "]"));
    }

    // 组合收集器

    /** ===== 下游收集器: 例子中用到的第二个收集器, 用来收集最终结果的一个子集 ===== */

    /**
     * 统计某个艺术家专辑数量
     */
    @Test
    public void combineTest() {
        long braden = numberOfAlbums(albums.stream(), "bradenlei");
        System.out.println(braden);
    }
    public long numberOfAlbums(Stream<Album> albums, String artistName) {
        return albums.map(album -> album.getMusicians())
                .filter(musicians -> musicians.filter(musician -> artistName.equals(musician.getName())).count() > 0)
                .count();
    }

    /**
     * 计算每个艺术家的专辑数(counting)
     */
    @Test
    public void combineTest2() {
        Map<Artist, Long> count = count(albums.stream());
        System.out.println(count.get(artist1)); // out -> 2
    }
    public Map<Artist, Long> count(Stream<Album> albums) {
        return albums.collect(groupingBy(album -> album.getMainMusician(), counting()));
    }

    /**
     * 收集每个艺术家的专辑名称 (mapping)
     */
    @Test
    public void combineTest3() {
        Map<Artist, List<String>> artistListMap = collectName(albums.stream());
        System.out.println(artistListMap.get(artist1)); // out -> [专辑1, 专辑2]
    }
    public Map<Artist, List<String>> collectName(Stream<Album> alubms) {
        return alubms.collect(groupingBy(Album::getMainMusician, mapping(Album::getName, toList())));
    }

    /** =====重构和定制收集器===== */

    /**
     * 使用除joining之外的方法拼接字符串
     *
     * 获取艺术家姓名 (初步)
     */
    @Test
    public void jointTest() {
        StringBuilder reduced = artists.stream()
                .map(Artist::getName)
                .reduce(new StringBuilder(), (builder, name) -> {
                    if (builder.length() > 0) builder.append(", ");

                    return builder.append(name);
                }, (left, right) -> left.append(right));
        reduced.insert(0, "[");
        reduced.append("]");
        String result = reduced.toString();

        System.out.println(result); // out -> [ace, each]
    }
    /**
     * 优化(自定义StringCombiner)
     */
    @Test
    public void jointTest2() {
        StringCombiner reduced = artists.stream()
                .map(Artist::getName)
                .reduce(new StringCombiner(", ", "[", "]"),
                        StringCombiner::add,
                        StringCombiner::merge); // 这里实际没有调用merge
        String string = reduced.toString();
        System.out.println(string);  // out -> [ace, each]
    }

    /***
     * 进一步优化(使用定制收集器StringCollector收集)
     */
     @Test
    public void jointTest3() {
         String collect = artists.stream()
                 .map(Artist::getName)
                 .collect(new StringCollector(", ", "[", "]"));
         System.out.println(collect); // out -> [ace, each]
     }

    /**
     * 用reducing代替自定义收集器
     */
    @Test
    public void jointtTest4() {
        // 这种方法的缺点是第二个参数每次都需要new一个对象
        StringCombiner collect = artists.stream()
                .map(Artist::getName)
                .collect(Collectors.reducing(
                        new StringCombiner(", ", "[", "]"),
                        name -> new StringCombiner(", ", "[", "]").add(name),
                        StringCombiner::merge));  // 这里会调用merge, 每次都是两个StringCombiner对象合并, 所以修改StringCombiner.add即可得到正确格式[ace, each]
        String s = collect.toString();
        System.out.println(s);  // out -> [ace[each]
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柏油

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值