Java 8到17新特性完全指南:告别臃肿代码,拥抱高效开发

一、Java版本变迁速览

  • Java 1.0 (1996.01.23): Sun 公司发布第一个开发工具包。
  • Java 5.0 (2004.09.30): 版本号直接更新至 5.0,平台更名为 Java SE、Java EE、Java ME。
  • Java 8.0 (2014.03.18): 变化最大,长期支持版本(LTS)。
  • Java 9.0 (2017.09.22): 开始每半年更新一次。
  • Java 10.0 (2018.03.21)
  • Java 11.0 (2018.09.25): 取消独立 JRE 安装包,长期支持版本(LTS)。
  • Java 12.0 (2019.03.19)
  • Java 17.0 (2021.09): 版本号称为 21.9,长期支持版本(LTS)。
  • Java 19.0 (2022.09): 版本号称为 22.9。

从 Java 9 开始,Java 的发布周期为 6 个月,更新由特性驱动转变为时间驱动,承诺不再延迟。此举使得关键特性能快速合并到 JDK,开发团队能更早获得反馈。Oracle 计划每三年发布一个长期支持版本(LTS)。

📌LTS小贴士:
LTS(Long-term Support)(长期支持)版本在新版本发布后仍会获得支持和修复,如 JDK 19。选择 LTS 版本可以确保在出现漏洞时获得修复。

学习新特性的角度
语法层面:

  • JDK 5: 自动拆箱、装箱、枚举、泛型。
  • JDK 8: Lambda 表达式、接口默认方法、静态方法。
  • JDK 10: 局部变量类型推断。
  • JDK 12: 新的 switch 语法。
  • JDK 13: 文本块。

API 层面:

  • JDK 8: Stream、Optional、新的日期时间 API、HashMap 底层结构。
  • JDK 9: String 的底层结构。
  • 新增或过时的 API。

底层优化:

  • JDK 8: 永久代被元空间替代、新的 JS 执行引擎。
  • 新的垃圾回收器、GC 参数、JVM 优化。

二、改变编码方式的三大神器

1.函数式编程思想概述

函数式编程 (Functional Programming):在数学中,函数是输入和输出的计算方案,即“用数据进行操作”。与面向对象强调“通过对象做事情”不同,函数式编程关注“做什么,而非如何做”。学习 Lambda 表达式就是这一思想的体现。

1. Lambda表达式-Java8新特性

1.1 启动线程实例
- 方式1:普通方式
- 方式2:匿名内部类

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("多线程方式启动");
    }
}).start();
  • 方式3:使用Lambda表达式
new Thread(()->{
    System.out.println("多线程方式启动");
}).start();

new Thread(()->System.out.println("多线程方式启动")).start();

1.2 Lambda表达式的标准格式
Lambda 表达式在 Java 8 中引入,使用“->”作为操作符,分为两部分:

  • 左侧: 参数列表
  • 右侧: Lambda 体,实现逻辑

组成要素:形式参数、箭头、代码块。

1.3 使用示例

  • 语法格式一:无参、无返回值
//匿名内部类
useEatable(new Eatable() {
    @Override
    public void eat() {
        System.out.println("一天一苹果,医生远离我!");
    }
});

//Lambda表达式
useEatable(()->{
    System.out.println("一天一苹果,医生远离我!");
});
  • 语法格式二:一个参数,但无返回值
useFlyable(new Flyable() {
    @Override
    public void fly(String s) {
        System.out.println(s);
        System.out.println("飞机自驾游!");
    }
});

useFlyable((String s)->{
    System.out.println(s);
    System.out.println("飞机自驾游!");
});

1.3 Lambda表达式的省略模式
省略规则:

  • 类型推断: 参数类型可省略,编译器会推断。
useAddable((x, y)->{});
  • 单参数: 若仅有一个参数,小括号可省略。
useFlyable(s->{});
  • 单行代码: 语句只有一条时,可以省略大括号和分号,甚至是return。
useFlyable(s->System.out.println("飞机自驾游!"));
useAddable((x, y)->x + y);

1.4 注意事项

  • Lambda 必须在接口中定义且只能有一个抽象方法
  • 需要上下文环境推导出对应接口。
Runnable r = () -> System.out.println("Lambda表达式");
new Thread(() -> System.out.println("Lambda表达式")).start();

1.5 Lambda表达式和匿名内部类的区别
1. 所需类型:

  • 匿名内部类:接口、抽象类或具体类。
  • Lambda 表达式:只能是接口。
    2. 使用限制:
  • 单抽象方法接口可用 Lambda 和匿名内部类。
  • 多于一个抽象方法的接口只能用匿名内部类。
    3. 实现原理:
  • 匿名内部类:编译后生成单独的 .class 文件。
  • Lambda 表达式:运行时动态生成字节码,无单独 .class 文件。

2. 函数式(Functional)接口

2.1 什么是函数式接口?

  • 定义: 函数式接口是仅包含一个抽象方法(Single Abstract Method, SAM)的接口,可以包含其他非抽象方法。
  • 使用: 通过 Lambda 表达式创建该接口的对象。如果 Lambda 表达式抛出受检异常,该异常需在接口的抽象方法中声明。
  • 注解: 使用 @FunctionalInterface 注解可以验证接口是否为函数式接口,并在 javadoc 中说明。
  • Java 8: java.util.function 包下定义了多种函数式接口。
    简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。只有一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。

2.2 函数式接口作为方法的参数

为了将 Lambda 表达式作为参数传递,接收参数的类型必须是兼容的函数式接口。

public class RunnableDemo {
    public static void main(String[] args) {
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"启动了");
            }
        });

        startThread(()->  System.out.println(Thread.currentThread().getName()+"启动了"));
    }

    private static void startThread(Runnable r){
        new Thread(r).start();
        System.out.println(Thread.currentThread().getName()+"启动了");
    }
}

2.3 函数式接口作为方法的返回值

需求: 定义一个类(ComparatorDemo),在类中提供两个方法

  • Comparator getComparator() 返回一个函数式接口。
  • 主方法调用 getComparator()。
    如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
public class ComparatorDemo {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("a");
        al.add("dddddd");
        al.add("bbb");
        al.add("cc");
        
        System.out.println("排序前:"+al);
        Collections.sort(al, getComparator());
        System.out.println("排序后:"+al);
    }

    private static Comparator<String> getComparator(){
      /*  Comparator<String> com = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        };
        return com;*/

        //return new Comparator<String>() {
        //    @Override
        //    public int compare(String o1, String o2) {
        //        return o1.length()-o2.length();
        //    }
        //};

        return (String s1, String s2)->{
            return s1.length() - s2.length();
        };
    }
}

2.4 常用的函数式接口

Java 8 在 java.util.function 包中预定义了多种函数式接口。以下是一些常见的函数式接口:

java.lang.Runnable: void run()
java.lang.Iterable: Iterator<T> iterator()
java.lang.Comparable: int compareTo(T t)
java.util.Comparator: int compare(T t1, T t2)

重点学习以下四大核心函数式接口:

接口参数类型用途
ConsumerT对类型为 T 的对象执行操作,包含方法 void accept(T t)
Supplier生成类型为 T 的对象,包含方法 T get()
Function<T, R>T对类型为 T 的对象进行操作并返回 R 类型的结果,包含方法 R apply(T t)
PredicateT判断类型为 T 的对象是否满足某个条件,返回 boolean 值,包含方法 boolean test(T t)
2.4.1 Supplier接口

Supplier 接口被称为生产型接口,提供一个无参的 get() 方法,返回指定类型的数据。

  • Supplier:包含一个无参的方法
  • T get():获得结果,该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
public class SupplierDemo {
    public static void main(String[] args) {
        String s = getString(() -> "林青霞");
        Integer i = getInteger(() -> 15);
        System.out.println(s + ", " + i);
    }

    private static String getString(Supplier<String> sup) {
        return sup.get();
    }

    private static Integer getInteger(Supplier<Integer> sup) {
        return sup.get();
    }
}

练习
定义 SupplierTest 类,提供 getMax(Supplier sup) 方法,返回 int 数组中的最大值。

public class SupplierTest {
    public static void main(String[] args) {
        int []arr = {19, 50, 28, 37, 46};

        int maxValue = getMax(()->{
            int max = arr[0];
            for (int i = 1; i < arr.length; ++i){
                max = arr[i] > max ? arr[i] : max;
            }
            return max;
        });
        System.out.println(maxValue);
    }

    private static int getMax(Supplier<Integer> sup){
        return sup.get();
    }
}
2.4.2 Consumer消费型接口

Consumer 接口用于消费数据,包含:

  • void accept(T t): 对参数执行操作。
  • default Consumer andThen(Consumer<? super T> after): 组合两个 Consumer。

举例

  • 在 JDK1.8 中 Collection 集合接口的父接口 Iterable 接口中增加了一个默认方法:
    public default void forEach(Consumer<? super T> action)
    遍历 Collection集合的每个元素,执行“xxx 消费型”操作。
  • 在 JDK1.8 中 Map 集合接口中增加了一个默认方法:
    public default void forEach(BiConsumer<? super K, ? super V> action)
    遍历 Map 集合的每对映射关系,执行“xxx 消费型”操作。
public class ConsumerDemo {
    public static void main(String[] args) {
        operatorString("林青霞", (String s)->{
            System.out.println(s);
        });
        operatorString("林青霞", s->System.out.println(s));
        operatorString("林青霞", System.out::println);

        System.out.println("================");
        operatorString("林青霞", s->System.out.println(s),
             s->System.out.println(new StringBuilder(s).reverse().toString()));
    }
    
    //消费一个字符串数据
    private static void operatorString(String name, Consumer<String> con){
        con.accept(name);
    }

    //用不同的方式消费一个字符串数据两次
    private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2){
        //con1.accept(name);
        //con2.accept(name);
        con1.andThen(con2).accept(name);
    }

练习
String[] strArray= {“林青霞,30”, “张曼玉,35”, “王祖贤,33”},字符串数组中有多条信息,请按照格式:“姓名: XX年龄: XX "的格式将信息打印出来
要求:

  • 把打印姓名的动作作为第一个Consumer接 的Lambda实例
  • 把打印年龄的动作作为第二个Consumer接口的Lambda实例
  • 将两个Consumer接[按照顺序组合到一起使用
public class ConsumerTest {
    public static void main(String[] args) {
        String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};

        printInfo(strArray, (String str)-> {
            String name = str.split(",")[0];
            System.out.print("姓名," + name);
         },(String str)-> {
            int age = Integer.parseInt(str.split(",")[1]);
            System.out.println("  年龄," + age);
        });
        System.out.println("======");

        printInfo(strArray, str->System.out.print("姓名," + str.split(",")[0]),
               str->System.out.println("  年龄," + Integer.parseInt(str.split(",")[1])));
    }

    private static void printInfo(String[] strArray, Consumer<String>con1, Consumer<String>con2){
        for (String str: strArray){
            con1.andThen(con2).accept(str);
        }
    }
}
2.4.3 Predicate接口

Predicate 用于判断条件,包含:

  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现), 返回一个布尔值
  • default Predicate negate():返回一个逻辑的否定,对应逻辑非
  • default Predicate and(Predicate other):返回一个组合判断,对应短路与
  • default Predicate or(Predicate other):返回一个组合判断,对应

JDK1.8 时,Collecton 接口增加了一下方法,其中一个如下:

  • public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足 filter 指定的条件判断的。
public class PredicateDemo01 {
    public static void main(String[] args) {
        boolean b1 = checkString("hello", s->s.length()>8);
        System.out.println(b1);
        boolean b2 = checkString("helloWorld", s->s.length()>8);
        System.out.println(b2);
        boolean b3 = checkString("helloworld", s->s.length()<8, s->s.length()>15);
        System.out.println(b3);
    }

    private static boolean checkString(String s, Predicate<String> pre){
        //return pre.test(s);
        //return !pre.test(s);
        return pre.negate().test(s);
    }

    private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
        //boolean b1 = pre1.test(s);
        //boolean b2 = pre2.test(s);
        //return b1 && b2;
        //return pre1.and(pre2).test(s);//逻辑与判断
        return pre1.or(pre2).test(s);
    }

练习
筛选姓名长度大于2且年龄大于33的字符串。

public static void main(String[] args) {
    String[] IstrArray= {"林青霞,30","柳岩,34", "张曼玉,35", "貂蝉,31","王祖贤,33"};

    ArrayList<String> str = filter(IstrArray, s->s.split(",")[0].length()>2,
            s->Integer.parseInt(s.split(",")[1])>33);
    for (String s: str){
        System.out.println(s.toString());
    }
}

private static ArrayList<String> filter(String[] al, Predicate<String> pre1, Predicate<String> pre2){
    ArrayList<String> s = new ArrayList<String>();
    for (String str : al){
        if (pre1.and(pre2).test(str)){
            s.add(str);
        }
    }
    return s;
}
2.4.4 Function接口

Function<T, R> 接口用于处理参数并返回新值,包含:

  • R apply(T t): 处理输入并返回结果。
  • default Function<T, V> andThen(Function<? super R, ? extends V> after): 返回组合函数。

在 JDK1.8 时 Map 接口增加了很多方法,例如:

  • public default void replaceAll(BiFunction<? super K,? super V,? extends V> function) 按照 function 指定的操作替换 map 中的 value。
  • public default void forEach(BiConsumer<? super K,? super V> action) 遍历 Map 集合的每对映射关系,执行“xxx 消费型”操作。
package function;

import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        convert("100", (String s)->{
            return Integer.parseInt(s);
        });
        convert("100", s->Integer.parseInt(s));
        convert("100", Integer::new);

        convert(100, i->String.valueOf(99+i));

        convert("100", s->Integer.parseInt(s), i->String.valueOf(i+199));
    }

    //定义一个方法,把一个字符串转换int类型,在控制台输出
    private static void convert(String s, Function<String, Integer> fun){
        int i = fun.apply(s);
        System.out.println(i);
    }

    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(int i, Function<Integer, String> fun){
        String s = fun.apply(i);
        System.out.println(s);
    }
    //定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2){
       //Integer i = fun1.apply(s);
       //String ss = fun2.apply(i);
       // System.out.println(ss);

        String ss = fun1.andThen(fun2).apply(s);
        System.out.println(ss);
    }
}

练习
通过 Function 接口实现函数拼接,将字符串处理为年龄并加70。

package function;

import java.util.function.Function;

public class FucntionTest {
    public static void main(String[] args) {
        String str = "林青霞,30";
        convert(str, s->s.split(",")[1], s->Integer.parseInt(s), i->70+i);
    }

    private static void convert(String s, Function<String, String>fun1, Function<String, Integer>fun2, Function<Integer, Integer> fun3){
        Integer i = fun1.andThen(fun2).andThen(fun3).apply(s);
        System.out.println(i);
    }
}

Stream API(Java8)

🔄 什么是Stream?

  • 数据处理流水线:类似SQL的链式操作,支持过滤/映射等复杂计算
  • 与集合的本质区别
    • 🗃️ 集合 = 静态数据存储(关注数据本身
    • 🚀 Stream = 动态数据计算(关注数据处理

为什么需要Stream?
实际开发中,项目中多数数据源都来自于 MySQL、Oracle 等。但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java 层面去处理。

// 传统方式 vs Stream方式(代码量对比)
List<String> result = new ArrayList<>();
for(String s : list) {
    if(s.startsWith("张") && s.length()==3) {
        result.add(s);
    }
}

list.stream()
   .filter(s -> s.startsWith("张"))
   .filter(s -> s.length() == 3)
   .forEach(System.out::println);

📌 三大特性

  • 无存储:不保存原始数据(仅操作通道)
  • 无污染:原始数据永不修改(返回新Stream)
  • 延迟执行:终结操作触发执行链

🛠️ 操作三步曲

  • 生成流:通过数据源(集合,数组等)生成流 list.stream()
  • 中间操作:一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用 filter()
  • 终结操作:一个流只能有一个终结操作, 当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作,forEach()

🌐 流生成方式

数据源生成方法示例
Collection集合.stream()list.stream()
数组Arrays.stream()Arrays.stream(arr)
散列数据间接转换map.keySet().stream()
直接创建Stream.of()Stream.of(“a”,“b”,“c”)

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以"张"开头的元素存储到一个新的集合
  • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一 步得到的集合
    使用Stream流的方式完成过滤操作
    list.stream().filter(s -> s.startsWith("张 ).filter(s -> s.length() == 3).forEach(System.out:println);
  • 直接阅读代码的字面意思即可完美 展示无关逻辑方式的语义:生成流、过滤姓张、过滤长度为3、逐一打印
  • Stream流把真正的函数式编程风格 入到Java中
package stream;

import java.util.*;
import java.util.stream.Stream;
public class StreamDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        //Map 间接生成流
        HashMap<String, Integer> map = new HashMap<>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        //数组
        String[] strArray = {"hello", "world", "java"};
        IntStream stream = Arrays.stream(strArray);
        Stream<String> strArrayStream = Stream.of(strArray);
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> integerStream = Stream.of(10, 20, 30);
        
        //动态添加流
        Stream.builder<String> streamBuilder = Stream.builder();
        streamBuilder.add("a");
        streamBuilder.add("b");
        Stream<String> stream = streamBuilder.build();
    }
}

常见中间操作方法

筛选与切片

  • Stream filter(Predicate predicate):用于对流中的数据进行过滤
    Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值
  • Stream distinct(): 返回由该流的不同元素(根据Object.equals(Object) )组成的流
  • Stream limit(long maxSize): 返回此流中的元素组成的流,截取前指定参数个数的数据
  • Stream skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流

映射

  • Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
    Function接口中的方法 R apply(Tt)
  • IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果。IntStream:表示原始int流
  • flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

排序

  • Stream sorted():返回由此流的元素组成的流,根据自然顺序排序

  • Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序

  • static Stream concat(Stream a, Stream b):合并a和b两个流为一个流

代码示例:

package stream;

import java.util.ArrayList;

public class StreamDemo01 {
    public static void main(String[] args) {
        //创建一个集合, 存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");
        //需求1:把list集合中以张开头的元素在控制台输出
        list.stream().filter(s->s.startsWith("张")).forEach(System.out::println);
        System.out.println("---------------");
        
        //需求2:把list集合中长度为3的元素在控制台输出
        list.stream().filter(s->s.length()==3).forEach(System.out::println);
        System.out.println("---------------");
        
        //需求3:把list集合中以张开头且长度为3的元素在控制台输出
        list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
        System.out.println("==========================");
        
        //需求4:取前3个数据输出
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("---------------");
        
        //需求5:跳过三个元素,剩下输出
        list.stream().skip(3).forEach(System.out::println);
        System.out.println("---------------");
       
        //需求6:跳过2个元素、取前2个数据 输出                     list.stream().skip(2).limit(2).forEach(System.out::println);
        System.out.println("====================");
        //排序list中的顺序并在控制台输出
        list.stream().sorted().forEach(System.out::println);
        System.out.println("====================");
       
        //按照字符串长度 把数据在控制台中输出
        list.stream().sorted((s1, s2)->{
            int num = s1.length() - s2.length();
            int num2 = num == 0 ? s1.compareTo(s2) : num;
            return num2;
        }).forEach(System.out::println);
    }
}

代码示例2:

package stream;

import java.util.ArrayList;

public class StreamDemo02 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("10");
        list.add("20");
        list.add("30");
        list.add("40");
        list.add("50");

        list.stream().map(s->Integer.parseInt(s)).forEach(System.out::println);       list.stream().map(Integer::parseInt).forEach(System.out::println);      list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);

        //返回 此流中的 元素的总和
        int result = list.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(result);
    }
}

常见终结操作方法

  • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
  • 流进行了终止操作后,流就被消费,不能再次使用。

匹配与查找

  • allMatch(Predicate p) 检查是否匹配所有元素
  • anyMatch(Predicate p) 检查是否至少匹配一个元素
  • noneMatch(Predicate p) 检查是否没有匹配所有元素
  • findFirst() 返回第一个元素
  • findAny() 返回当前流中的任意元素
  • count() 返回流中元素总数
  • max(Comparator c) 返回流中最大值
  • min(Comparator c) 返回流中最小值
  • forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)

归约
reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。

收集

  • collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
    Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
    另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下:
    工具类Collectors提供了具体的收集方式
  • public static Collector toList():把元素收集到List集合中
  • public static Collector toSet():把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper, Function valueMapper):把元素收集到Map集合中
  • toCollection Collector<T, ?, C> 把流中元素收集到创建的集合
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
  • counting Collector<T, ?, Long> 计算流中元素的个数
    long count = list.stream().collect(Collectors.counting());
  • summingInt Collector<T, ?, Integer> 对流中元素的整数属性求和
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
  • averagingInt Collector<T, ?, Double> 计算流中元素 Integer 属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
  • summarizingInt Collector<T, ?, IntSummaryStatistics> 收集流中 Integer 属性的统计值。如:平均值
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
  • joining Collector<CharSequence, ?, String> 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
  • maxBy Collector<T, ?, Optional> 根据比较器选择最大值
    Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
  • minBy Collector<T, ?, Optional> 根据比较器选择最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
  • reducing Collector<T, ?, Optional> 从一个作为累加器的初始值开始,利用BinaryOperator 与流中元素逐个结合,从而归约成单个值
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
  • collectingAndThen Collector<T,A,RR> 包裹另一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
  • groupingBy Collector<T, ?, Map<K, List>> 根据某属性值对流分组,属性为K,结果为 V
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
  • partitioningBy Collector<T, ?, Map<Boolean, List>> 根据 true 或 false 进行分区
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));

Stream<String> listStream =  list.stream().filter(s->s.startsWith("张"));
List<String> names =  listStream.collect(Collectors.toList());

练习
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray2 = {“林青霞,30”, “张曼玉,35”,“王祖贤,33”, “柳岩,25”};

//得到字符串中年龄数据大于28的流
Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);

//需求:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
Map<String, Integer> map2 = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0],s->Integer.parseInt(s.split(",")[1]);
Set<String> keySet = map.keySet();
for (String key : keySet) {
    Integer value = map.get(key);
    System.out.println(key + "," + value);
}

Java9新增API

1. Stream 实例化方法

ofNullable() 方法
在 Java 8 中,使用 Stream.of(null) 会引发空指针异常。而在 Java 9 中,ofNullable() 方法允许创建一个单元素的 Stream,可以包含非空元素或创建一个空 Stream。

// Java 8 可能报 NullPointerException
// Stream<Object> stream1 = Stream.of(null);
// System.out.println(stream1.count()); // 报错

// Java 9 允许 null 值
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count()); // 3

List<String> list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count()); // 2

// ofNullable() 允许值为 null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count()); // 0

Stream<String> stream = Stream.ofNullable("hello world");
System.out.println(stream.count()); // 1
iterator() 方法重载

在 Java 9 中,Stream.iterate() 进行了重载,允许指定终止条件。

复制
// 原来的控制终止方式
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println);

// 现在的终止方式
Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值