Java stream的使用
java.util.function 包中的接口分为四类,分别是 Consumer(消费型接口)、Supplier(供给型接口)、Predicate(谓词型接口)以及Function(功能型接口)。
- Consumer 接口传入一个泛型参数(generic argument),不返回任何值;
- Supplier 接口不传入参数,返回一个值;
- Predicate 接口传入一个参数,返回一个布尔值;
- Function 接口传入一个参数,返回一个值。
筛选
IntStream intStream = IntStream.range(1, 100).filter(n -> n > 50);
- distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。
List<Dish> dishes = menu.stream().filter(dish -> dish.getType() == Dish.Type.MEAT).limit(2).collect(toList());
切片
- Java 9引入了两个新方法,可以高效地选择流中的元素,这两个方法分别是:takeWhile和dropWhile。
dropWhile操作是对takeWhile操作的补充。即便要处理的对象是一个由无限数量元素构成的流,它也能工作得很好。
List<Dish> slicedMenu1 = specialMenu.stream().takeWhile(dish -> dish.getCalories() < 320).collect(toList());
List<Dish> slicedMenu2 = specialMenu.stream().dropWhile(dish -> dish.getCalories() < 320).collect(toList());
- 截短流 limit:limit也可以用在无序流上,limit的结果不会以任何顺序排列。
List<Dish> dishes = specialMenu.stream().limit(3).collect(toList());
- 跳过元素 skip:返回一个扔掉了前n个元素的流
Stream<Dish> dishes = menu.stream().skip(2);
映射
Stream<String> dishNames = menu.stream().map(Dish::getName);
- 流的扁平化
List<String> words = Arrays.asList("Hello", "World");
Stream<Stream<String>> streamStream = words.stream().map(word -> word.split("")).map(Arrays::stream);
Stream<String> stringStream = words.stream().map(word -> word.split("")).flatMap(Arrays::stream);
查找和匹配
Stream API通过allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了这样的工具。
- boolean anyMatch(Predicate<? super T> predicate); 至少有一个
- boolean allMatch(Predicate<? super T> predicate); 都是
- boolean noneMatch(Predicate<? super T> predicate); 都不是
- Optional
findFirst(); 顺序符合的第一个 - Optional
findAny(); 符合的任意一个
归约
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
- 初始值的定义(Identity),累加器(Accumulator),组合器(Combiner)
- Identity : 定义一个元素代表是归并操作的初始值,如果Stream 是空的,也是Stream 的默认结果
- Accumulator: 定义一个带两个参数的函数,第一个参数是上个归并函数的返回值,第二个是Strem 中下一个元素。
- Combiner: 调用一个函数来组合归并操作的结果,当归并是并行执行或者当累加器的函数和累加器的实现类型不匹配时才会调用此函数。
List<Integer> intList = Arrays.asList(1,2,3);
Optional<Integer> result1=intList.stream().reduce(Integer::sum);
System.out.println(result1);
Integer result2=intList.stream().reduce(100, Integer::sum);
System.out.println(result2);
Integer result3=intList.parallelStream().reduce(100, Integer::sum);
System.out.println(result3);
Integer result4=intList.stream().reduce(100, Integer::sum, Integer::sum);
System.out.println(result4);
Integer result5=intList.parallelStream().reduce(100, Integer::sum, Integer::sum);
System.out.println(result5);
非是计算不对
而是 identity必须是accumulator函数的一个identity,也就是说必须满足:对于所有的t,都必须满足 accumulator.apply(identity, t) == t
而且identity需要满足combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
Integer result7=intList.stream().reduce(0, Integer::sum);
System.out.println(result7);
Integer result8=intList.parallelStream().reduce(0, Integer::sum);
System.out.println(result8);
Integer result9=intList.stream().reduce(0, Integer::sum, Integer::sum);
System.out.println(result9);
Integer result0=intList.parallelStream().reduce(0, Integer::sum, Integer::sum);
System.out.println(result0);
数值流
Java 8引入了三个原始类型特化流接口:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱成本。
- 这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性——即类似int和Integer之间的效率差异。
range和rangeClosed。
- 这两个方法都是第一个参数接受起始值,第二个参数接受结束值。
- 但range是不包含结束值的,rangeClosed则包含结束值
IntStream.range(1, 100).forEachOrdered(n -> System.out.println(n));
IntStream.rangeClosed(1, 100).forEachOrdered(n -> System.out.println(n));
boxed
- Stream
boxed();
boxed方法把原始流转换成一般流(每个int都会装箱成一个Integer)
IntStream.range(1, 100).boxed();
- Optional原始类型特化版本:OptionalInt、OptionalDouble和OptionalLong 会有一个初始值0
创建流
- 由值创建流
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");
Stream<String> emptyStream = Stream.empty();
- 由可空对象创建流
Stream<String> homeValueStream = Stream.ofNullable(System.getProperty("home"));
Stream<String> values = Stream.of("config", "home", "user").flatMap(key -> Stream.ofNullable(System.getProperty(key)));
- 由数组创建流
/**
* public static IntStream stream(int[] array)
*/
int[] numbers = {2, 3, 5, 7, 11, 13};
Stream<Integer> arrays = Arrays.stream(numbers).boxed();
- 由文件生成流
/**
* public static Stream<String> lines(Path path, Charset cs) throws IOException
* 流会自动关闭,因此不需要执行额外的try-finally操作
*/
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
- 由函数生成流:创建无限流
/**
* Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。
* 这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。
*/
Stream<Integer> integerStream = Stream.iterate(0, n -> n + 2).limit(10);
// Java 9对iterate方法进行了增强,它现在可以支持谓词操作了。
Stream<Integer> integerStream2 = IntStream.iterate(0, n -> n < 100, n -> n + 4);
Stream<Double> integerStream3 = Stream.generate(Math::random).limit(5);
顺序流和并行流
- S parallel()
将流转换成并行流
- S sequential()
将流转换成顺序流
排序
list.stream().sorted(Comparator.comparing(Long::longValue).reversed()).collect(Collectors.toList());
求取最大值
int a = Stream.of(2,1,4,5,3).max(Integer::compare).get();------5
int b = Stream.of(2,1,4,5,3).min(Integer::compare).get();------1
用Integer::compare即可。也可以直接:
int a = Stream.of(1,2,4,5,3).mapToInt(i -> i).max().getAsInt();