Java 8 Stream
什么是stream?
stream使用的是函数式编程模式,流表示包含着一系列元素的集合,我们可以对其做不同类型的操作,用来对这些元素执行计算。
-
Stream 不是集合 , 也不是数据结构 , 不可以保存数据
-
Stream 有点类似于高级 的
Iterator, 可以用于算法和计算 -
不同于迭代器 , Stream 可以并行化操作 , 数据被分为很多段 , 在不同的线程中进行处理
-
数据源、零个或多个中间操作 (
intermediate) 以及零个或一个终端操作 (terminal) -
所有中间操作都是惰性的 , 在管道开始工作之前,任何操作都不会产生任何效果
-
终端操作有点像水龙头 , 开启了水龙头后 , 水才会流动 , 中间操作才会执行
如:
List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream() // 创建流
.filter(s -> s.startsWith("c")) // 执行过滤,过滤出以 c 为前缀的字符串 ①start
.map(String::toUpperCase) // 转换成大写
.sorted() // 排序 ①end中间操作
.forEach(System.out::println); // for 循环打印 ②终端操作
// C1
// C2
可以对流进行中间操作或者终端操作.
- ①中间操作会再次返回一个流,所以,我们可以链接多个中间操作,注意这里是不用加分号的。如上
filter过滤,map对象转换,sorted排序,就属于中间操作。 - ②终端操作是对流操作的一个结束动作,一般返回
void或者一个非流的结果。如上forEach循环 就是一个终止操作。
Java8 Stream 流是不能被复用的,一旦你调用任何终端操作,流就会关闭:
Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true); // ok
stream.noneMatch(s -> true); // exception
当我们对 stream 调用了 anyMatch 终端操作以后,流即关闭了,再调用 noneMatch 就会抛出异常:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
at test.demo.main(demo.java:22)
为了克服这个限制,我们必须为我们想要执行的每个终端操作创建一个新的流链,例如,通过 Supplier 来包装一下流,通过 get() 方法来构建一个新的 Stream 流,如下所示:
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok true 流中任一元素满足 s.startsWith("a")
streamSupplier.get().noneMatch(s -> true); // ok false 流中所有元素都不满足 s.startsWith("a")
通过构造一个新的流,来避开流不能被复用的限制, 这也是取巧的一种方式。
函数式接口 Supplier
java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
高级操作
Streams 支持的操作很丰富,除了上面介绍的这些比较常用的中间操作,还有一些更复杂的操作,如
- forEach 迭代流中的每个数据
- map 映射每个元素对应的结果
- filter 设置条件过滤元素
- limit 获取指定数量的流
- sorted 对流进行排序(降序.sorted(Comparator.reverseOrder()))
- parallelStream 并行流
- Collectors 实现归约操作
例如:
forEach | map | filter | sorted | limit
List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1","Cc","cd");
myList.stream() // 创建流
.filter(s -> s.startsWith("c")) // 执行过滤,过滤出以 c 为前缀的字符串
.map(String::toUpperCase) // 转换成大写
.sorted()// 排序
.limit(3)//截取前3个
.forEach(System.out::println);//for 循环打印
//C1
//C2
//CD
Collectors
- 将流转换成集合和聚合元素,Collectors用于返回列表或字符串;
.collect(Collectors.toList()) - 合并字符串将“,”去除。
.collect(Collectors.joining(","))
统计
- 主要int double long 类型获取最大值.getMax(),最小数.getMin(),所有数和.getSum(),平均数.getAverage()
groupingBy
- 分组求和,例如计算某年有多少钱(因为money类型为BigDecimal类型,所以需要映射)
Map<String,Optional<BigDecimal>> item = list.stream() .collect(Collectors.groupingBy(List::getYear,Collectors.mapping(List::getMoney, Collectors.reducing(BigDecimal::add))));
- 只是根据某个字段进行分组:
Map<String,List<ListDto>> monthMap = list.stream().collect(Collectors.groupingBy(ListDto::getMonth));
并行流
// API :
Stream<E> parallelStream()
// 例子 :
并行流计算 parallelStream()
//年加权融资成本
Map<String,BondPledgeBusinessDto>
List<BondPledgeBusinessDto> yearTJ =
bondPledgeBusinessDtoList.parallelStream().collect(Collectors.groupingBy(BondPledgeBusinessDto::getYear,
Collectors.collectingAndThen(Collectors.toList(),item->{
//年含权融资成本
BigDecimal HQRZ = item.parallelStream().map(BondPledgeBusinessDto::getHQRZCBH).reduce(BigDecimal.ZERO,BigDecimal::add);
//年权重
BigDecimal QZ = item.parallelStream().map(BondPledgeBusinessDto::getWeight).reduce(BigDecimal.ZERO,BigDecimal::add);
//年加权融资成本
BigDecimal JQZRCB = HQRZ.divide(QZ4,RoundingMode.HALF_UP);
BigDecimal ZSY1 = item.parallelStream().map(BondPledgeBusinessDto::getSY).reduce(BigDecimal.ZERO,BigDecimal::add);
return new BondPledgeBusinessDto();
}))
).entrySet()
.stream()
.map(Map.Entry::getValue)
.collect(Collectors.toList());
flatMap()
flatMap()操作得作用是对流得元素进行一对多转换,将结果元素展平为新的流。
Stream.flatMap()有助于将Collection<Collection<T>>转换为Collection<T>
- flatMap()= map()+展平
- 例子:
//Before flattening : [[1,2,3], [4,5], [6,7,8]]
//After flattening : [1,2,3,4,5,6,7,8]
List<Integer> list1 = Arrays.asList(1,2,3);
List<Integer> list2 = Arrays.asList(4,5,6);
List<Integer> list3 = Arrays.asList(7,8,9);
List<List<Integer>> listOfLists = Arrays.asList(list1, list2, list3);
List<Integer> listOfAllIntegers = listOfLists.stream()
.flatMap(x -> x.stream())
.collect(Collectors.toList());
/*或者
String[][] dataArray = new String[][]{{"1","2","3"}, {"4","5"}, { "6","7","8"}};
List<String> listOfAllChars = Arrays.stream(dataArray)
.flatMap(x -> Arrays.stream(x))
.collect(Collectors.toList());
System.out.println(listOfAllChars);
*/
System.out.println(listOfAllIntegers);
peek()
peek(Consumer action)方法,该方法在应用方法参数ConsumerAction之后返回一个包含原始流的所有元素的新流
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> newList = list.stream()
.peek(System.out::println)
.collect(Collectors.toList());//没有任何终端操作的`Stream.peek()`不会执行任何操作
System.out.println(newList);
/*
1
2
3
4
5
[1,2,3,4,5]
*/
skip()
stream.skip(long n)方法从流中跳过第n个元素。skip()方法返回一个由该流得其余元素组成的流。
skip(long n)在按encounter order丢弃流的前'n'元素后,返回由流的其余元素组成的流。
//例子
Stream<Integer> evenNumInfiniteStream = Stream.iterate(0 , n -> n + 2);
List<Integer> newList = evenNumInfiniteStream
.skip(5)
.limit(10)
.collect(Collectors.toList());
System.out.println(newList);
/*[10,12,14,16,18,20,22,24,26,28]*/
forEachOrdered
foreachored()方法对该流中的每个元素执行一个操作,确保对具有定义的遇到顺序的流按遇到顺序处理每个元素。
toArray()
Stream.toArray()方法将流转换为数组
`IntStream infiniteNumberStream = IntStream.iterate(1, i -> i+``1``);
Integer[] integerArray = infiniteNumberStream.limit(10)
.boxed()
.toArray(Integer[]::new);
System.out.println(Arrays.toString(integerArray));
/**
[1,2,3,4,5,6,7,8,9,10]
*/
我们可以使用Stream.filter()方法传递一个字段该将仅返回与该前置条件匹配的那些元素。
List<Employee> employeeList = new ArrayList<>(Arrays.asList(
new Employee(1, "A", 100),
new Employee(2, "B", 200),
new Employee(3, "C", 300),
new Employee(4, "D", 400),
new Employee(5, "E", 500),
new Employee(6, "F", 600)));
Employee[] employeesArray = employeeList.stream()
.filter(e -> e.getSalary() < 400)
.toArray(Employee[]::new);
System.out.println(Arrays.toString(employeesArray));
推荐帖子:Java8 Stream 数据流,大数据量下的性能效率怎么样?
blog.csdn.net/weixin_3840…