【java8】Stream流:常用方法案例总结

297 阅读9分钟

携手创作,共同成长!开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

1. 什么是Stream流?

    stream是数据通道,用于操作数据源所生成的元素序列,它可以实现对集合、数组的复杂操作,进行简化开发。

2. Stream流的执行流程

2.1 Stream流生命周期

graph LR
no1("数据源<br/>(集合、数组等)") --> no2(Stream流获取) --> no3("Stream流中间操作<br/>(filter、map/flatMap等)") --> no4("Stream流终结操作<br/>(forEach、collect等)")

    注意:Stream流中间的链式操作,对数据源的数据进行处理(例如过滤、排序等),直到执行终止操作作才执行(Stream流操作是延迟执行的)

3. Stream流的特点

  1. Stream流配合lambda表达式使用使代码更加简洁明了。
  2. 对集合、数组的流式操作效率更高。
  3. 延迟执行的特性,避免了多余的开销。
  4. 提供了并行流,可以满足程序多线程处理数据的能力。(谨慎使用)

4. Stream流的使用

4.1 Stream流的创建

  1. 数组和集合创建流
//数组(通过静态方法获取流)
String[] arr = new String[]{"A", "B"};
Stream<String> stream01 = Stream.of(arr);
Stream<String> stream02 = Arrays.stream(arr);

//集合(Collection体系集合:使用默认方法stream()生成流)
List<String> list = Arrays.asList(arr);
Stream<String> stream03 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream04 = set.stream();

//Map集合(Map转成Set集合,间接的生成流)
Map<String,Object> map = new HashMap<>();
Stream<String> stream05 = map.keySet().stream();
Stream<Object> stream06 = map.values().stream();
Stream<Map.Entry<String, Object>> stream07 = map.entrySet().stream();
  1. 其他常用创建流方式
//Stream.generate()返回一个无穷序列无序流
Stream<Integer> generate = Stream.generate(new Random()::nextInt).limit(3);
System.out.println(generate.collect(Collectors.toList()));

//Stream.iterate()返回由迭代产生的无限的数据
Stream.iterate(0,x->x+1).limit(10).forEach(System.out::println);

//Pattern.splitAsStream()返回将字符串分隔成流
Pattern.compile(" ").splitAsStream("hello world").forEach(System.out::println);

//BufferedReader.lines()将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("文件路径"));
Stream<String> lineStream = reader.lines();

//创建并行流
Stream<String> stream = list.parallelStream();

//创建一个空的stream
Stream<Integer> emptyStream  = Stream.empty();

//类似取1-5之间的数字生成流(range不包含5,rangeClosed包含5)
IntStream.range(1, 5);
IntStream.rangeClosed(1, 5);
//LongStream
//DoubleStream

4.2 Stream流的中间方法

4.2.1 常用函数式接口

接口名抽象方法概述
Function<T,R>R apply(T)函数型接口:该接口接收到数据,操作完数据,返回新的数据
Predicateboolean test(T t)断言型接口:对数据做出指定的判断,返回判断结果
Consumervoid accept(T t)消费型接口:消费一个参数数据
SupplierT get()供给型接口:返回一个指定参数的值
  • 学习Stream流之前需要对常用的函数式接口有一定的了解,才能看懂方法的具体的使用。

4.2.2 并行流、串行流、无序流

  • 串行流:串行流是基于pipeline(管道)的,串行流上的操作在单独的一个线程中执行的。

  • 并行流:并行流可以利用系统多cup特性,底层主要运用Fork/Join框架进行实现,fork根据cpu核数进行数据分块,join对各个fork进行合并。(存在线程安全性问题)

  • 无序流:不会执行任何操作来显式地对流进行排序。它的作用是消除了流必须保持有序的约束,从而允许后续操作使用不必考虑排序的优化。

4.2.3 常用的中间方法及其案例

方法名修饰符和类型说明
map Stream map(Function<? super T, ? extends R> mapper)接收一个函数为参数,该函数应用到每个元素上,并将其映射成一个新元素(一对一)
flatMap Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)接收一个函数为参数,该函数应用到每个元素上,并将其映射成一个新元素(一对多)
filterStream filter(Predicate<? super T> predicate)设置条件来过滤元素形成新的Stream流
distinctStream distinct()去除Stream流中重复的元素
sortedStream sorted(Comparator<? super T> comparator)指定对应的规则进行排序
peekStream peek(Consumer<? super T> action)接收一个函数为参数,用于查看该参数,但不会映射返回新元素,主要用于调试
limitStream limit(long maxSize)用于获取指定数量的流,配合skip跳过多少元素 可分页
skipStream skip(long n)用于指定跳过多少元素,配合limit使用,可分页
parallelS parallel()基于调用流,返回并行流
sequentialS sequential()基于调用流,返回串行流
unorderedS unordered()基于调用流,返回无序流
concat Stream concat(Stream<? extends T> a, Stream<? extends T> b)将两个Stream流合并为新的Stream流
//map
Arrays.stream(new String[]{"A", "B"}).map(String::toUpperCase).forEach(System.out::println);

//flatMap
Stream<List<String>> stream = Stream.of(
        Arrays.asList("A"),
        Arrays.asList("B", "C")
);
Stream<String> outputStream = stream.
        flatMap((childList) -> childList.stream());
outputStream.forEach(System.out::println);

//filter
Stream.of("A","B").filter((str) -> {return "A".equals(str);}).forEach(System.out::println);

//distinct
Stream.of("A","B","A").distinct().forEach(System.out::println);

//sorted
Stream.of("A","B","A").sorted().forEach(System.out::println);

//peek
Stream.of("A","B","A").sorted().peek(System.out::println).count();

//limit
Stream.of("A","B","A").limit(1).forEach(System.out::println);

//skip
Stream.of("A","B","A").skip(1).forEach(System.out::println);

//concat
Stream.concat(Stream.of("A","B","A"),Stream.of("C","D","F")).forEach(System.out::println);

//parallel
Stream<String> parallel = Stream.of("A", "B", "A").parallel();

//sequential
Stream.of("A","B","A").sequential().forEach(System.out::println);

//unordered
IntStream.rangeClosed(1, 100).unordered().forEach(System.out::println);

4.3 Stream流的终结方法及其案例

  • Stream流的惰性机制,只有执行终结方法时,才会执行之前的中间方法。
方法名修饰符和类型说明
forEachvoid forEach(Consumer<? super T> action)接收一个Lambda表达式,Stream流的每一个元素上执行该表达式
forEachOrderedvoid forEachOrdered(Consumer<? super T> action)接收一个Lambda表达式,Stream流的每一个元素按照顺序上执行该表达式(并行流无法保证有序性)
toArray1、Object[] toArray();
2、<A> A[] toArray(IntFunction<A[]> generator);
返回该包含该流的元素数组,可指定具体参数
reduce1、Optional reduce(BinaryOperator accumulator);
2、T reduce(T identity, BinaryOperator accumulator)
3、 U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner)
把Stream流中元素组合起来并返回,sum可以理解为特殊的reduce
collect1、<R, A> R collect(Collector<? super T, A, R> collector)
2、 R collect(Supplier supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)
收集器,将Stream流中的元素根据接受的参数类型返回(具体参数参考下表)
minOptional min(Comparator<? super T> comparator)根据提供的Comparator返回此流的最小值
maxOptional max(Comparator<? super T> comparator)根据提供的Comparator返回此流的最大值
countlong count()返回流中元素数量
anyMatchboolean anyMatch(Predicate<? super T> predicate)流中存在元素符合predicate规则,则返回true
allMatchboolean allMatch(Predicate<? super T> predicate)流中全部元素符合predicate规则,则返回true
noneMatchboolean noneMatch(Predicate<? super T> predicate)流中没有元素符合predicate规则,则返回true
findFirstOptional findFirst()返回满足条件的第一个元素
findAnyOptional findAny()返回满足条件的任意元素
iteratorIterator iterator()返回迭代器

4.3.1 collect收集器常用参数

工厂方法返回类型说明
toCollectionCollection将Stream流中的元素收集到创建的集合中
toListList将Stream流中元素收集为List,默认为ArrayList
toSetSet将Stream流中元素收集为Set,默认为HashSet,自动去重
toMapMap<K,T>通过对应的生成器方法生成对应key和value并返回至集合中
toConcurrentMapConcurrentHashMap<K,T>与toMap差不多,返回线程安全的ConcurrentHashMap
countingLong计算Stream流中元素个数
minByOptional最小值的Optional,如果为空返回的是Optional.empty()
maxByOptional最大值的Optional,如果为空返回的是Optional.empty()
summingIntInteger求和,返回类型为Integer
summingLongLong求和,返回类型为Long
summingDoubleDouble求和,返回类型为Double
averagingIntDouble平均值,返回类型Integer
averagingLongDouble平均值,返回类型Long
averagingDoubleDouble平均值,返回类型Double
summarizingIntIntSummaryStatistics汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Integer
summarizingLongLongSummaryStatistics汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Long
summarizingDoubleDoubleSummaryStatistics汇总,返回包含元素个数、最大值、最小值、求和值、平均值,值类型为Double
joiningString连接流中每个元素的toString方法生成的字符串
mapping指定类型先对流中的每个元素进行映射,即类型转换,然后再将新元素给定的Collector进行合并返回
collectingAndThen转换函数返回的类型转换结束后,对其结果再进行处理返回
groupingByMap<K,List>根据流中元素的某个值对流中的元素进行分组,并将属性值做为结果map的键
groupingByConcurrentConcurrentHashMap<K,List>与groupingBy类似,返回ConcurrentHashMap
partitioningByMap<Boolean,List>将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List
reducing归纳返回类型与reduce方法类似

4.3.2 终结方法案例

//forEach
Stream.of("A","B","A").forEach(System.out::println);
Stream.of("A","B","A").forEachOrdered(System.out::println);

//toArray
Object[] objects = Stream.of("A", "B", "A").toArray();
String[] arr = Stream.of("A", "B", "A").toArray(String[]::new);

//==========reduce==========
String reduce = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
//求和
int sumValue01 = Stream.of(1, 2, 3, 4).reduce(0,Integer::sum);
//求和,sumValue = 10, 无起始值
int sumValue02 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
//过滤,字符串连接,concat = "ace"
String concat = Stream.of("a", "B", "c", "D", "e", "F").
                filter(x -> x.compareTo("Z") > 0).
                reduce("", String::concat);
//========================

//=======collect==========
//toList
List<Integer> list = Stream.of(1, 2, 3, 4).collect(Collectors.toList());

//toSet
Set<Integer> set = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());

//toCollection
ArrayList<Integer>arrayList=Stream.of(1,2,3,4).collect(Collectors.toCollection(ArrayList::new);

//toMap/ConcurrentHashMap
Map<Integer, Integer> map1 = Stream.of(1, 2, 3, 4, 5, 6)
                                   .collect(Collectors.toMap(i -> i, i -> i));
Map<Integer, Integer> map2 = Stream.of(1, 2, 3, 4, 5, 5)
                                   .collect(Collectors.toMap(i -> i, i -> i, (k1, k2) -> k1));
HashMap<Integer, Integer> map3 = Stream.of(1, 2, 3, 4, 5, 5)
                      .collect(Collectors.toMap(i -> i, i -> i, (k1, k2) -> k1, HashMap::new));

//counting
Long count = Stream.of(1, 2, 3, 4).collect(Collectors.counting());

//minBy/maxBy
Integer min = Stream.of(1, 2, 3, 4).collect(Collectors.minBy(Comparator.naturalOrder())).get();
Integer max = Stream.of(1, 2, 3, 4).collect(Collectors.maxBy(Comparator.naturalOrder())).get();

//summingInt/summingLong/summingDouble
Integer integerSum = Stream.of(1, 2, 3, 4).collect(Collectors.summingInt(i -> i));
Long longSum = Stream.of(1L, 2L, 3L, 4L).collect(Collectors.summingLong(i -> i));
Double doubleSum = Stream.of(1.1, 2.2, 3.3, 4.4).collect(Collectors.summingDouble(i -> i));

//averagingInt/averagingLong/averagingDouble
Double averagingInt = Stream.of(1, 2, 3, 4).collect(Collectors.averagingInt(i -> i));
Double averagingLong = Stream.of(1L, 2L, 3L, 4L).collect(Collectors.averagingLong(i -> i));
Double averagingDouble = Stream.of(1.1, 2.2, 3.3, 4.4).collect(Collectors.averagingDouble(i -> i));

//summarizingInt/summarizingLong/summarizingDouble
IntSummaryStatistics summarizingInt = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingInt(i -> i));
LongSummaryStatistics summarizingLong = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingLong(i -> i));
DoubleSummaryStatistics summarizingDouble = Stream.of(1, 2, 3, 4).collect(Collectors.summarizingDouble(i -> i));

//joining
String str = Stream.of("zs", "ls", "ww").collect(Collectors.joining("->"));

//mapping [列子:将对象里面的名字收集返回list]
Stream.of(userA,userB,userC).collect(Collectors.mapping(User::getName,Collectors.toList()));

//collectingAndThen [例子:将结果集转为大写]
Stream.of("zs","ls","ww").collect(Collectors.collectingAndThen(Collectors.joining(","),String::toUpperCase));

//groupingBy/groupingByConcurrent  [例子:将字符串长度作为键,将该长度的字符串列表作为值]
Map<Integer, List<String>> groupingBy = Stream.of("zs", "ls", "ww").collect(Collectors.groupingBy(String::length));

//partitioningBy [例子:将字符串长度大于3的字符串,与其他字符串分开保存]
Map<Boolean,List<String>>partitioningBy=Stream.of("123","11","3232").collect(Collectors.partitioningBy(x -> x.length() > 3));

//reducing [列子:求和]
Optional<Integer> a = Stream.of(12, 13, 14).reduce((num1, num2) -> {
    return num1 + num2;
});

//===================

//min 最小值
Stream.of(12, 13, 14).min(Integer::compareTo);

//max 最大值
Stream.of(12, 13, 14).max(Integer::compareTo);

//count 求元素个数
Stream.of(12, 13, 14).count();

//anyMatch
Stream.of(12, 13, 14).anyMatch(x -> x > 12);

//allMatch
Stream.of(12, 13, 14).allMatch(x -> x > 11);

//noneMatch
Stream.of(12, 13, 14).noneMatch(x -> x > 14);

//findFirst
Stream.of(12, 13, 14).filter(x -> x > 12).findFirst();

//findAny
Stream.of(12, 13, 14).filter(x -> x > 12).findAny();

//iterator
Iterator<Integer> iterator = Stream.of(12, 13, 14).iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}