stream流是支持数据处理操作的数据源生成的元素序列,这些数据源可以是集合、数组、文件I/O channel等。stream不是一种数据结构,也不会存储数据,并且它支持数据聚合操作,如过滤filter、映射map、去重distinct、匹配match等等。
stream流操作可以分为,生成stream流、操作stream流(中间操作和终端操作)。
有两种方式创建stream
stream() − 为集合创建串行流。parallelStream() − 为集合创建并行流。集合实例后面直接.stream()就可以非常方便的创建一个stream。比如List、Set、Map集合都可以通过这种方式创建stream。
List list = Arrays.asList("欢迎阅读", "鳄鱼儿");
list.stream();
list.parallelStream()
使用Arrays.stream()方法生成流,生成流的类型是数值流IntStream。除了IntStream、LongStream、 DoubleStream
int[] arr = new int[]{1,2,3};
IntStream stream = Arrays.stream(arr);
通过Files.lines(path, Charset)生成stream。
注意:此时生成的流需要需要手动关闭,通过一个BaseStream.close()方法和实现AutoCloseable。一般来说几乎所有的流实例实际上不需要在使用后关闭,只有来源为I/O channel的流才需要如此。
Stream lines = Files.lines(Paths.get("./test.txt"), Charset.defaultCharset());
stream操作类型分为两种,中间操作和终端操作。
中间操作即代表一个stream后返回的还是stream,其后面仍可以跟随中间操作,比如过滤filter后仍可以接distinct去重。
filter、distinct、sorted、limit、skip通过以下代码展示filter、distinct、sorted、limit、skip的用法。
public static void main(String[] args) {List list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);list.stream().filter(item -> item > 2) // 过滤元素 1.distinct() // 去重 12.sorted() // 排序.skip(2) // 跳过流中元素 4 6.limit(5) // 保留前5个元素.forEach(System.out::println); // 输出
}
map、mapToInt、mapToLong、mapToDouble、flatMapmap流映射,即将元素映射成另外一个新的元素,这是一种一对一关系。
flatMap流转换,即将一个流中的每个值都转换为另一个流,是一种一对多的关系.
比如参考以下代码
List stringList = Arrays.asList("欢迎 阅读", "鳄鱼儿 文章");
List strLen = stringList.stream().map(item -> item.length()).collect(Collectors.toList());
System.out.println(strLen);List newStrList = stringList.stream().flatMap(item -> Arrays.stream(item.split(" "))).collect(Collectors.toList());
System.out.println(newStrList);;
输出结果分别是
mapToInt、mapToLong、mapToDouble分别是返回一个IntStream、LongStream、DoubleStream。
这些类型的stream包括一些sum()、max()
min()方法等,可以用于计算,如下面代码求和示例:
List list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);
// 转换IntStream
list.stream().mapToInt(item -> item * 2).forEach(System.out::println);
// 对IntStream内元素进行计算,并求和
System.out.println(list.stream().mapToInt(item -> item * 2).sum());
peek、foreachpeek和foreach是对元素进行遍历处理的方法。
区别在于peek是中间操作,foreach是终端操作,
List list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);
// 如果没有终端操作,则peek不会执行
list.stream().peek(System.out::println);// 有终端操作,先执行peek,再返回count,最后通过println输出
System.out.println(list.stream().peek(System.out::println).count()
);
// foreach是终端操作
list.stream().forEach(System.out::println);
输出结果:
终端操作即一个stream的终止(关闭),一个stream中只能有一个终端操作。
allMatch、anyMatch、noneMatchallMatch:匹配所有元素anyMatch:匹配其中一个元素noneMatch:全部元素都不匹配,跟allMatch相反List integerList = Arrays.asList(1, 3);if (integerList.stream().allMatch(i -> i > 5)) {System.out.println("所有元素值都大于5");
} else {System.out.println("并非所有元素值都大于5");
}if (integerList.stream().anyMatch(i -> i > 5)) {System.out.println("存在值大于5的元素");
} else {System.out.println("不存在值大于5的元素");
}if (integerList.stream().noneMatch(i -> i > 5)) {System.out.println("元素值都小于5");
} else {System.out.println("元素值不都小于5");
}
输出结果:
count、max、mincount: 统计流中元素个数max: 获取流中最大值min: 获取流中最小值List stringList = Arrays.asList("欢迎 阅读", "鳄鱼儿 文章");Optional min = stringList.stream().map(String::length).min(Integer::compareTo);stringList.stream().mapToInt(String::length).min();
有两种写法,通过传参min(Comparatorcomparator)。或者转换成为IntStream再进行min操作。
findAny、findFirstfindAny:从符合条件的元素中,随机查找到一个元素findFirst:根据条件查找到符合条件的第一个元素List list = Arrays.asList(1, 12, 66, 12);System.out.println(list.stream().filter(i -> i > 3).findFirst().orElse(-1));
System.out.println(list.stream().filter(i -> i > 3).findAny().orElse(-1));
结果输出:
到此,基础Stream用户就已经说完了,看到这里我们会发现,所以的stream其实都可以用for循环来实现,而我们全篇都没有使用for循环来实现,这些如果用for循环实现,你会发现实现的语句变得复杂了很多,不信的话,你可以试试。
我们也可以看到stream带来的好处,包括代码更简洁、解耦等。