JAVA 8 新特性学习笔记

JAVA 8 新特性学习笔记

Lambda 表达式

一、Lambda表达式简介

什么是Lambda?

Lambda是JAVA 8添加的新特性,说白了,Lambda是一个匿名函数

为什么使用Lambda

使用Lambda表达式可以对一个接口的方法进行非常简洁的实现

Lambda对接口的要求

虽然可以使用Lambda表达式对某些接口进行简单的实现,但是并不是所有的接口都可以用Lambda表达式来实现,要求接口中定义的必须要实现的抽象方法只能是一个

Lambda 表达式需要“函数式接口”支持

函数式接口:只有一个抽象方法的接口,成为函数式接口。可以使用注解 @FunctionalInterface来进行修饰,注解的作用就是检查接口是否为 函数式接口

在JAVA8中 ,对接口加了一个新特性:default
可以使用default对接口方法进行修饰,被修饰的方法在接口中可以默认实现
@FunctionalInterface

修饰函数式接口的,接口中的抽象方法只有一个

二、Lambda的基础语法

Lambda 表达式基础语法:JAVA 8引入了一个新的操作符“->”,成为箭头操作符或Lambda 操作符
箭头操作符将Lambda 表达式拆分为两部分:
左侧是:Lambda 表达式的参数列表
右侧是:Lambda 表达式中所需要执行的功能,即 Lambda 体

语法
/**
 * 语法格式一: 无参数 无返回值
 *          () -> System.out.println("Hello Lambda!");
 */
@Test
public void test1() {
    // JDK 1.8 之前  全局参数传入局部内部类(匿名内部类)时需要用 final 修饰,JDK 1.8 之后 会默认用 final 修饰,可以省略 
    int num = 0;

    // 原来的用法
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello Lambda!" + num);
        }
    };
    r.run();
    System.out.println("----------------------------------");
    // Lambda 表达式用法
    Runnable r1 = () -> System.out.println("Hello Lambda!" + num);
    r1.run();
}

/**
 * 语法格式二:有一个参数,无返回值
 *         (x) -> System.out.println(x); 
 */
@Test
public void test2() {
    Consumer<String> con = (x) -> System.out.println(x);
    con.accept("Hello Lambda!");

    Consumer<String> con1 = System.out::println;
    con1.accept("Hello Lambda!!!");
}

/**
 * 语法格式三:若只有一个参数,小括号可以省略
 *          x -> System.out.println(x);
 */
@Test
public void test3() {
    Consumer<String> con = x -> System.out.println(x);
    con.accept("Hello Lambda!");
}

/**
 * 语法格式四:有两个或两个以上的参数,并且 Lambda 体中有多条语句
 *          (x, y) - {
 *              System.out.println(x+"->"+y)
 *              return Integer.compare(x, y);
 *          }
 */
@Test
public void test4() {
    Comparator<Integer> com = (x, y) -> {
        System.out.println(x + y);
        return Integer.compare(x, y);
    };
}

/**
 * 语法格式五:有两个或两个以上的参数,并且 Lambda 体中只有一条语句,则 return 和大括号 {} 都可以省略
 *          (x, y) -> Integer.compare(x, y);
 *          Integer::compare;
 */
@Test
public void test5() {
    // 第一种格式
    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    // 第二种格式
    Comparator<Integer> com1 = Integer::compare;
}

/**
 * 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器会根据上下文推断出,即“类型推断”
 *			(x, y) -> Integer.compare(x, y);
 *			(Integer x, Integer y) -> Integer.compare(x, y);
 */

总结

上联:左右遇一括号省 下联:左侧推断类型省 横批:能省则省

三、Java8 四大核心内置函数式接口

这四个内置接口学会了,Lambda大部分方法就可以了!!!

接口方法描述
Consumervoid accept(T t)消费型接口
SupplierT get()供给型接口
Function<T, R>R apply(T t)函数型接口
Predicateboolean test(T t)断言型接口
Consumer : 消费型接口

用于消费一些服务,实例如下

// Consumer<T> : 消费型接口       
@Test
public void test() {
    happy(10000, x -> System.out.println("今天小明消费1000元"));
}

public void happy(double money, Consumer<Double> consumer) {
    consumer.accept(money);
}
Supplier : 供给型接口

用于提供服务,实例如下

// Supplier<T> : 供给型接口   
@Test
public void test() {
    List<Integer> purchase = Purchase(10, () -> (int) (Math.random() * 100));
    purchase.forEach(System.out::println);
}

// 需求:产生指定个数的整数,并放入接口中
public List<Integer> Purchase(int num, Supplier<Integer> supplier) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < num; i++) {
        list.add(supplier.get());
    }
    return list;
}
Function<T, R> : 函数型接口

用于处理数据,返回一些相同或不同的数据类型,实例如下

// Function<T, R> : 函数型接口 
@Test
public void test3() {
    System.out.println(fun("biandekaixin", k -> k.toUpperCase(Locale.ROOT).substring(2, 4)));

    System.out.println(fun1(new Employee(1, "张三", 18, 6000), Employee::getName));
}

public String fun(String str, Function<String, String> function) {
    return function.apply(str);
}

public String fun1(Employee employee, Function<Employee, String> function) {
    return function.apply(employee);
}
Predicate : 断言型接口

用于处理满足条件的数据,如筛选长度大于3的元素置于一个新数组中,实例如下:

// Predicate<T> : 断言型接口
@Test
public void test4() {
    List<String> list = new ArrayList<>();
    list.add("www");
    list.add("Hello");
    list.add("Lambda");
    list.add("ok");
    list.add("cccc");
    List<String> str = filterStr(list, k -> k.length() > 3);
    str.forEach(System.out::println);
}

// 需求: 将满足需求的字符串,放入集合中
public List<String> filterStr(List<String> strs, Predicate<String> predicate) {
    List<String> list = new ArrayList<>();
    for (String str : strs) {
        if (predicate.test(str)) {
            list.add(str);
        }
    }
    return list;
}
内置四大核心函数式接口的扩展子接口
接口方法描述
BiFunction <T, U, R>R apply (T t, U u)对传入参数进行操作,返回R类型处理结果
UnaryOperator T apply (T t)对参数进行一元运算操作,返回类型为T的结果
BinaryOperator T apply (T t1, T t2)对参数进行二元运算操作,返回T类型的结果
BiConsumer <T, U>void accept (T t, U u)对参数t,u进行操作,不返回结果
ToIntFunction int applyAsInt (T t)将传入参数强转成int类型返回
ToLongFunction long applyAsLong (T t)将传入参数强转成long类型返回
ToDoubleFunction double applyAsDouble (T t)将传入参数强成double类型返回
IntFunction R apply(int param)将一个int类型经过处理后强转成R类型返回
LongFunction R apply(long param)将一个long类型经过处理后强转成R类型返回
DoubleFunction R apply(double param)将一个double参数经过处理后强转成R类型返回

四、方法引用

方法引用:若 Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”
 				(可以理解为方法引用是 Lambda 表达式的另外一种表现形式)

 主要有三种语法格式:
 
		 对象名::实例方法名

		 类::静态方法名

		 类::实例方法名
		 
注意:
   1、Lambda 体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致.
   2、若 Lambda 参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 ClassName::method.
对象名::实例方法名
// 对象名::实例方法名
@Test
public void test1() {
    PrintStream out = System.out;
    
    Consumer<String> con = (x) -> out.println(x);
   
    Consumer<String> con2 = out::println;
    
    Consumer<String> con3 = System.out::println;
    con3.accept("ccc");
}

@Test
public void test2() {
    Employee emp = new Employee(1, "zs", 18, 2000);
    
    Supplier<String> s = () -> emp.getName();
    System.out.println(s.get());

    Supplier<Integer> s1 = emp::getAge;
    System.out.println(s1.get());
}
类名::静态方法名
// 类::静态方法名
@Test
public void test3() {
    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    
    Comparator<Integer> com1 = Integer::compare;
}
类名::实例方法名
// 类::实例方法名
@Test
public void test4() {
    BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    BiPredicate<String, String> bp1 = String::equals;
}

五、构造器引用

格式:
	ClassName :: new
注意:
	需要调用的构造器的参数列表要与函数式接口中的抽象方法的参数列表保持一致!
ClassName :: new
// 实体类
@Data
@Accessors(chain = true)
public class Employee {
    /**
     * id
     */
    private Integer id;
    /**
     * 的名字
     */
    private String name;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 工资
     */
    private Integer salary;

    public Employee(Integer id, String name, Integer age, Integer salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Employee(Integer id, Integer age) {
        this.id = id;
        this.age = age;
    }

    public Employee(Integer id) {
        this.id = id;
    }

    public Employee() {

    }
}
// 构造器引用
@Test
public void test5() {
    Supplier<Employee> s = () -> new Employee();
    System.out.println(s.get());

    Supplier<Employee> s1 = Employee::new;
    System.out.println(s1.get());
}

@Test
public void test6() {
    Function<Integer, Employee> fun = Employee::new;
    System.out.println(fun.apply(15));

    BiFunction<Integer, Integer, Employee> bf = Employee::new;
    System.out.println(bf.apply(11, 18));
}

六、数组引用

// 数组引用
@Test
public void test7() {
Function<Integer, String[]> fun = (x) -> new String[x];
System.out.println(fun.apply(10).length);

Function<Integer, String[]> fun2 = String[]::new;
System.out.println(fun2.apply(15).length);
}

Stream API

一、Stream API 简介

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  1. 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  2. 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  3. 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

二、Stream 的三个操作步骤

Stream 的三个操作步骤:
 
	1、创建 Stream

	2、中间操作

	3、中止操作(终端操作)
创建 Stream
// 创建 Stream 一共有四种方式
@Test
public void test1() {
    // 通过 Collection 系列集合提供的 Stream()(串行流) 和 parallelStream()(并行流)
    List<String> list = new ArrayList<>();
    // 第一种:串行流
    Stream<String> stream1 = list.stream();
    // 第二种:并行流
    Stream<String> stream2 = list.parallelStream();

    // 通过 Arrays 中的静态方法 stream() 获取数组流
    Employee[] arr = new Employee[]{};
    Stream<Employee> stream3 = Arrays.stream(arr);

    // 通过 Stream 类中的 of() 方法   可以传可变参数  也可以传数组、集合
    Stream<String> stream4 = Stream.of("zs", "ls", "ww");

    // 创建无限流
    // 第一种:迭代   第一个参数是从哪个开始  第二个是函数式接口 Function 的一个字接口
    Stream<Integer> stream5 = Stream.iterate(0, x -> x + 2);
    stream5.limit(10)
        .forEach(System.out::println);
    // 第二种:生产   穿的是函数式接口  消费型接口
    Stream<Double> stream6 = Stream.generate(() -> Math.random());
    stream6.limit(5)
        .forEach(System.out::println);
}
中间操作

注意:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为**“惰性求值”** 。 Stream的中间操作是不会有任何结果数据输出的。

Stream的中间操作在整体上可以分为:筛选与切片、映射、排序。

// 以下操作数据使用的数据源
List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 2500),
            new Employee(2, "李四", 25, 6500),
            new Employee(3, "王五", 38, 6000),
            new Employee(4, "赵六", 45, 8500),
            new Employee(5, "田七", 25, 1500),
    		new Employee(5, "田七", 25, 1500),
            new Employee(5, "田七", 25, 1500)
    );
筛选与切片
方法描述
filter(Predicate p)接收Lambda表达式,从流中排除某些元素
distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补
// filter 过滤
// 内部迭代:迭代操作由 Stream API 完成
@Test
public void test1() {
    // 中间操作 不会执行任何人操作
    Stream<Employee> stream = employees.stream()
        .filter(emp -> emp.getAge() > 35);
    // 中止操作 一次性执行所有操作,成为“惰性求值”
    stream.forEach(System.out::println);
	/**
	 *输出:Employee(id=3, name=王五, age=38, salary=6000)
	 *	   Employee(id=4, name=赵六, age=45, salary=8500)
	 */
}

// 外部迭代
@Test
public void test2() {
    Iterator<Employee> iterator = employees.iterator();

    if (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

// limit 截断流  
// 只要找到我们所需要的数据,比如 limit(2),找到两个数据以后,就不会继续内部迭代了。满足 limit 的个数,就短路,就不执行了
@Test
public void test3() {
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .forEach(System.out::println);
    /** 
     *输出:Employee(id=2, name=李四, age=25, salary=6500)
	 *	   Employee(id=3, name=王五, age=38, salary=6000)
     *     Employee(id=4, name=赵六, age=45, salary=8500)
	 */
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .limit(2)
        .forEach(System.out::println);
    /** 
     *输出:Employee(id=2, name=李四, age=25, salary=6500)
	 *	   Employee(id=3, name=王五, age=38, salary=6000)
	 */
}

// skip 跳过元素
@Test
public void test4() {
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .forEach(System.out::println);
	/** 
     *输出:Employee(id=2, name=李四, age=25, salary=6500)
	 *	   Employee(id=3, name=王五, age=38, salary=6000)
     *     Employee(id=4, name=赵六, age=45, salary=8500)
	 */
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .skip(1)
        .forEach(System.out::println);
    /** 
     *输出:Employee(id=4, name=赵六, age=45, salary=8500)
	 */
}

// distinct 去重
// 通过hashCode()与equals()来筛选
@Test
public void test5() {
    employees.stream()
        .filter(e -> e.getSalary() < 3500)
        .forEach(System.out::println);
    /**
     * 输出:Employee(id=1, name=张三, age=18, salary=2500)
     *      Employee(id=5, name=田七, age=25, salary=1500)
     *      Employee(id=5, name=田七, age=25, salary=1500)
     *      Employee(id=5, name=田七, age=25, salary=1500)
     */
    employees.stream()
        .filter(e -> e.getSalary() < 3500)
        .distinct()
        .forEach(System.out::println);
    /**
     *输出:Employee(id=1, name=张三, age=18, salary=2500)
     *     Employee(id=5, name=田七, age=25, salary=1500)
     */
}
映射
方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
// map 映射流  是将map(Function<T t> fun)中的函数式接口应用到每一个元素上
@Test
public void test6() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    list.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);
    /**
     * 输出:
     *      AA
     *      BB
     *      CC
     *      DD
     */

    employees.stream()
        .map(Employee::getName)
        .distinct()
        .forEach(System.out::println);
    /**
     * 输出:
     *      张三
     *      李四
     *      王五
     *      赵六
     *      田七
     */
}

// mapToDouble(ToDoubleFunction f)  mapToInt(ToIntFunction f)  mapToLong(ToLongFunction f) 这三个就不一一举例了

// flatMap() 将一个流作为参数,将一个流中的每个值转换成另一个流,将所有流连城一个流,map()和flatMap()关系,类似 list 中方法,add() 与 addAll()方法的关系
@Test
public void test7() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    // map()
    list.stream()
        .map(TestStreamAPI::filterCharacter) // 里面存的是这个 {[a,a],[b,b],[c,c],[d,d]}
        .forEach(k -> k.forEach(System.out::println));
    // flatMap()
    list.stream()
        .flatMap(TestStreamAPI::filterCharacter) // 里面村的是这个 {a,a,b,b,c,c,d,d}
        .forEach(System.out::println);

}

public static Stream<Character> filterCharacter(String str) {
    ArrayList<Character> list = new ArrayList<>();
    for (char c : str.toCharArray()) {
        list.add(c);
    }
    return list.stream();
}

// add() 与 addAll() 的区别
@Test
public void test8() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    List list2 = new ArrayList();
    
    list2.add(11);
    list2.add(22);
    list2.add(list);
    list2.forEach(System.out::println);
    /**
     *输出:
     *	11
     *	22
     *	[aa, bb, cc, dd]
     */
    list2.addAll(list);
    list2.forEach(System.out::println);
    /**
     *输出:
     *	11
     *	22
     *	aa
     *	bb
     *	cc
     *	dd
     */
}
排序
方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator comp)产生一个新流,其中按比较器顺序排序
// sorted()
 @Test
public void test9() {
    // 自然排序
    List<String> list = Arrays.asList("a", "ccc", "bbb", "dd");
    list.stream()
        .sorted()
        .forEach(System.out::println);
    /**
     *输出:
     *	a
     *	bbb
     *	ccc
     *	dd
     */
    // 比较器排序
    employees.stream()
        .distinct()
        .forEach(System.out::println);
	/**
     *输出:
     *	Employee(id=1, name=张三, age=18, salary=2500)
	 *	Employee(id=2, name=李四, age=25, salary=6500)
	 *	Employee(id=3, name=王五, age=38, salary=6000)
	 *	Employee(id=4, name=赵六, age=45, salary=8500)
	 *	Employee(id=5, name=田七, age=25, salary=1500)
     */
    employees.stream()
        .sorted((e1, e2) -> {
            if (e1.getAge() == e2.getAge()) {
                return e1.getName().compareTo(e2.getName());
            } else {
                return Integer.compare(e1.getAge(), e2.getAge());
            }
        })
        .distinct()
        .forEach(System.out::println);
    /**
     *输出:
     *	Employee(id=1, name=张三, age=18, salary=2500)
	 *	Employee(id=2, name=李四, age=25, salary=6500)
	 *	Employee(id=5, name=田七, age=25, salary=1500)
	 *	Employee(id=3, name=王五, age=38, salary=6000)
	 *	Employee(id=4, name=赵六, age=45, salary=8500)
     */
}
终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer、Double、String等等,甚至是 void 。

在Java8中,Stream的终止操作可以分为:查找与匹配、规约和收集。

// 所用数据源
List<Employee> employees = Arrays.asList(
            new Employee(1, "张三", 18, 2500, Employee.Status.FREE),
            new Employee(2, "李四", 25, 6500, Employee.Status.BUSY),
            new Employee(3, "王五", 38, 6000, Employee.Status.VACATION),
            new Employee(4, "赵六", 45, 8500, Employee.Status.BUSY),
            new Employee(5, "田七", 25, 1500, Employee.Status.VACATION)
    );
查找与匹配
方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代)
@Test
public void test1() {
    // allMatch()  检查元素是否全部匹配 全为 BUSY 为 true 否则为 false  类似:AND
    boolean allMatch = employees.stream()
        .allMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(allMatch); // false
    // anyMatch() 检查元素至少有一个匹配 包含这个 BUSY 为 true 否则为 false 类似:OR
    boolean anyMatch = employees.stream()
        .anyMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(anyMatch); // true
    // noneMatch() 检查元素没有匹配 包含这个 BUSY 为false 否则为 true 类似:!非
    boolean noneMatch = employees.stream()
        .noneMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(noneMatch); // flase
    // findFirst() 返回第一个元素 返回的是 Optional<T> 容器类  后续会在补充
    Optional<Employee> findFirst = employees.stream()
        .sorted(Comparator.comparingInt(Employee::getSalary))
        .findFirst();
    System.out.println(findFirst.get()); // Employee(id=5, name=田七, age=25, salary=1500, status=VACATION)
    // findAny() 返回任意一个元素 返回的是 Optional<T> 容器类  后续会在补充  parallelStream() 并行流 后续补充
    Optional<Employee> findAny = employees.parallelStream()
        .filter(k -> k.getStatus().equals(Employee.Status.BUSY))
        .findAny();
    System.out.println(findAny.get()); // Employee(id=2, name=李四, age=25, salary=6500, status=BUSY)
}

@Test
public void test2() {
    // count() 返回流中元素总数
    long count = employees.stream()
        .count();
    System.out.println(count);
    // max() 返回流中最大值
    Optional<Employee> max = employees.stream()
        .max(Comparator.comparingInt(Employee::getSalary));
    System.out.println(max.get());
    // min() 返回流中最小值
    Optional<Integer> min = employees.stream()
        .map(Employee::getSalary)
        .min(Integer::compare);
    System.out.println(min.get());
}

// forEach 就不用补充了
规约
方法描述
reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。 返回 T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。 返回 Optional
@Test
public void test3() {
    // reduce(T iden, BinaryOperator b)/reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回T/Optional<T>
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Integer reduce = list.stream() // Integer  因为第一个参数是起始值,他不会为空 所以返回T
        .reduce(0, Integer::sum);
    System.out.println(reduce); // 45

    Optional<Integer> optional = employees.stream()
        .map(Employee::getSalary)  // 返回Optional,因为没有起始值,可能为空,所以返回Optional
        .reduce(Integer::sum);
    System.out.println(optional.get());
}
收集
方法描述
collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
如何收集Stream流?
方法返回类型作用
toListList把流中元素收集到List
toSetSet把流中元素收集到Set
toCollectionCollection把流中元素收集到创建的集合
countingLong计算流中元素的个数
summingIntInteger对流中元素的整数属性求和
averagingIntDouble计算流中元素Integer属性的平均 值
summarizingIntIntSummaryStatistics收集流中Integer属性的统计值。 如:平均值
joiningString连接流中每个字符串
maxByOptional根据比较器选择最大值
minByOptional根据比较器选择最小值
reducing归约产生的类型从一个作为累加器的初始值 开始,利用BinaryOperator与 流中元素逐个结合,从而归 约成单个值
collectingAndThen转换函数返回的类型包裹另一个收集器,对其结 果转换函数
groupingByMap<K, List>根据某属性值对流分组,属 性为K,结果为V
partitioningByMap<Boolean, List>根据true或false进行分区

每个方法对应的使用示例如下表所示。

方法使用示例
toListList employees= list.stream().collect(Collectors.toList());
toSetSet employees= list.stream().collect(Collectors.toSet());
toCollectionCollection employees=list.stream().collect(Collectors.toCollection(ArrayList::new));
countinglong count = list.stream().collect(Collectors.counting());
summingIntint total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingIntdouble avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary))
summarizingIntIntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
CollectorsString str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxByOptionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minByOptional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducingint total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThenint how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingByMap<Emp.Status, List> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus));
partitioningByMap<Boolean,List>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));
// Collection
@Test
public void test4() {
    // toList
    List<String> list = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toList());
    list.forEach(System.out::println);
    // toSet
    Set<String> set = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toSet());
    set.forEach(System.out::println);
    // toCollection
    LinkedList<String> linkedList = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toCollection(LinkedList::new)); // 后面这个 想要那个 类型就 new那个类型 LinkedList,HashSet 等等等等
    linkedList.forEach(System.out::println);
}

@Test
public void test5() {
    // 总数
    Long count = employees.stream()
        .collect(Collectors.counting());
    System.out.println(count);
    // 平均数
    Double aDouble = employees.stream()
        .collect(Collectors.averagingInt(Employee::getSalary));
    System.out.println(aDouble);
    // 总和
    Integer sum = employees.stream()
        .collect(Collectors.summingInt(Employee::getSalary));
    System.out.println(sum);
    // 最大值
    Optional<Employee> max = employees.stream()
        .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary())));
    System.out.println(max.get());
    // 最小值
    Optional<Integer> min = employees.stream()
        .map(Employee::getSalary)
        .collect(Collectors.minBy((x, y) -> Integer.compare(x, y)));
    System.out.println(min.get());
}

// 分组
@Test
public void test6() {
    Map<Employee.Status, List<Employee>> groupingBy = employees.stream()
        .collect(Collectors.groupingBy(Employee::getStatus));
    groupingBy.forEach((k, v) -> System.out.println(k + "--->" + v));
    /**
     * 输出:
     *      VACATION--->[Employee(id=3, name=王五, age=38, salary=6000, status=VACATION), Employee(id=5, name=田七, age=25, 		 *		salary=1500, status=VACATION), Employee(id=5, name=田七, age=25, salary=1500, status=VACATION)]
     *
     *      BUSY--->[Employee(id=2, name=李四, age=25, salary=6500, status=BUSY), Employee(id=4, name=赵六, age=45, 				 *		salary=8500, status=BUSY)]
     *
     *      FREE--->[Employee(id=1, name=张三, age=18, salary=2500, status=FREE)]
     */
}

// 多列分组
@Test
public void test7() {
    Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream()
        .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {
            if (e.getAge() < 30) {
                return "青年";
            } else {
                return "壮年";
            }
        })));
    collect.forEach((k, v) -> System.out.println(k + "--->" + v));
    /**
     * 输出:
     *      BUSY--->{青年=[Employee(id=2, name=李四, age=25, salary=6500, status=BUSY)], 壮年=[Employee(id=4, name=赵六, 			 *		age=45, salary=8500, status=BUSY)]}
     *
     *      FREE--->{青年=[Employee(id=1, name=张三, age=18, salary=2500, status=FREE)]}
     *
     *      VACATION--->{青年=[Employee(id=5, name=田七, age=25, salary=1500, status=VACATION), Employee(id=5, name=田七, 			 *		age=25, salary=1500, status=VACATION)], 壮年=[Employee(id=3, name=王五, age=38, salary=6000, status=VACATION)]}
     */
}

// 分片/分区
@Test
public void test8() {
    Map<Boolean, List<Employee>> partitioningBy = employees.stream()
        .distinct()
        .collect(Collectors.partitioningBy(e -> e.getSalary() > 6000));
    partitioningBy.forEach((k, v) -> System.out.println(k + "---" + v));
    /**
     * 输出:
     *      false---[Employee(id=1, name=张三, age=18, salary=2500, status=FREE), Employee(id=3, name=王五, age=38, salary=6000,  	   *	  status=VACATION),Employee(id=5, name=田七, age=25, salary=1500, status=VACATION)]
     *
     *      true---[Employee(id=2, name=李四, age=25, salary=6500, status=BUSY), Employee(id=4, name=赵六, age=45, salary=8500, 	 *		status=BUSY)]
     */
}

@Test
public void test9() {
    DoubleSummaryStatistics dds = employees.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dds.getMax());
    System.out.println(dds.getAverage());
    System.out.println(dds.getCount());
    System.out.println(dds.getMin());
    System.out.println(dds.getSum());
}

// 获取到数据以后拼接可以用到
@Test
public void test10() {
    String names1 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining());
    System.out.println(names1); // 张三李四王五赵六田七
    String names2 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining(","));
    System.out.println(names2); // 张三,李四,王五,赵六,田七
    String names3 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining(",", "==", "=="));
    System.out.println(names3); // ==张三,李四,王五,赵六,田七==
}

三、并行流与串行流

java8的底层是 ForkJoin 框架,无限拆分最后合到一块总体来说比串行流快

/**
 * @author G
 */
public class ForkJoinCalculate extends RecursiveTask<Long> {

    private Long start;
    private Long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD) {
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            left.fork();
            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();
            return left.join() + right.join();
        }
    }
}


public class TestForkJoin {

    /**
     * ForkJoin 框架
     */
    @Test
    public void test1() {

        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000000);

        Long sum = pool.invoke(task);

        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:" + Duration.between(start, end).toMillis()); //133
    }

    /**
     * 普通 for
     */
    @Test
    public void test2() {
        Instant start = Instant.now();
        Long sum = 0L;
        for (long i = 0; i <= 100000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:" + Duration.between(start, end).toMillis()); //958
    }

    /**
     * java8 并行流
     */
    @Test
    public void test3() {
        Instant start = Instant.now();
        long sum = LongStream.rangeClosed(0, 100000000L)
                .parallel()
                .reduce(0, Long::sum);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:" + Duration.between(start, end).toMillis()); // 220
    }
}

Optional 容器类

Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null 表示一个值不存在,现在Optional 可以更好的表达这个概念。并且可以避免空指针异常。

  • Optional.of(T t) : 创建一个Optional 实例
  • Optional.empty() : 创建一个空的Optional 实例
  • Optional.ofNullable(T t):若t 不为null,创建Optional 实例,否则创建空实例
  • isPresent() : 判断是否包含值
  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回s 获取的值
  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map 类似,要求返回值必须是Optional
 @Test
public void test() {
    // Optional.of(T t):封装一个Optional实例,其中值t不能为null,否则有NullPointException
    Optional<Employee> optional1 = Optional.of(new Employee());
    System.out.println(optional1.get());
    // 运行结果:Employee{name='null', age=null, salary=null, status=null}

    // Optional.empty():封装一个空的Optional对象(封装null)
    Optional<Employee> optional2 = Optional.empty();
    System.out.println(optional2.get());
    // 运行结果:Exception in thread "main" java.util.NoSuchElementException: No value present
    //	        at java.util.Optional.get(Optional.java:135)
    //	        at TestOptional.main(TestOptional.java:16)

    // Optional.ofNullable(T t):非null就和of方法一样,null就和empty()一样
    Optional<Employee> optional3 = Optional.ofNullable(new Employee());
    // isPresent():判断其中是否包含值
    if (optional3.isPresent()) {
        System.out.println(optional3.get());
    }
    // 运行结果:Employee{name='null', age=null, salary=null, status=null}

    // orElse(T t) : 如果包含值,那返回值;否则返回替代值T
    Optional<Employee> optional4 = Optional.ofNullable(null);
    Employee Employee1 = optional4.orElse(new Employee(18, "张三", 24, 12450, Employee.Status.FREE));
    System.out.println(Employee1);
    // 运行结果:Employee{name='张三', age=24, salary=15000.1, status=FREE}

    // orElseGet(Supplier s) :如果包含值,那返回值;否则返回s 获取的值
    Employee Employee2 = optional4.orElseGet(() -> new Employee());
    System.out.println(Employee2);
    // 运行结果:Employee{name='null', age=null, salary=null, status=null}

    // map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
    Optional<Employee> optional5 = Optional.ofNullable(new Employee(15, "张三", 24, 38462, Employee.Status.FREE));
    Optional<String> stringOptional1 = optional5.map(Employee::getName);
    System.out.println(stringOptional1.get());
    // 运行结果:张三

    // flatMap(Function mapper):与map 类似,要求返回值必须是Optional
    Optional<String> stringOptional2 = optional5.flatMap(x -> Optional.of(x.getName()));
    System.out.println(stringOptional2.get());
    // 运行结果:张三
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值