stream流式API
stream 的思想是内部迭代,for-each是外部迭代。
流只能使用一次。
stream 优势:
- 并行 能充分利用多核
- 能直观描述数据的操作,代码可读性更高
创建流的方式
// 1、创建stream的方式,通过Collection集合提供的stream或者parallerStream()
ArrayList<Object> list = new ArrayList<>();
Stream<Object> stream1 = list.stream();
// list.parallelStream();
// 2、通过Arrays中的静态方法stram()获取数组流
Integer[] ints = new Integer[10];
Stream<Integer> stream2 = Arrays.stream(ints);
// 3、通过Stream中的静态of方法
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
// 4、创建无限流
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
String xx = stream3.reduce("xx", (x, y) -> {
return x + y;
});
流的中间操作和终端操作
中间操作:
- filter
- distinct
- skip
- limit
- map
- flatmap
- sorted
终端操作: - anyMatch
- noneMatch
- allMatch
- findAny
- findFirst
- forEach
- collect
- reduce
- count
原始类型流
- DoubleStream
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
- IntStream
IntStream mapToInt(ToIntFunction<? super T> mapper);
- LongStream
LongStream mapToLong(ToLongFunction<? super T> mapper);
lambda 表达式
lambda表达式可以作为参数传递给方法或者是存储在变量中
优势:简介,可读性好,正因为有了lambda表达式才有了jdk库里stream等流式API
劣势:个人认为目前存在最大的问题就是,由于lambda表达式没有名字,报错简陋,不方便调试。
ps: 因此,调试起来只能采用打印日志的方式就行调试,stream api 提供了peek方法,可以在元素恢复运行之前插入一段操作。
复合lambda表达式
比较器复合
transactions.stream().sorted(Comparator.comparing(Transaction::getValue)) 等价于//transactions.stream().sorted(Comparator.comparing((transaction -> transaction.getValue())))
逆序
transactions.stream().sorted(Comparator.comparing(Transaction::getValue).reversed())
比较器链
// 如果value一样 则比较year
transactions.stream()
.sorted(Comparator.comparing(Transaction::getValue)
.thenComparing(Transaction::getValue))
.forEach(System.out::println);
谓词复合
- negate() 现有predicate对象的非
- and()
- or ()
函数复合
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);//数学上等价于 g(f(x))
//Function<Integer, Integer> h = f.compose(g); // 数学上等价于 g(f(x))
函数式接口
定义:只定义一个抽象方法的接口,即使接口定义了很多默认方法。只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
常见的函数式接口有:Runnable、Compator、Callable
方法引用
定义:方法引用是lambda的快捷写法,以便于提高可读性
方法应用分类:
1、指向静态方法的方法引用
Integer::parseInt 等价于 (String str) ->Integer.parseInt(str)
2、指向实例对象的方法引用 (你在引用一个对象的方法,而这个对象本身是Lambda的一个参数)
List<String> list = Arrays.asList("a","b","c");
list.sort(String::compareTo); //等价于 (String str1,String str2)->str1.compareTo(str2);
3、指向外部对象的方法引用 (你在lambda中调用一个已经存在外部对象中的方法)
其它特殊的方法引用
构造函数方法引用
ClassName::new
无参构造函数方法引用
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get();
等价于:
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
一个参数构造方法引用
Function<Integer, Apple> c2 = Apple::new;
Apple a2 = c2.apply(110);
两个参数构造方法引用
BiFunction<String, Integer, Apple> c3 = Apple::new;
Apple c3 = c3.apply("green", 110);
使用流收集数据
筛选
- filter() 谓词筛选,filter接受一个谓词
- distinct() 去重
- limit()截断流
- skip()跳过流
映射
- map (对流中的每一个元素应用函数,返回的是Stream)
- flatMap 流的扁平化
flatMap作用:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
给定一个函数,flatmap会对原先流中的每个元素apply此函数,得到一个新的值,并将所有的新值放到一个新的流中,最后返回该新流即是流的扁平化。
map和flatMap的本质区别是map是返回一个新的元素,而flatmap是返回一个新的流。
书上的例子是这样的
List<String> collect =words.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
但是我最初的想法是,先将每个单词全部转换为大写,然后把每个字符切分成char[]数组,很遗憾的是
Steam不能够用char[] 构造,所以间接的用int[]构造流,然后转换为Character[],最后在将Character[]
扁平化到一个流中,然后去重收集起来。
Stream<String> words = Stream.of("hello", "world", "China");
List<Character> collect = words.map(String::toUpperCase)
.map((str) -> {// 这一段代码是为了把char[]流转换为Character[]流,很奇怪,能够根据int[] double[]构造流 却不能根据char[]构造流
Character[] characters = str.chars().mapToObj(c -> (char) c).toArray(Character[]::new);
return characters;
})
.flatMap((chars -> Arrays.stream(chars)))
.distinct()
.collect(Collectors.toList());
匹配
- anyMatch 检查是否至少有一个匹配
- allMatch 检查是否匹配所有元素
- noneMatch 确保没有任何元素与给定的谓词匹配
查找元素
- findAny 返回任意元素
- findFirst 查找第一个元素
规约
规约:将流中的元素反复结合起来,得到一个值,这样的查询被归类为规约操作。
元素求和
// 0是初始值 首先, 0作为Lambda(a)的第一个参数,从流中获得4作为第二个参数(b)。
0 +4得到4,它成了新的累积值。然后再用累积值和流中下一个元素5调用Lambda,产生新的累积值9。
int sum = numbers.stream().reduce(0, (a, b) -> a + b)
求最大值最小值
List<Integer> numbers = Arrays.asList(1, 2, 4);
Optional<Integer> max = numbers.stream().reduce(Integer::max);
收集器collect
查找最大值最小值
List<Integer> list = new ArrayList<>(Arrays.asList(100, 55, 34, 23, 88));
Optional<Integer> max = list.stream().collect(Collectors.maxBy(Integer::max));
System.out.println("最大值:"+max.get());//最大值:100
Optional<Integer> min = list.stream().collect(Collectors.minBy(Integer::compare));
System.out.println("最小值:"+min.get());//最小值:23
求和
Integer sum = list.stream().collect(Collectors.summingInt((num)->num));
System.out.println("总和:"+sum);//总和:300
求平均值
Double avg = list.stream().collect(Collectors.averagingInt((num) -> num));
System.out.println("平均值:"+avg);//平均值:60.0
连接字符串
// 连接字符串
String joining1 = list.stream()
.map((num) -> num.toString())
.collect(Collectors.joining());
String joining2 = list.stream()
.map((num) -> num.toString())
.collect(Collectors.joining(","));
System.out.println(joining1);//10055342388
System.out.println(joining2);//100,55,34,23,88
Collectors.reducing()
上面的收集器都可以用reducing实现,但是可读性降低了。
收集器转换
List<Integer> collect = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<Integer>(Integer::compareTo)), ArrayList::new));
分组
假设有一组学生成绩,按照优秀、一般、差进行分组。
Map<GradeLevel, List<Integer>> groups = list.stream().collect(Collectors.groupingBy((num) -> {
if (num < 60)
return GradeLevel.BAD;
else if (num < 80)
return GradeLevel.JUST_SO_SO;
else if (num > 80 && num <= 100)
return GradeLevel.GOOD;
else
return GradeLevel.ILGEAL;
}));
System.out.println(groups);//{BAD=[55, 34, 23], GOOD=[100, 88]}
多级分组
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
groupingBy的第二个参数接受Collector类型,因此可以传递分组收集器
// 男女生的成绩进行分组
Map<Character, Map<GradeLevel, List<Stuent>>> map = list.stream().collect(Collectors.groupingBy(Stuent::getSex, Collectors.groupingBy((stu) -> {
if (stu.getGrade() < 60)
return GradeLevel.BAD;
else if (stu.getGrade() < 80)
return GradeLevel.JUST_SO_SO;
else if (stu.getGrade() > 80 && stu.getGrade() <= 100)
return GradeLevel.GOOD;
else
return GradeLevel.ILGEAL;
})));
System.out.println(map);
分区
分区只是分组的特例而已
partitioningBy() 接受一个谓词,返回true 或者false
Map<Boolean, List<Stuent>> collect = list.stream().collect(Collectors.partitioningBy((stu) -> {
return '女' == (stu.getSex());
}));