Collectors 与集合转换

Collectors.toList 

streamArr.collect(Collectors.toList());
List<Integer> collectList = Stream.of(1, 2, 3, 4)
        .collect(Collectors.toList());
System.out.println("collectList: " + collectList);
// 打印结果 collectList: [1, 2, 3, 4]

Collectors.toMap

map value 为对象 student
Map<Integer, Student> map = list.stream().collect(Collectors.toMap(Student::getId, student -> student));
// 遍历打印结果
map.forEach((key, value) -> {
    System.out.println("key: " + key + "    value: " + value);
});
map value 为对象中的属性
Map<Integer, String> map = list.stream().collect(Collectors.toMap(Student::getId, Student::getName));
map.forEach((key, value) -> {
    System.out.println("key: " + key + "    value: " + value);
});

Collectors.toSet

Set<String> result = Stream.of("aa", "bb", "cc", "aa").collect(HashSet::new, HashSet::add, HashSet::addAll);
//Collectors类中已经预定义好了toList,toSet,toMap,toCollection等方便使用的方法,所以以上代码还可以简化如下:
Set<String> result2 = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toSet());

Set<Integer> collectSet = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
System.out.println("collectSet: " + collectSet);
// 打印结果 collectSet: [1, 2, 3, 4]

Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// collect toString
String str = stream.collect(Collectors.joining()).toString();

集合处理

从List中过滤出一个元素

User match = users.stream()
.filter((user) -> user.getId() == 1).findAny().get();

两集合寻找相同元素

 // 求同时出现在老师集合和学生集合中的人数,name相同即视为同一个人
        int size = teachers.stream()
                .map(t -> students.stream().filter(s -> Objects.nonNull(t.getName()) && Objects.nonNull(s.getName()) && Objects.equals(t.getName(), s.getName())).findAny().orElse(null))
                .filter(Objects::nonNull)
                .collect(Collectors.toList())
                .size(); // .collect(Collectors.toList()).size() == .count()
                

        // 求同时出现在老师集合和学生集合中人的name集合,name相同即视为同一个人
        List<String> names = teachers.stream()
                .map(t -> students.stream().filter(s -> Objects.nonNull(t.getName()) && Objects.nonNull(s.getName()) && Objects.equals(t.getName(), s.getName())).findAny().orElse(null))
                .filter(Objects::nonNull)
                .map(r -> r.getName())
                .collect(Collectors.toList());

分组

假设要得到按年龄分组的Map<Integer,List>,可以按这样写:

Map<Integer, List<User>> ageMap = userStream.collect(Collectors.toMap(User::getAge, Collections::singletonList, (a, b) -> {
            List<User> resultList = new ArrayList<>(a);
            resultList.addAll(b);
            return resultList;
        }));

Map<Integer, String> map = persons
    .stream()
    .collect(Collectors.toMap(
        p -> p.age,
        p -> p.name,
        (name1, name2) -> name1 + ";" + name2));

System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}

Collectors groupingBy 分组

Map<Integer, List<User>> ageMap2 = userStream
.collect(Collectors.groupingBy(User::getAge));

还可以对集合按照多个属性分组。将多个字段拼接成一个新字段,然后再使用groupBy分组

Map<String, List<EntryDeliveryDetailywk>> detailmap = details.stream()
.collect(Collectors.groupingBy(this::fetchGroupKey));

private String fetchGroupKey(EntryDeliveryDetailywk detail){
        return detail.getSkuId().toString() 
        + detail.getItemsName() 
        + detail.getWarehouseId().toString()   
        + detail.getSupplierId().toString();
    }

groupingby-分组后操作

Collectors中还提供了一些对分组后的元素进行downStream处理的方法:

counting方法返回所收集元素的总数;
summing方法会对元素求和;
maxByminBy会接受一个比较器,求最大值,最小值;
mapping函数会应用到downstream结果上,并需要和其他函数配合使用;

Map<Boolean, Long> partiCount = Stream.of(1, 2, 3, 4)
        .collect(Collectors.partitioningBy(it -> it.intValue() % 2 == 0,
                Collectors.counting()));
System.out.println("partiCount: " + partiCount);
// 打印结果
// partiCount: {false=2, true=2}
Map<Integer, Long> sexCount = userStream.collect(Collectors.groupingBy(User::getSex,Collectors.counting()));

Map<Integer, Integer> ageCount = userStream.collect(Collectors.groupingBy(User::getSex,Collectors.summingInt(User::getAge)));

Map<Integer, Optional<User>> ageMax =  userStream.collect(Collectors.groupingBy(User::getSex,Collectors.maxBy(Comparator.comparing(User::getAge))));

Map<Integer, List<String>> nameMap =  userStream.collect(Collectors.groupingBy(User::getSex,Collectors.mapping(User::getName,Collectors.toList())));

groupingby-更多例子

groupingBy 根据年龄来分组:

Map<Integer, List> peopleByAge = peoples.stream()
.filter(p -> p.age > 12).collect(Collectors.groupingBy(p -> p.age, Collectors.toList()));
Map<Integer, List<Person>> personsByAge = persons.stream().collect(Collectors.groupingBy(p -> p.age));
personsByAge.forEach((age, p) -> System.out.format("age %s: %s\n", age, p));
// age 18: [Max]
// age 23: [Peter, Pamela]
// age 12: [David]

groupingBy 根据年龄分组,年龄对应的键值List存储的为Person的姓名:

Map<Integer, List> peopleByAge = people.stream()
.collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p.name, Collectors.toList())));
//mapping即为对各组进行投影操作,和Stream的map方法基本一致。

groupingBy 根据姓名分组,获取每个姓名下人的年龄总和:

Map sumAgeByName = peoples.stream().collect(Collectors.groupingBy(p -> p.name, Collectors.reducing(0, (Person p) -> p.age, Integer::sum)));
/* 或者使用summingInt方法 */
sumAgeByName = peoples.stream().collect(Collectors.groupingBy(p -> p.name, Collectors.summingInt((Person p) -> p.age)));

groupingBy Boolean分组:

Map<Boolean, List<Integer>> collectGroup = Stream.of(1, 2, 3, 4)
            .collect(Collectors.groupingBy(it -> it > 3));
System.out.println("collectGroup : " + collectGroup);
// 打印结果
// collectGroup : {false=[1, 2, 3], true=[4]}

Map.merge() 类似于分组之后sum

 Map<String, Integer> studentScoreMap2 = new HashMap<>();
        studentScoreList.forEach(studentScore -> studentScoreMap2.merge(
          studentScore.getStuName(),
          studentScore.getScore(),
          Integer::sum));

Collectors partitioningBy

Collectors中还提供了partitioningBy方法,接受一个Predicate函数,该函数返回boolean值,用于将内容分为两组。

另外Collectors中还存在一个类似groupingBy的方法:partitioningBy,它们的区别是partitioningBy为键值为Boolean类型的groupingBy,这种情况下它比groupingBy更有效率。

例子

userStream按性别分组*

假设User实体中包含性别信息getSex(),可以按如下写法:

Map<Boolean, List<User>> sexMap = userStream
.collect(Collectors.partitioningBy(item -> item.getSex() > 0));

partitioningBy 将数字的Stream分解成奇数集合和偶数集合。

Map<Boolean, List<Integer>> collectParti = Stream.of(1, 2, 3, 4)
            .collect(Collectors.partitioningBy(it -> it % 2 == 0));
System.out.println("collectParti : " + collectParti);
// 打印结果
// collectParti : {false=[1, 3], true=[2, 4]}

排序

//按照自然顺序进行排序 如果要自定义排序sorted 传入自定义的 Comparator
list.stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

//对象排序比较 请重写对象的equals()和hashCode()方法
list.sorted((a, b) -> b.compareTo(a))

Collections.sort(names, (a, b) -> b.compareTo(a));

比较

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0

Collectors joining 拼接字符串

Collectors.joining 收集Stream中的值,该方法可以方便地将Stream得到一个字符串。joining函数接受三个参数,分别表示允(用以分隔元素)、前缀和后缀:

String names = peoples.stream().map(p->p.name).collect(Collectors.joining(","))

String strJoin = Stream.of("1", "2", "3", "4")
        .collect(Collectors.joining(",", "[", "]"));
System.out.println("strJoin: " + strJoin);
// 打印结果
// strJoin: [1,2,3,4]

//字符串连接
String phrase = persons
    .stream()
    .filter(p -> p.age >= 18)
    .map(p -> p.name)
    .collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));
System.out.println(phrase);
// In Germany Max and Peter and Pamela are of legal age.

List 转String

//java8 String.join 方式  
List<String> webs = Arrays.asList("voidcc.com", "voidmvn.com", "voidtool.com");
//webs 必须是List<String>
String allwebs = String.join(",", webs);
System.out.println(allwebs);

//stream 
List<String> webs = Arrays.asList("voidcc.com", "voidmvn.com", "voidtool.com");
String allwebs = webs.stream().collect(Collectors.joining(","));
System.out.println(allwebs);

聚合(使用collect将Stream转换成单个值)

Collectors分别提供了求平均值averaging、总数couting、最小值minBy、最大值maxBy、求和suming等操作。

聚合函数列表

maxByminBy允许用户按照某个特定的顺序生成一个值。

averagingDouble:求平均值,Stream的元素类型为double

averagingInt:求平均值,Stream的元素类型为int

averagingLong:求平均值,Stream的元素类型为long

counting:Stream的元素个数

maxBy:在指定条件下的,Stream的最大元素

minBy:在指定条件下的,Stream的最小元素

reducing: reduce操作

summarizingDouble:统计Stream的数据(double)状态,其中包括count,min,max,sum和平均。

summarizingInt:统计Stream的数据(int)状态,其中包括count,min,max,sum和平均。

summarizingLong:统计Stream的数据(long)状态,其中包括count,min,max,sum和平均。

summingDouble:求和,Stream的元素类型为double

summingInt:求和,Stream的元素类型为int

summingLong:求和,Stream的元素类型为long

Collectors.maxBy

Optional<Integer> collectMaxBy = Stream.of(1, 2, 3, 4)
            .collect(Collectors.maxBy(Comparator.comparingInt(o -> o)));
System.out.println("collectMaxBy:" + collectMaxBy.get());
// 打印结果
// collectMaxBy:4

Collectors.averagingInt

计算集合的平均年龄

Double averageAge = persons
    .stream()
    .collect(Collectors.averagingInt(p -> p.age));

System.out.println(averageAge);     // 19.0

Collectors.SummaryStatistics

假如你希望将流中结果聚合为一个总和、平均值、最大值、最小值,那么Collectors.summarizing(Int/Long/Double)就是为你准备的,它可以一次行获取前面的所有结果,其返回值为(Int/Long/Double)SummaryStatistics。

DoubleSummaryStatistics dss = people.collect(Collectors.summarizingDouble((Person p)->p.age));
double average=dss.getAverage();
double max=dss.getMax();
double min=dss.getMin();
double sum=dss.getSum();
double count=dss.getCount();

IntSummaryStatistics ageSummary = persons
        .stream()
        .collect(Collectors.summarizingInt(p -> p.age));

System.out.println(ageSummary);
// IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23}

自定义 Collector

Collector<Person, StringJoiner, String> personNameCollector =
    Collector.of(
        () -> new StringJoiner(" | "),          // supplier
        (j, p) -> j.add(p.name.toUpperCase()),  // accumulator
        (j1, j2) -> j1.merge(j2),               // combiner
        StringJoiner::toString);                // finisher

String names = persons
    .stream()
    .collect(personNameCollector);

System.out.println(names);  // MAX | PETER | PAMELA | DAVID

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐