首页 > 其他分享 >【JDK8】新特性(二)

【JDK8】新特性(二)

时间:2024-07-09 13:55:12浏览次数:9  
标签:Stream stream System 特性 personList JDK8 println out

6. Stream API

Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。Stream是java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的査找、过滤、筛选等操作.

6.1 为什么使用stream流

当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:

1.一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
2 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据

代码如下:

public class My {
    public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 1.拿到所有姓张的
        ArrayList<String> zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
        for (String name : list) {
            if (name.startsWith("张")) {
                zhangList.add(name);
            }
        }
// 2.拿到名字长度为3个字的
        ArrayList<String> threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
        for (String name : zhangList) {
            if (name.length() == 3) {
                threeList.add(name);
            }
        }
// 3.打印这些数据
        for (String name : threeList) {
            System.out.println(name);
        }
    }
}

循环遍历的弊端

这段代码中含有三个循环,每一个作用不同:

  1. 首先筛选所有姓张的人;

  2. 然后筛选名字有三个字的人;

  3. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。

那Stream能给我们带来怎样更加优雅的写法呢?

Stream的更优写法

下面来看一下借助Java 8的Stream API,修改后的代码:

public class Demo03StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(System.out::println);
    }
}

直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。我们真 正要做的事情内容被更好地体现在代码中。

6.2 Stream流式思想概述

注意:stream和I0流(lnputstream/0utputstream)没有任何关系,请暂时忘记对传统I0流的固
有印象!

Stream流式思想类似于工厂车间的"生产流水线",Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

Stream不存在数据,只对数据进行加工处理。

6.3 如何获取Stream流对象

  • 通过Collection对象的stream()或parallelStream()方法
  • 通过Arrays类的stream()方法
  • 通过Stream接口的of()、iterate()、generate()方法
  • 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法

代码示例:

public class Test02 {
    public static void main(String[] args) {
        // 通过集合对象调用Stream()获取流
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王二");
        list.add("码字");
        Stream<String> stream = list.stream();
        stream.forEach(item-> System.out.println(item));


        // 通过Arrays数组工具获取Stream对象
        int[] arr = {3, 4, 34, 45, 23, 33, 12};
        IntStream stream1 = Arrays.stream(arr);
        stream1.forEach(item -> System.out.println(item)); // 打印每个元素

        // 使用Stream类中of方法
        Stream<String> stream2 = Stream.of("hello", "hi", "world");
        stream2.forEach(item -> System.out.println(item)); // 打印每个元素

        // LongStream
        LongStream range = LongStream.range(1, 10);
        range.forEach(item-> System.out.println(item));


        // 上面都是串行流,还可以获取并行流

        Stream<String> stringStream = list.parallelStream();
        stringStream.forEach(item-> System.out.println(item));

    }
}

7. Stream流中常见的Api

中间操作Api:一个操作的中间链,对数据源的数据进行操作。而这种操作的返回类型还是一个Stream对象。
终止操作Api: 一个终止操作,执行中间操作链,并产生结果,返回类型不在是Stream流对象。

举个简单的例子:

假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。

@Data
class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));

在JDK8以前,我们可以通过遍历列表来完成。但是在有了Stream API后,可以这样来实现:

public static void main(String[] args) {
​
    // 1)找到年龄大于18岁的人并输出;
    personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);
​
    System.out.println("-------------------------------------------");
​
    // 2)找出所有中国人的数量
    long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
    System.out.println("中国人有:" + chinaPersonNum + "个");
}

在这个例子中,personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。

7.1 Stream中间操作--筛选与切片

  • filter:接收Lambda,从流中排除某些操作;

  • limit:截断流,使其元素不超过给定对象

  • skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补

  • distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。

7.1.1 limit举例

需求,从Person列表中取出两个女性。

personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);

输出结果为:

Person(name=欧阳雪, age=18, country=中国, sex=F)
Person(name=Harley, age=22, country=英国, sex=F)

7.1.2 skip举例

从Person列表中从第2个女性开始,取出所有的女性。

personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);

输出结果为:

Person(name=Harley, age=22, country=英国, sex=F)
Person(name=小梅, age=20, country=中国, sex=F)
Person(name=何雪, age=21, country=中国, sex=F)

7.1.3 distinct举例

从Person列表中从取出所有的男性,并去除重复

personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);

输出结果为:

Person(name=Tom, age=24, country=美国, sex=M)
Person(name=向天笑, age=20, country=中国, sex=M)
Person(name=李康, age=22, country=中国, sex=M)

男性中有两个李康,去除掉了一个重复的。

7.2 Stream中间操作--映射

  • map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

  • flatMap--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

7.2.1 map举例

例1:比如,我们用一个PersonCountry类来接收所有的国家信息:

@Data
class PersonCountry {
    private String country;
}
​
​
personList.stream().map((p) -> {
       PersonCountry personName = new PersonCountry();
       personName.setCountry(p.getCountry());
       return personName;
}).distinct().forEach(System.out::println);

例2:假如有一个字符列表,需要提出每一个字符

List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");

代码如下:

根据字符串获取字符方法:

public static Stream<Character> getCharacterByString(String str) {
​
    List<Character> characterList = new ArrayList<>();
​
    for (Character character : str.toCharArray()) {
        characterList.add(character);
    }
​
    return characterList.stream();
}
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
final Stream<Stream<Character>> streamStream
         = list.stream().map(TestStreamAPI::getCharacterByString);
streamStream.forEach(System.out::println);

运行结果:

java.util.stream.ReferencePipeline$Head@3f91beef
java.util.stream.ReferencePipeline$Head@1a6c5a9e
java.util.stream.ReferencePipeline$Head@37bba400
java.util.stream.ReferencePipeline$Head@179d3b25
java.util.stream.ReferencePipeline$Head@254989ff

从输出结果及返回结果类型(Stream<Stream<Character>>)可以看出这是一个流中流,要想打印出我们想要的结果,需要对流中的每个流进行打印:

streamStream.forEach(sm -> sm.forEach(System.out::print));

运行结果为:

aaabbbcccdddddd

但我们希望的是返回的是一个流,而不是一个包含了多个流的流,而flatMap可以帮助我们做到这一点。

7.2.2 flatMap举例

改写上面的方法,将map改成flatMap:

1 final Stream<Character> characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
2 characterStream.forEach(System.out::print);

7.2.3 map和flatMap的图解

map图解:

map在接收到流后,直接将Stream放入到一个Stream中,最终整体返回一个包含了多个Stream的Stream。

flatMap图解:

flatMap在接收到Stream后,会将接收到的Stream中的每个元素取出来放入一个Stream中,最后将一个包含多个元素的Stream返回。

7.3 Stream中间操作--排序

  • sorted()--自然排序(Comparable)

  • sorted(Comparator com)--定制排序(Comparator)

自然排序比较好理解,这里只讲一下定制排序,对前面的personList按年龄从小到大排序,年龄相同,则再按姓名排序:

final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {
​
    if (p1.getAge().equals(p2.getAge())) {
        return p1.getName().compareTo(p2.getName());
    } else {
        return p1.getAge().compareTo(p2.getAge());
    }
});
sorted.forEach(System.out::println);

7.4 终止操作--查找与匹配

  • allMatch--检查是否匹配所有元素

  • anyMatch--检查是否至少匹配一个元素

  • noneMatch--检查是否没有匹配所有元素

  • findFirst--返回第一个元素

  • findAny--返回当前流中的任意元素

  • count--返回流中元素的总个数

  • max--返回流中最大值

  • min--返回流中最小值

7.4.1 allMatch

判断personList中的人是否都是成年人:

 final boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
 System.out.println("是否都是成年人:" + adult);
 
 final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
 System.out.println("是否都是中国人:" + chinaese);

7.4.2 max min

 final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
 System.out.println("年龄最大的人信息:" + maxAge.get());
 
 final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
 System.out.println("年龄最小的人信息:" + minAge.get());

7.5 归约

Stream API的归约操作可以将流中元素反复结合起来,得到一个值,有:

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

7.5.1 求一个1到100的和

List<Integer> integerList = new ArrayList<>(100);
for(int i = 1;i <= 100;i++) {
    integerList.add(i);
}
final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
System.out.println("结果为:" + reduce);

这个例子用到了reduce第二个方法:T reduce(T identity, BinaryOperator<T> accumulator)

把这个动作拆解一下,其运算步骤模拟如下:

0  (1,2) -> 1 + 2 + 0     
3  (3,4) -> 3 + 4 + 3
10 (5,6) -> 5 + 6 + 10
.
.
.

其运算步骤是,每次将列表的两个元素相加,并将结果与前一次的两个元素的相加结果进行累加,因此,在开始时,将identity设为0,因为第1个元素和第2个元素在相加的时候,前面还没有元素操作过。

7.5.2 求所有人的年龄之和

final Optional<Integer> reduce = personList.stream().map(Person::getAge).reduce(Integer::sum); System.out.println("年龄总和:" + reduce);

7.6 收集

collect:将流转换为其他形式,接收一个Collector接口实现 ,用于给Stream中汇总的方法

<R, A> R collect(Collector<? super T, A, R> collector);
​
​
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);

collect不光可以将流转换成其他集合等形式,还可以进行归约等操作,具体实现也很简单,主要是与Collectors类搭配使用。

7.6.1 改写7.2.1 map举例

将国家收集起来转换成List

final List<String> collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
        System.out.println(collect);

7.6.2 计算出平均年龄

 final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
 System.out.println("平均年龄为:" + collect1);

7.6.3 找出最小年龄、最大年龄

 final Optional<Integer> maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
 System.out.println(maxAge2.get());

最小年龄类型。还有其他很操作,可以参考java.util.stream.Collectors。

8. 完整代码

public class TestStreamAPI {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",20,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("小梅",20,"中国",'F'));
        personList.add(new Person("何雪",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));

        // 1)找到年龄大于18岁的人并输出;
        personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);

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

        // 2)找出所有中国人的数量
        long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
        System.out.println("中国人有:" + chinaPersonNum);

        // limit
        personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
        System.out.println();
        // skip
        personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);

        // distinct
        personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);

        // map
        personList.stream().map((p) -> {
            PersonCountry personName = new PersonCountry();
            personName.setCountry(p.getCountry());
            return personName;
        }).distinct().forEach(System.out::println);

        // map2
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");

        final Stream<Stream<Character>> streamStream
                = list.stream().map(TestStreamAPI::getCharacterByString);
//        streamStream.forEach(System.out::println);
        streamStream.forEach(sm -> sm.forEach(System.out::print));

        // flatMap
        final Stream<Character> characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
        characterStream.forEach(System.out::print);

        // sort
        final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {

            if (p1.getAge().equals(p2.getAge())) {
                return p1.getName().compareTo(p2.getName());
            } else {
                return p1.getAge().compareTo(p2.getAge());
            }
        });
        sorted.forEach(System.out::println);

        // allMatch
        final Stream<Person> stream = personList.stream();
        final boolean adult = stream.allMatch(p -> p.getAge() >= 18);
        System.out.println("是否都是成年人:" + adult);

        final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
        System.out.println("是否都是中国人:" + chinaese);

        // max min
        final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
        System.out.println("年龄最大的人信息:" + maxAge.get());

        final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
        System.out.println("年龄最小的人信息:" + minAge.get());

        // reduce
        List<Integer> integerList = new ArrayList<>(100);
        for(int i = 1;i <= 100;i++) {
            integerList.add(i);
        }
        final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
        System.out.println("结果为:" + reduce);

        final Optional<Integer> totalAge = personList.stream().map(Person::getAge).reduce(Integer::sum);
        System.out.println("年龄总和:" + totalAge);

        // collect
        final List<String> collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
        System.out.println(collect);

        final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
        System.out.println("平均年龄为:" + collect1);

        final Optional<Integer> maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
        System.out.println(maxAge2.get());

        try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
            final Optional<Integer> minAge2 = integerStream.collect(Collectors.minBy(Integer::compareTo));
            System.out.println(minAge2.get());
        }
    }

    public static Stream<Character> getCharacterByString(String str) {

        List<Character> characterList = new ArrayList<>();

        for (Character character : str.toCharArray()) {
            characterList.add(character);
        }

        return characterList.stream();
    }
}

@Data
class PersonCountry {
    private String country;
}

@Data
class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}

JDK新特新就告一段落了!

标签:Stream,stream,System,特性,personList,JDK8,println,out
From: https://blog.csdn.net/As_Yua/article/details/140225944

相关文章

  • HTTP协议30 丨 2特性概览
    刚结束的“安全篇”里的HTTPS,通过引入SSL/TLS在安全上达到了“极致”,但在性能提升方面却是乏善可陈,只优化了握手加密的环节,对于整体的数据传输没有提出更好的改进方案,还只能依赖于“长连接”这种“落后”的技术(参见第17讲)。所以,在HTTPS逐渐成熟之后,HTTP就向着性能方面......
  • HTTP协议27丨更好更快的握手:TLS1.3特性解析
    上一讲中我讲了TLS1.2的握手过程,你是不是已经完全掌握了呢?不过TLS1.2已经是10年前(2008年)的“老”协议了,虽然历经考验,但毕竟“岁月不饶人”,在安全、性能等方面已经跟不上如今的互联网了。于是经过四年、近30个草案的反复打磨,TLS1.3终于在去年(2018年)“粉墨登场”,再......
  • Java [ 基础 ] Java 8以上新特性 ✨
    ✨探索Java基础Java8以上新特性✨Java8及以上的新特性Java8引入了一些重大更新和新特性,这些特性极大地增强了Java的功能和性能。随着Java9、10、11、12及以后的版本发布,Java持续引入更多的改进和新功能。本文将介绍Java8及以上版本的一些关键新特性。Java8新特......
  • ts 常用api和特性
    交叉类型将多个类型进行合并成一个类型typePerson={name:string;age:number;}typeEmployee=Person&{employeeId:number;}联合类型联合类型通过|符号连接多个类型从而生成新的类型。它主要是取多个类型的交集,即多个类型共有的类型才是联合类型最终的类型t......
  • 掌握Java 8新特性:Lambda表达式与Stream API详解
    ......
  • 【C++干货基地】C++模板深度解析:进阶技巧与高级特性掌握(按需实例化、全特化与偏特化)文
    ......
  • 【JDK8】新特性(一)
    1.简介JDK8是官方发布的一个大版本,提供了很多新特性功能给开发者使用,包含语言、编译器、库、工具和JVM等方面的十多个新特性。本文将介绍编码过程中常用的一些新特性. JDK8帮助文档2.lambda表达式概念:语法:注意:    函数式接口:接口中只有一个抽象方法。......
  • GaussDB AI新特性:gs_index_advise推荐索引
    GaussDB的AI新特性,可以把SQL文本嵌套在函数中,数据库会返回一个创建合适索引的列gs_index_advise(text)描述:针对单条查询语句推荐索引。参数:SQL语句字符串返回值类型:record  一、通常的SQL优化会通过参数调优的方式进行调整,例如如下参数setenable_fast_query_shippi......
  • 抬头显示器HUD原理及特性
    HUD基本原理  抬头数字显示仪(HeadUpDisplay),又叫平视显示系统,它的作用,就是把时速、导航等重要的行车信息,投影到驾驶员前风挡玻璃上,让驾驶员尽量做到不低头、不转头就能看行车信息。 HUD成像为离轴三反的过程,该过程中,PGU中的像被不断放大反射,最终形成虚像,成像大小......
  • CentOS安装部署JDK8
    1.下载jdk8(1)、官网下载:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html2)百度网盘:链接:https://pan.baidu.com/s/1dTwKC65nDWBjmckWc-9igg?pwd=paxl提取码:paxl2、开始部署(1)解压jdk包,并将解压后的包移至想要存放的目录下,我放在/usr/loc......