1.遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 7, 8, 9, 0, 100);
// find使用,查找第一个元素
Optional<Integer> first = list.stream().findFirst();
log.info(first.get().toString());
// match使用,判断是否存在某个值
boolean b1 = list.stream().anyMatch(value -> value >= 100);
boolean b2 = list.stream().anyMatch(value -> value > 10);
log.info(String.valueOf(b1));
log.info(String.valueOf(b2));
// foreach使用,遍历输出元素
list.stream().filter(value -> value > 4).forEach(System.out::print);
System.out.println();
list.forEach(System.out::print);

2.筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

// 数字筛选
List<Integer> list = Arrays.asList(1, 3, 4, 5, 6, 7, 8, 9, 10, 20);
ArrayList<Integer> arrayList1 = new ArrayList<>();
list.stream().filter(value -> value > 4).forEach(value -> arrayList1.add(value));
log.info(arrayList1.toString());
ArrayList<Integer> arrayList2 = new ArrayList<>();
list.stream().filter(value -> value <= 4).forEach(arrayList2::add);
log.info(arrayList2.toString());
// 对象筛选
List<User> userList1 = Arrays.asList(
        new User(1, "xw", "男"),
        new User(2, "zgx", "男"),
        new User(3, "gg", "男"),
        new User(4, "whb", "男"),
        new User(5, "yda", "男"),
        new User(6, "bhm", "女")
);
List<User> userList2 = new ArrayList<>();
userList1.stream().filter(user -> user.getId() > 2).forEach(userList2::add);
log.info(userList2.toString());
userList1.stream().filter(user -> 					         user.getName().equals("xw")).forEach(System.out::println);
HashMap<String, Optional<User>> userHashMap = new HashMap<>();
Optional<User> man = userList1.stream().filter(user -> user.getSex().equals("男")).findFirst();
        userHashMap.put("man", man);
        log.info(userHashMap.toString());

3.聚合(max/min/count)

maxmincount这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。

max、min、count

 // max & min & count
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 89, 9, 0, 10, 20, 30);
Optional<Integer> max = list.stream()
        .max(Comparator.comparing(Integer::intValue));
log.info(String.format("最大值是:%d", max.get()));
Optional<Integer> min = list.stream()
        .min(Comparator.comparing(value -> value.intValue()));
log.info(String.format("最小值是:%d", min.get()));
Integer count1 = Math.toIntExact(list.stream().count());
log.info(String.format("list总元素量1为:%d", count1));
Integer count2 = Math.toIntExact(list.stream().filter(value -> value > 5).count());
log.info(String.format("list元素值大于5的个数:%d", count2));
List<User> userList = Arrays.asList(
        new User(1, "xw", "男", 22),
        new User(2, "zgx", "男", 22),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 22),
        new User(7, "lwn", "女", 22)
);
Optional<User> ageMax = userList.stream().max(Comparator.comparing(value -> value.getAge()));
log.info(String.format("年龄最大的是:%s", ageMax.get()));
Optional<User> ageMin = userList.stream().filter(user -> user.getSex().equals("男")).min(Comparator.comparing(User::getAge));
log.info(String.format("性别为男且年龄最小的:%s", ageMin.get()));
Integer count3 = Math.toIntExact(userList.stream().filter(user -> user.getAge() > 22).count());
log.info(String.format("年龄大于22的用户数量为:%d", count3));

4.映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为mapflatMap

map

// map
List<Integer> list1 = Arrays.asList(1, 3, 5, 6, 7, 8, 0, 10, 20, 22, 39);
List<Integer> collect1 = list1.stream().filter(value -> value > 7).collect(Collectors.toList());
log.info(String.format("list1元素值大于7的有: %s", collect1));
List<String> list2 = Arrays.asList("xw", "sjdk", "sf", "jk", "hoksh", "shdfj", "jhgkj");
List<String> collect2 = list2.stream().map(String::toUpperCase).collect(Collectors.toList());
log.info(String.format("list2元素值全转大写,结果:%s", collect2));
List<User> userList1 = Arrays.asList(
        new User(1, "xw", "男", 22),
        new User(2, "zgx", "男", 22),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 22),
        new User(7, "lwn", "女", 22),
        new User(8, "ksj", "女", 22)
);
List<User> userList2 = userList1.stream()
        .map(user -> {
            if (user.getSex().equals("女")) {
                user.setAge(user.getAge() - 2);
            }
            user.setName(user.getName().toUpperCase());
            return user;
        })
        .filter(user -> user.getAge() > 19 && user.getSex().equals("女"))
        .collect(Collectors.toList());
log.info(String.format("修改结果为:%s", userList2));

flatMap

// flatMap
List<String> stringList = userList1.stream()
        .flatMap(user -> {
            Stream<String> stream = Arrays.stream(user.toString().split("="));
            return stream;
        })
        .collect(Collectors.toList());
log.info(String.format("flatMap处理前:%s", userList1));
log.info(String.format("flatMap转换结果:%s", stringList));

5.归约(reduce)

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和求乘积求最值操作。

// 求和
List<Integer> list1 = Arrays.asList(1, 3, 5, 2, 1, 5, 89, 23, 89, 23, 34);
Integer sum = list1.stream().reduce(0, Integer::sum);
log.info(String.format("list1中各元素之和:%d", sum));
// 求积
List<Integer> list2 = Arrays.asList(1, 2, 4);
Optional<Integer> product = list2.stream().reduce((x, y) -> x * y);
log.info(String.format("list中2各元素之积:%d", product.get()));
// 求最大值1
Optional<Integer> max1 = list1.stream().reduce(Integer::max);
log.info(String.format("list1中的最大值是:%d", max1.get()));
// 求最大/小值2
Optional<Integer> min1 = list1.stream().reduce((x, y) -> x < y ? x : y);
log.info(String.format("list1中的最小值:%d", min1.get()));
List<User> userList1 = Arrays.asList(
        new User(1, "xw", "男", 22),
        new User(2, "zgx", "男", 22),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
Integer maxAge1 = userList1.stream().reduce(0, (maxAge, user) -> maxAge > user.getAge() ? maxAge : user.getAge(), Integer::max);
log.info(String.format("年龄最大是:%d", maxAge1));
Optional<Integer> max2 = userList1.stream().map(User::getAge).reduce(Integer::max);
Optional<Integer> max3 = userList1.stream().map(User::getAge).reduce((x, y) -> x > y ? x : y);
log.info(String.format("年龄最大是:%d", max2.get()));
log.info(String.format("年龄最大是:%d", max3.get()));
Integer stringMaxLength = userList1.stream()
        .filter(user -> user.getAge() > 22 && user.getAge() < 25)
        .flatMap(user -> {
            Stream<String> newStream = Arrays.stream(user.toString().split("="));
            return newStream;
        })
        .collect(Collectors.toList())
        .stream().map(String::toUpperCase)
        .reduce(0, (maxLength, string) -> maxLength > string.length() ? maxLength : string.length(), Integer::max);
log.info(String.format("最大字符串长度为:%s", stringMaxLength));

6.收集(collect)

collect,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。

collect主要依赖java.util.stream.Collectors类内置的静态方法。

6.1归集(toList/toSet/toMap)

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toListtoSettoMap比较常用,另外还有toCollectiontoConcurrentMap等复杂一些的用法。

toList

List<Integer> list1 = Arrays.asList(1, 2, 8, 0, 9, 0, 1, 23, 32, 37, 49, 48);
List<Integer> collect1 = list1.stream().filter(value -> value > 5).collect(Collectors.toList());
log.info(String.format("list1中收集>5的结果为:%s", collect1));

toSet

Set<Integer> collect2 = list1.stream().filter(value -> value < 5).collect(Collectors.toSet());
log.info(String.format("list1中收集<5的结果为:%s", collect2));
collect2.forEach(value -> {System.out.print(value + " ");

toMap

List<User> userList1 = Arrays.asList(
        new User(1, "xww", "男", 22),
        new User(2, "zgx", "男", 22),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
Map<String, User> userMap = userList1.stream()
         .filter(user -> user.getSex().equals("女"))
         .collect(Collectors.toMap(User::getName, user -> user));
log.info(String.format("性别为女的用户转map:%s", userMap));

6.2 统计(count/averaging)

Collectors提供了一系列用于数据统计的静态方法:

List<Integer> list = Arrays.asList(1, 2, 3, 2, 5, 3, 9, 8, 7, 6, 29, 10, 22);
// count
long count = list.stream().filter(value -> value > 10).count();
log.info(String.format("list中元素>10的个数为:%d", count));
// average
Double average = list.stream().filter(value -> value > 1).collect(Collectors.averagingInt(Integer::intValue));
log.info(String.format("list中元素>1的元素平均值为:%.2f", average));
List<User> userList1 = Arrays.asList(
        new User(1, "xww", "女", 22),
        new User(2, "zgx", "男", 22),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
Double averageAge = userList1.stream()
        .filter(user -> user.getSex().equals("男"))
        .map(User::getAge)
        .collect(Collectors.averagingInt(Integer::intValue));
log.info(String.format("男用户的平均年龄为:%d 岁", averageAge.intValue()));
// mapToInt
int ageSum = userList1.stream()
        .filter(user -> user.getSex().equals("女"))
        .mapToInt(User::getAge)
        .sum();
log.info(String.format("女用户的年龄之和为:%d", ageSum));
// summarizingInt 统计 计数、总和、最小值、平均值、最大值
IntSummaryStatistics recording = userList1.stream()
        .filter(user -> user.getSex().equals("男"))
        .collect(Collectors.summarizingInt(User::getAge));
log.info(String.format("记录所有男用户的年龄各项值,结果为:%s", recording));

6.3 分组(partitioningBy/groupingBy)

partitioningBy
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);
// partitioningBy
Map<Boolean, List<Integer>> collect1 = list.stream()
        .collect(Collectors.partitioningBy(value -> value > 20));
log.info(String.format("元素值是否大于20进行分组,结果为:%s", collect1));
collect1.forEach((key, value) -> {
    log.info(String.format("元素值是否大于20进行分组,结果为:%s:%s", key, value));
});

groupingBy

List<User> userList = Arrays.asList(
        new User(1, "xww", "女", 22),
        new User(2, "zgx", "男", 21),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
// groupingBy
Map<String, List<User>> collect2 = userList.stream()
        .collect(Collectors.groupingBy(User::getSex));
log.info(String.format("根据性别对用户进行分组,结果为:%s", collect2));
collect2.forEach((key, user) -> {
    log.info(String.format("根据性别对用户进行分组,结果为:%s:%s", key, user));
});

6.4 接合(joining)

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);
String collect = list.stream()
        .map(Object::toString)
        .collect(Collectors.joining("——"));
log.info(String.format("joining测试结果为:%s", collect));

7.排序(sorted)

sorted,中间操作。有两种排序:

List<User> userList1 = Arrays.asList(
        new User(1, "xw", "女", 22),
        new User(2, "zgx", "男", 21),
        new User(3, "whb", "男", 23),
        new User(4, "gg", "男", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
// sorted
List<User> userList2 = userList1.stream()
        .sorted(Comparator.comparing(User::getAge))
        .collect(Collectors.toList());
log.info(String.format("按照年龄排序,结果为:%s", userList2));
// 从小到大,正序
List<String> userName1 = userList1.stream()
        .sorted(Comparator.comparing(User::getAge))
        .map(User::getName)
        .collect(Collectors.toList());
log.info(String.format("根据年龄从小到大排序:%s", userName1));
// 从大到小,倒序
List<String> userName2 = userList1.stream()
        .filter(user -> user.getSex().equals("男"))
        .sorted(Comparator.comparing(User::getAge).reversed())
        .map(User::getName)
        .collect(Collectors.toList());
log.info(String.format("男用户根据年龄从大到小排序:%s", userName2));

8.提取/组合

流也可以进行合并去重限制跳过等操作。

1.去重排序

List<Integer> list = Arrays.asList(1, 2, 4, 4, 10, 9, 6, 8, 6, 2, 3, 7, 5);
List<Integer> collect = list
        .stream()
        .distinct()
        .sorted(Comparator.comparing(Integer::intValue))
        .collect(Collectors.toList());
collect.forEach(x -> System.out.print(x+" ")); // 1 2 3 4 5 6 7 8 9 10

存在重复数据的问题,这里使用stream流的衍生功能,去除一个对象中的部分元素的重复如下:

List<User> userList = Arrays.asList(
        new User(1, "xw", "女", 21),
        new User(2, "zgx", "男", 21),
        new User(3, "whb", "男", 23),
        new User(4, "gag", "男", 30),
        new User(4, "gbg", "男", 30),
        new User(4, "gcg", "女", 30),
        new User(5, "yda", "男", 22),
        new User(6, "bhm", "女", 23),
        new User(7, "lsn", "女", 22),
        new User(8, "ksj", "女", 22)
);
ArrayList<User> collect1 = userList.stream().collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(
                                Comparator.comparing(
                                        User::getId))), ArrayList::new));

多个字段或者多个条件去重

ArrayList<User> collect2 = userList.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(
                        Comparator.comparing(user->user.getName() + ";" + user.getId()))), ArrayList::new)

以上使用到了collectingAndThen()根据属性进行去重的操作,进行结果集的收集,收集到结果集之后再进行下一步的处理。在这个去重操作中还用到了toCollection、TreeSet两个操作。

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)

看源码中需要传的参数有两个,第一个参数是Collector的子类,所以Collectors类中的大多数方法都能使用,比如:toList(),toSet(),toMap()等,当然也包括collectingAndThen()。第二个参数是一个Function函数,也是去重的关键,用到的ArrayList::new调用到了ArrayList的有参构造。Function函数是R apply(T t),在第一个参数downstream放在第二个参数Function函数的参数里面,将结果设置为t。对于toCollection是一个通用的方法,满足treeSet收集集合,再传入需要根据某个属性进行比较的比较器,就能达到去重的效果。

2.限制长度(limit)

List<Integer> list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);
List<Integer> collect1 = list
        .stream()
        .distinct()
        .sorted(Comparator.comparing(Integer::intValue))
        .limit(6)
        .collect(Collectors.toList());
collect1.forEach(x -> System.out.print(x + " "));

3.跳过(skip)

// 跳过前几项
List<Integer> list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);
List<Integer> collect2 = list
        .stream()
        .distinct()
        .sorted(Comparator.comparing(Integer::intValue))
        .skip(3)
        .limit(6)
        .collect(Collectors.toList());
collect2.forEach(x -> System.out.print(x + " ")); 

发表回复