天天看点

java8流式API

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表达式

比较器复合

逆序

比较器链

// 如果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、指向静态方法的方法引用

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());
        }));