JDK8的新特性(五)— —Stream

文章目录

    • 一、何以为“Stream”
    • 二、“Stream”的特点
    • 三、“Stream”的构成
    • 四、Stream的常用API
          • 4.1 筛选与切片
          • 4.2 映射
          • 4.3 排序
          • 4.4 消费
          • 4.5 流的终止操作
            • 4.5.1 匹配、聚合操作
            • 4.5.2 规约操作
          • 4.6 收集操作
    • 参考链接

一、何以为“Stream”

个人感觉Stream是作为JDK8最出色的成果,没有之一。Stream的存在是对于过去集合类(Collection)的增强,它提供大量的API对于集合对象的聚合操作,这些API使用起来十分便利,并且使代码的可读性大大提高。
首先,我们一起来感受下Stream的能力

例1:在一个ArrayList中添加如下角色:
	乔峰、段誉、虚竹、天山童姥、王语嫣、张无忌、张三丰、张飞
需求:1.得到所有姓张的;
	2.得到所有名字是2个字的;
	3.遍历打印所有名字
public class DemoStream1 {
    public static void main(String[] args) {
        ArrayList<String> nameList = new ArrayList<>();
        Collections.addAll(nameList, "乔峰", "段誉", "虚竹", "天山童姥", "王语嫣", "张无忌", "张三丰", "张飞");

        // 1.得到所有姓张的;
        ArrayList<String> zhangList = new ArrayList<>();
        for (String s : nameList) {
            if(s.contains("张")){
                zhangList.add(s);
            }
        }
        System.out.println("zhangList = " + zhangList.toString());

        List<String> zhangList1 = nameList.stream().filter(name->name.contains("张")).collect(Collectors.toList());
        System.out.println("zhangList1 = " + zhangList1.toString());
        System.out.println("-----------------------");

        // 2.得到所有名字是2个字的;
        ArrayList<String> twoList = new ArrayList<>();
        for (String s : nameList) {
            if(s.length() == 2){
                twoList.add(s);
            }
        }
        System.out.println("twoList = " + twoList.toString());

        List<String> twoList2 = nameList.stream().filter(name->name.length() == 2).collect(Collectors.toList());
        System.out.println("twoList2 = " + twoList2.toString());
        System.out.println("-----------------------");

        // 3.遍历打印所有名字
        for (String s : nameList) {
            System.out.println(s);
        }
        System.out.println("-----------------");
        nameList.stream().forEach(System.out::println);
    }
}

==运行结果==
zhangList = [张无忌, 张三丰, 张飞]
zhangList1 = [张无忌, 张三丰, 张飞]
-----------------------
twoList = [乔峰, 段誉, 虚竹, 张飞]
twoList2 = [乔峰, 段誉, 虚竹, 张飞]
-----------------------
乔峰
段誉
虚竹
天山童姥
王语嫣
张无忌
张三丰
张飞
-----------------
乔峰
段誉
虚竹
天山童姥
王语嫣
张无忌
张三丰
张飞

通过以上代码,我们可以轻易地发现。过去对于集合类的操作过于繁琐;而通过Steam的API,我们可以是代码更加简洁,如果使用流的并发模式,当数据量爆炸的时候,还可以提高执行效率。

二、“Stream”的特点

  1. 单向,数据只能遍历一次
  2. 不是数据结构,不会保存数据。
  3. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
public class DemoStream2 {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("张三", "李四", "王五");

        stream1.forEach(System.out::println);
        System.out.println("--------------");
        stream1.forEach(System.out::println);
    }
}

==运行结果==
张三
李四
王五
Exception in thread "main" --------------
java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at com.onlymark.demo1.jdk8.DemoStream2.main(DemoStream2.java:21)

三、“Stream”的构成

  • Collection.stream() 和 Collection.parrellelStream()
  • Arrays.stream(T array)
  • Stream.of()
// Collection.stream() 和 Collection.parrellelStream()
List<String> list = new ArrayList<>();
Stream<String> stream3 = list.stream();
        
// Arrays.stream(T array)
int[] intArr = new int[5];
IntStream stream = Arrays.stream(intArr);

// Stream.of()
Stream<String> stream5 = Stream.of("张三", "李四", "王五");

四、Stream的常用API

这部分内容大部分借鉴了Java 8 stream的详细用法这篇Blog的内容,仰望大神。

方法名 描述 返回值类型 方法类型
count 统计 long 终结
forEach 逐个处理 void 终结
filter 过滤 Stream 管道
limit 取前N个 Stream 管道
skip 跳过前N个 Stream 管道
distinct 去重 Stream 管道
map 映射 Stream 管道
concat 组合 Stream 管道
4.1 筛选与切片
  • filter:过滤流中的某些元素
  • limit(n):获取n个元素
  • skip(n):跳过n元素,配合limit(n)可实现分页
  • distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素
public class DemoStream3 {
    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 3, 4, 5, 6, 7, 11, 13, 14, 14, 14, 20);

        stream1.filter(i->i>4)  //5, 6, 7, 11, 13, 14, 14, 14, 20
                .limit(8)   //5, 6, 7, 11, 13, 14, 14, 14
                .skip(2)    //7, 11, 13, 14, 14, 14
                .distinct() //7, 11, 13, 14
                .forEach(System.out::println);
    }
}

==运行结果==
7
11
13
14
4.2 映射
  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。在这里插入代码片
public class DemoStream4 {
    public static void main(String[] args) {
        List<String> strList = Arrays.asList("h,e,l,l,o", "w,o,r,l,d");

        //map
        strList.stream()
                .map(str->str.replace(",", ""))// "hello", "world"
                .forEach(System.out::println);

        System.out.println("---------------");

        //flatmap
        strList.stream()
                .flatMap(str->{
                    String[] split = str.split(",");
                    Stream<String> stream = Arrays.stream(split);
                    return stream;
                })
                .forEach(System.out::println);
    }
}

==运行结果==
hello
world
---------------
h
e
l
l
o
w
o
r
l
d
4.3 排序
  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):定制排序,自定义Comparator排序器
public class DemoStream5 {
    public static void main(String[] args) {
        Stream<Integer> integerStream = Stream.of(2, 1, 5, 4, 7);
        //sorted
        integerStream.sorted().forEach(System.out::println);

        List<Person> personList = new ArrayList<>();
        Person p1 = new Person("小明", 12);
        Person p2 = new Person("小红", 11);
        Person p3 = new Person("小刚", 13);
        Collections.addAll(personList, p1, p2, p3);

        //sorted(Comparator com)
        personList.stream().sorted((person1, person2)->{
            return person1.getAge() - person2.getAge();
        }).forEach(p-> System.out.println(p.toString()));
    }
}

==运行结果==
1
2
4
5
7
Person{name='小红', age=11}
Person{name='小明', age=12}
Person{name='小刚', age=13}
4.4 消费
  • peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
public class DemoStream6 {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        Person p1 = new Person("小明", 12);
        Person p2 = new Person("小红", 11);
        Person p3 = new Person("小刚", 13);
        Collections.addAll(personList, p1, p2, p3);

        //peek
        personList.stream()
                .peek(person -> person.setAge(100))
                .forEach(person -> System.out.println(person.toString()));
    }
}

==运行结果==
Person{name='小明', age=100}
Person{name='小红', age=100}
Person{name='小刚', age=100}
4.5 流的终止操作
4.5.1 匹配、聚合操作
  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  • findFirst:返回流中第一个元素
  • findAny:返回流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中元素最大值
  • min:返回流中元素最小值
public class DemoStream7 {
    public static void main(String[] args) {
        List<Integer> integerList = Arrays.asList(1, 2, 3, 5, 6, 7, 9, 11, 13, 20, 99, 100);

        //allMatch
        boolean b1 = integerList.stream().allMatch(integer -> integer > 99);
        System.out.println("b1 = " + b1);
        //noneMatch
        boolean b2 = integerList.stream().noneMatch(integer -> integer > 100);
        System.out.println("b2 = " + b2);
        //anyMatch
        boolean b3 = integerList.stream().anyMatch(integer -> integer == 11);
        System.out.println("b3 = " + b3);

        //findFirst
        Integer i1 = integerList.stream().findFirst().get();
        System.out.println("i1 = " + i1);
        //findAny
        Integer i2 = integerList.stream().findAny().get();
        System.out.println("i2 = " + i2);

        //count
        long count1 = integerList.stream().count();
        System.out.println("count1 = " + count1);
        //max
        Integer max = integerList.stream().max(Integer::compareTo).get();
        System.out.println("max = " + max);
        //min
        Integer min = integerList.stream().min(Integer::compareTo).get();
        System.out.println("min = " + min);

    }
}

==运行结果==
b1 = false
b2 = true
b3 = true
i1 = 1
i2 = 1
count1 = 12
max = 100
min = 1
4.5.2 规约操作
  • Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
  • T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
public class DemoStream8 {
    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        Integer v = intList.stream().reduce((x1, x2) -> x1 + x2).get();
        System.out.println(v);   // 55
        System.out.println("-----------");
        Integer v1 = intList.stream().reduce(10, (x1, x2) -> x1 + x2);
        System.out.println(v1);  //65
    }
}

==运行结果==
55
-----------
65
4.6 收集操作
public class DemoStream9 {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        Person p1 = new Person("小明", 12);
        Person p2 = new Person("小红", 11);
        Person p3 = new Person("小刚", 13);
        Person p4 = new Person("小王", 12);
        Collections.addAll(personList, p1, p2, p3, p4);

        //封装list
        List<String> nameList = personList.stream().map(person -> person.getName()).collect(Collectors.toList());
        System.out.println("nameList = " + nameList);
        //封装set
        Set<String> nameSet = personList.stream().map(person -> person.getName()).collect(Collectors.toSet());
        System.out.println("nameSet = " + nameSet);
        //封装成map
        Map<String, Integer> personMap = personList.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
        System.out.println("personMap = " + personMap);
        //字符串分隔符连接
        String nameJoinStr = personList.stream().map(Person::getName).collect(Collectors.joining("$"));
        System.out.println("nameJoinStr = " + nameJoinStr);

        //聚合操作
        //总数
        Long personNum = personList.stream().collect(Collectors.counting());
        System.out.println("personNum = " + personNum);
        //求所有人的年龄和1
        Integer ageSum = personList.stream().map(Person::getAge).collect(Collectors.summingInt(age -> age));
        System.out.println("ageSum = " + ageSum);
        //求所有人的年龄和2
        Integer ageSum1 = personList.stream().collect(Collectors.summingInt(Person::getAge));
        System.out.println("ageSum1 = " + ageSum1);
        //求所有人的年龄和3
        Integer ageSum2 = personList.stream().map(Person::getAge).reduce((age1, age2) -> age1 + age2).get();
        System.out.println("ageSum2 = " + ageSum2);

        //求所有人的年龄平均数
        Double ageAvg = personList.stream().collect(Collectors.averagingDouble(Person::getAge));
        System.out.println("ageAvg = " + ageAvg);

        //分组
        Map<Integer, List<Person>> personAgeGroup = personList.stream().collect(Collectors.groupingBy(Person::getAge));
        System.out.println("personAgeGroup = " + personAgeGroup);
        //多层分组
        Map<Integer, Map<String, List<Person>>> agenameGroup = personList.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getName)));
        System.out.println("agenameGroup = " + agenameGroup);

        //分区
        Map<Boolean, List<Person>> collect = personList.stream().collect(Collectors.partitioningBy(person -> person.getAge() > 12));
        System.out.println("collect = " + collect);
    }
}

==运行结果==
Connected to the target VM, address: '127.0.0.1:56217', transport: 'socket'
nameList = [小明, 小红, 小刚, 小王]
nameSet = [小刚, 小明, 小王, 小红]
personMap = {小刚=13, 小明=12, 小王=12, 小红=11}
nameJoinStr = 小明$小红$小刚$小王
personNum = 4
ageSum = 48
ageSum1 = 48
ageSum2 = 48
ageAvg = 12.0
personAgeGroup = {11=[Person{name='小红', age=11}], 12=[Person{name='小明', age=12}, Person{name='小王', age=12}], 13=[Person{name='小刚', age=13}]}
agenameGroup = {11={小红=[Person{name='小红', age=11}]}, 12={小明=[Person{name='小明', age=12}], 小王=[Person{name='小王', age=12}]}, 13={小刚=[Person{name='小刚', age=13}]}}
Disconnected from the target VM, address: '127.0.0.1:56217', transport: 'socket'
collect = {false=[Person{name='小明', age=12}, Person{name='小红', age=11}, Person{name='小王', age=12}], true=[Person{name='小刚', age=13}]}

参考链接

Java 8 中的 Streams API 详解
Java 8 stream的详细用法

你可能感兴趣的:(java)