第十七章、java8 新特性-----Lambda表达式、Stream、Optional

一、Lambda表达式

1、函数式编程的思想

只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

2、什么是lambda表达式:实现函数式接口的语法

其实就是实现SAM接口语法糖,使得Java也算是支持函数式编程的语言。

好处: 为了减少冗余的代码,替换原来匿名内部类的写法

**用法:**给“函数式接口/SAM”的变量或形参赋值

SAM:Single Abstract Method

某个接口它有且只有一个抽象方法。

但是,这个接口可以允许有其他的静态方法和默认方法,这个个数不限制。

我们只限制抽象方法的个数,有且只有一个。

我们把满足这种特征的接口,称为函数式接口

语法糖

**“语法糖”**是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实
底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部
类的“语法糖”,但是二者在原理上是不同的。

结论

SAM接口的特征,即只有一个抽象方法的接口,都可以使用Lambda表达式。

Java8还增加了一个注解**@FunctionalInterface**,这个注解用于标记某个接口是函数式接口。

  • 如果加了@FunctionalInterface这个注解的,明确是函数式接口,我们可以放心的使用Lambda表达式。

  • 如果没有标记@FunctionalInterface这个注解的,表示之后这个接口可能会修正,称为一个非函数式接口,目前虽然也可以使用Lambda表达式,但是建议不要使用。

(1)建议只针对标记@FunctionalInterface这个注解的函数式接口使用Lambda表达式。

(2)如果自己要声明函数式接口,请加上**@FunctionalInterface**这个注解

3、接口的分类

Java8版本,给我们增加了很多很多的新的函数式接口,在java.util.function 包下

一共分为四大类

1、消费型接口:

​ 它的抽象方法:有参无返回值

2、供给型接口

​ 它的抽象方法:无参有返回值

3、判断型接口

​ 它的抽象方法:有参有返回值,但是返回值类型是boolean类型

4、功能型接口

​ 它的抽象方法:有参有返回值

3.1、消费型接口:有参无返回值

消费型接口的抽象方法特点:有形参,但是返回值类型是void

经典代表

  •  Consumer<T>接口		它的抽象方法     void accept(T t)  
    
接口名抽象方法描述
Consumervoid accept(T t)接收一个对象用于完成功能
BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
DoubleConsumervoid accept(double value)接收一个double值
IntConsumervoid accept(int value)接收一个int值
LongConsumervoid accept(long value)接收一个long值
ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值
ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值
ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值
3.2、供给型接口:无参有返回值

这类接口的抽象方法特点:无参,但是有返回值

经典代表

  •  Supplier<T>  它的抽象方法    T       get()  
    
接口名抽象方法描述
SupplierT get()返回一个对象
BooleanSupplierboolean getAsBoolean()返回一个boolean值
DoubleSupplierdouble getAsDouble()返回一个double值
IntSupplierint getAsInt()返回一个int值
LongSupplierlong getAsLong()返回一个long值
3.3、判断型接口:有参有返回值,但是返回值类型是boolean类型

这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。

经典代表

  •  Predicate<T>   抽象方法    boolean test(T t)  
    
接口名抽象方法描述
Predicateboolean test(T t)接收一个对象
BiPredicate<T,U>boolean test(T t, U u)接收两个对象
DoublePredicateboolean test(double value)接收一个double值
IntPredicateboolean test(int value)接收一个int值
LongPredicateboolean test(long value)接收一个long值
3.4、功能型接口:有参有返回值

这类接口的抽象方法特点:既有参数又有返回值

经典代表:

  •  Function<T,R>		抽象方法    R      apply(T t)                   R:return        T:type
    
接口名抽象方法描述
Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果
UnaryOperatorT apply(T t)接收一个T类型对象,返回一个T类型对象结果
DoubleFunctionR apply(double value)接收一个double值,返回一个R类型对象
IntFunctionR apply(int value)接收一个int值,返回一个R类型对象
LongFunctionR apply(long value)接收一个long值,返回一个R类型对象
ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象,返回一个double
ToIntFunctionint applyAsInt(T value)接收一个T类型对象,返回一个int
ToLongFunctionlong applyAsLong(T value)接收一个T类型对象,返回一个long
DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果
DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果
IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果
IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果
LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果
LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果
DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double
IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果
LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果
BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果
BinaryOperatorT apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果
ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double
ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int
ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long
DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果
IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果
LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果
3.5、自定义函数式接口

只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称 {
    public abstract 返回值类型 方法名称(可选参数信息);
    // 其他非抽象方法内容
}

接口当中抽象方法的 public abstract 是可以省略的

例如:声明一个计算器Calculator接口,内含抽象方法calc可以对两个int数字进行计算,并返回结果:

public interface Calculator {
    int calc(int a, int b);
}

在测试类中,声明一个如下方法:

    public static void invokeCalc(int a, int b, Calculator calculator) {
        int result = calculator.calc(a, b);
        System.out.println("结果是:" + result);
    }

4、Lambda表达式语法

语法格式:

​ (形参列表) -> {Lambda体}

说明:

  •  (形参列表):它是你要赋值的函数式接口的抽象方法的形参列表
    
  •  ->:Lambda操作符,英文状态下输入,中间不要加空格,一个减号一个大于号
    
  •  {Lambda体}:它是你要赋值的函数式接口的抽象方法的实现,即实现该抽象方法的方法体
    
4.1、Lambda的优化/简化
  • (1)当(形参列表)的形参类型是已知的或者是可以推断的,那么形参列表的数据类型可以省略

  • (2)当(形参列表)的形参个数只有一个,并且数据类型已经省略的情况下,那么()也可以省略

  • (3)当(形参列表)无参,那么()是不能省略的

  • (4)当{Lambda体}中语句只有一句,那么{}和;可以省略

  • (5)当{Lambda体}中语句只有一句,如果这句语句是一个return语句,那么要么连同return一起省略,要么不省略

  • (6)如果{Lambda体}的{}没有省略,那么每个语句仍然要;,如果对应的抽象方法有返回值,仍然要return

/* 例如:
 * 	Runnable接口  抽象方法   void run()
 * 		为它赋值的Lambda表达式    () -> {...}
 *  Consumer<T>接口   抽象方法   void accept(T t)
 *  	为它赋值的Lambda表达式  (T t) -> {...}
 *  Predicate<T>接口  抽象方法  boolean test(T t)	
 * 		为它赋值的Lambda表达式  (T t) -> {...}
 * 
 * 例如:
 * 	Runnable接口  抽象方法   void run()
 * 		为它赋值的Lambda表达式    () -> {...}
 * 		我要让这个线程,打印hello
 * 		为它赋值的Lambda表达式    () -> {System.out.println("hello");}
 *  Consumer<T>接口   抽象方法   void accept(T t)
 *  	为它赋值的Lambda表达式  (T t) -> {...}
 *  Predicate<T>接口  抽象方法  boolean test(T t)	
 * 		为它赋值的Lambda表达式  (T t) -> {...}
 */

5、方法引用与构造器引用

Lambda表达式作用是为了简化匿名内部类的冗余代码。

方法引用和构造器引用作用是为了简化Lambda表达式。

方法引用和构造器引用:当Lambda表达式满足一些特殊情况时,可以使用。

(1)当Lambda表达式的{Lambda体}只有一个语句,并且这句语句是通过调用一个某个类或某个对象的现 有的方法来完成的。

(2)并且Lambda表达式(形参列表)中的所有形参,都被用上了,用在了{Lambda体}中这个调用方法的实参列表 中或者,其中第一个形参作为调用这个方法的对象,其余形参作为调用这个方法的实参列表。

(3)整个Lambda表达式中没有其他多余的数据出现

5.1、方法引用

方法引用的语法格式:

(1)实例对象名::实例方法

(2)类名::静态方法

(3)类名::实例方法

说明:

  • ::称为方法引用操作符(两个:中间不能有空格,而且必须英文状态下半角输入)
  • Lambda表达式的形参列表,全部在Lambda体中使用上了,要么是作为调用方法的对象,要么是作为方法的实参。
  • 在整个Lambda体中没有额外的数据。
	@Test
	public void test4(){
//		Runnable r = () -> System.out.println("hello lambda");
		Runnable r = System.out::println;//打印空行
		
		//不能简化方法引用,因为"hello lambda"这个无法省略
	}
	
	@Test
	public void test3(){
		String[] arr = {"Hello","java","chai"};
//		Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2));
		
		//用方法引用简化
		/*
		 * Lambda表达式的形参,第一个(例如:s1),正好是调用方法的对象,剩下的形参(例如:s2)正好是给这个方法的实参
		 */
		Arrays.sort(arr, String::compareToIgnoreCase);
	}
	
	@Test
	public void test2(){
//		Stream<Double> stream = Stream.generate(() -> Math.random());
		
		//用方法引用简化
		Stream<Double> stream = Stream.generate(Math::random);
	}
	
	@Test
	public void test1(){
		List<Integer> list = Arrays.asList(1,3,4,8,9);
		
		//list.forEach(t -> System.out.println(t));
		
		//用方法再简化
		list.forEach(System.out::println);
	}
5.2、构造器引用

(1)当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表。

(2) 当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度

构造器引用的语法格式:

  • 类名::new
  • 数组类型名::new
public class TestMethodReference {
	
	//这个方法是模仿HashMap中,把你指定的数组的长度纠正为2的n次方的代码
	//createArray()的作用是,创建一个长度为2的n次方的数组
	public <R> R[] createArray(Function<Integer,R[]> fun,int length){
		int n = length - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        length = n < 0 ? 1 : n + 1;
		return fun.apply(length);
	}
	
	@Test
	public void test6(){
		/*
		 * Function是一个函数式接口,可以用Lambda表达式赋值
		 * Function<T,R>的抽象方法   R apply(T t)
		 * 
		 * createArray这个方法中用的是Function<Integer,R[]> fun。说明T类型已经指定为Integer
		 * 说明
		 */
//		Function<Integer,String[]> f = (Integer len) -> new String[len];
		
		//因为Lambda体是在创建一个数组对象完成的,而且Lambda表达式的形参正好是创建数组用的长度
		//通过构造器引用省略
		Function<Integer,String[]> f = String[]::new;
		String[] array = createArray(f, 10);
		
		System.out.println(array.length);//16
	}
	
	@Test
	public void test5(){
//		Supplier<String> s = () -> new String();//通过供给型接口,提供一个空字符串对象
		
		//构造器引用
		Supplier<String> s = String::new;//通过供给型接口,提供一个空字符串对象
	}

}

二、Stream API

1、定义

Stream:数据处理流。是对内存中的数据,一般是集合或数组中的数据进行处理用的。

2、作用

Stream只负责对数据进行处理(筛选、过滤、统计、遍历显示等),不负责存储。

存储数据仍然由集合、数组等容器完成。

Stream对数据的处理是不修改集合和数组,只会返回处理后的结果。

好比:select的sql查询语句对数据库进行查询,得到结果,但是查询不会修改数据库中的数据。

3、Stream特点

(1)Stream对数据的处理是不修改集合和数组,只会返回处理后的结果。

(2)Stream操作是一个延迟操作,如果没有进行终结操作之前,之前的操作是不会执行的

(3)Stream是不可变对象,即每次调用stream的中间操作方法后,必须重新用Stream的对象接收,否则就会有问题或者进行连写

4、Stream的操作步骤(共三步)

(1)创建Stream

(2)中间操作

​ 0~n步的筛选、排序…

(3)终结操作

如:

//创建流、中间、终结    进行了“连写”
Stream.generate(Math::random)     //创建Stream
  .limit(10)
  .filter(num->num>0.5)
  .distinct()					//中间操作,此处三步
  .forEach(System.out::println);  //终结操作
4.1、创建Stream(五种方式)
(1)通过集合创建

继承了Collection接口的集合类可以使用下面两种方式来创建Stream流

方式1:集合对象.stream -----------------创建普通Stream流

方式2:集合对象.parallelStream -----------------创建并行流

方式一:

ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
	list.add(i);
}

//Java8给集合添加了stream()
Stream<Integer> stream = list.stream();

方式二:

list.parallelStream().forEach(s -> System.out.print("|||" + s));//---------创建并行流
(2)通过数组工具类

Arrays.stream(数组对象)

主要包含两种方式

方式1:Arrays.stream(T[] array)

Arrays.stream(T[] array)-------------------------------------------参数为一个数组

Arrays.stream(new String[]{"hello", "world", "hi", "java"}).forEach(System.out::println);
方式2:Arrays.stream(T[] array, int startInclusive, int endExclusive)

Arrays.stream(T[] array, int startInclusive, int endExclusive)-----参数为一个数组,加上起始下标(包头不包尾)

Arrays.stream(new int[]{1111, 1, 22, 33, 4, 5, 6}, 2, 5).forEach(System.out::println); //22,33,4

Arrays.stream(new String[]{"hello", "world", "hi", "java", "python"}, 1,     3).forEach(System.out::println); //world hi
(3)Stream类创建流

注意1: Stream类创建流与 “数据流” (下方第4种)创建流的方式很想只是少了range 和 rangeClosed方法

注意2: Stream和IntStream/LongStream/DoubleStream/并没有继承关系,由 IntStream/LongStream/DoubleStream变为Stream流要进行装箱操作----boxed

Stream.generate

static Stream generate(Supplier s)

这个静态方法,也是无限生成对象的集合流,也是一个无限流

Stream.iterate

static Stream iterate(final T seed, final UnaryOperator f)

是Stream接口下的一个静态方法,从名字也可以看出,这个静态方法,是以迭代器的形式,创建一个数据流

Stream.of

①static Stream of(T t)

​ return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);

②static Stream of(T… values)

​ return Arrays.stream(values);

//产生一个空的Stream流
Stream.empty();
Stream.of("hello", "world", "hi", "java").forEach(System.out::println);
Stream.of(new String[]{"hello2", "world2", "hi2", "java2"}).forEach(System.out::println);
Stream.iterate("say", s -> s + " hi").limit(3).forEach(System.out::println); //say    say hi    say hi hi

Stream.generate(Math::random).limit(5).forEach(System.out::println);  //产生5个double随机数
 //合并数组
Stream.concat(Stream.of(11, 22, 33), Stream.of(44, 55, 66)).forEach(System.out::println); 
/**
* 这里需要注意Stream和IntStream并没有继承关系,InStream转为Stream要进行装箱操作  boxed
*/
Stream.concat(IntStream.of(99, 88, 77).boxed(), Stream.of(-11, -22, -33)).forEach(System.out::println);
//同时要注意下面种方式是无法完成转换的
//IntStream intStream = (IntStream) Stream.of(1, 2, 3); //会报错
(4)数字流

数字流-------IntStream/LongStream/DoubleStream/

以IntStream为例

 //产生一个空的IntStream流
IntStream.empty(); 
IntStream.of(1, 2, 3, 4).forEach(System.out::println);
IntStream.of(new int[]{11, 22, 33, 44}).forEach(System.out::println);
//1 3 5 7 9 (其中第一个参数1为基数,第二个参数要一个输入和输出都为int型的一元函数)
IntStream.iterate(1, i -> i + 2).limit(5).forEach(System.out::println); 
//参数为一个提供者
IntStream.generate(() -> 3333).limit(5).forEach(System.out::println); 
/**1-9,如果第一个参数大于第二个参数返回一个空的IntStream流*/
IntStream.range(1, 10).forEach(System.out::println);  
/**1-10,如果第一个参数大于第二个参数返回一个空的IntStream流*/
IntStream.rangeClosed(1, 10).forEach(System.out::println); 
//合并两个IntStream流
IntStream.concat(IntStream.of(1, 2, 3), IntStream.of(11, 22, 33)).forEach(System.out::println); 

(5)其它方式

由字符串.chars创建流

/**由字符串直接获得一个流对象*/
"abc".chars().forEach(System.out::println);//97 98 99 //流中是各个字符对应的ASCII码值
"abc".chars().forEach(i -> System.out.println((char) i)); //a b c

由 new Random().ints new Random().longs new Random().doubles

以new Random().ints 为例

//产生5个随机数//通过limit限制流产生的个数
new Random().ints().limit(5).forEach(System.out::println); 
//产生4个随机数//在ints()里显示流产生的个数
new Random().ints(4).forEach(System.out::println); 
//随机产生5个在2-7之间的数字(包头不包尾)
new Random().ints(2, 7).limit(5).forEach(System.out::println);  
//随机产生5个大小在1-10之间的数(包头不包尾)
new Random().ints(5, 1, 10).forEach(System.out::println); 
4.2、中间操作

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

方 法描 述
filter(Predicate p)接收 Lambda , 从流中排除某些元素
distinct()筛选,通过流所生成元素的equals() 去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
peek(Consumer action)接收Lambda,对流中的每个数据执行Lambda体操作
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
package com.atguigu.test06;

import java.util.ArrayList;
import java.util.OptionalDouble;
import java.util.stream.Stream;

import org.junit.Test;

/*
 * 中间操作:0~n步
 * (1)filter(Predicate p):过滤
 * (2)distinct():去重
 * (3)limit(long maxSize):限制几个
 * (4)skip(long n):跳过几个
 * (5)sorted():排序  		用元素的自然排序
 *    sorted(Comparator com)排序  		用定制排序
 * (6)peek(Consumer action):对流中的数据进行Consumer指定的操作,但是不影响元素的个数
 * (7)map(Function f)
 * (8)mapToDouble(ToDoubleFunction f)
 * (9)flatMap(Function f)   
 * 
 * 为了验证结果,必须有终结操作,才能看到结果
 * 终结操作:void forEach(Consumer c)
 * 		  long count():统计流中的元素的个数
 * 		T reduce(DoubleBinaryOperator op) 
 */
public class TestMiddle {
	
	@Test
	public void test13(){
		//把流中的"hello:java"等,拆开,合并成一个新的流
		Stream.of("hello:java","atguigu:good","world:big")
			/*
			 * <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)  
			 * Function<T,R>    R apply(T t)
			 * Function<T,R>    Stream<R> apply(T t)
			 * Function<String,R>    Stream<R> apply(String t)
			 * 
			 * t.split(":")返回值类型    String[]
			 */
			.flatMap(t -> Stream.of(t.split(":")))
			.forEach(System.out::println);
			
	}
	
	@Test
	public void test12(){
		ArrayList<Employee> list = new ArrayList<>();
		list.add(new Employee("张三", 13000));
		list.add(new Employee("李四", 14000));
		list.add(new Employee("王五", 9000));
		
		//用流操作集合的元素
		//(1)打印流中的每一个元素
		//(2)只保留薪资是10000以上的
		//(3)统计最后留下的员工的薪资总数
		OptionalDouble sum = list.stream()
								.peek(System.out::println)
								.filter(e -> e.getSalary()>10000)
								/*
								 * ToDoubleFunction   抽象方法   double applyAsDouble(T value)  
								 */
								.mapToDouble(e -> e.getSalary())
								/*
								 * DoubleBinaryOperator   抽象方法 double applyAsDouble(double left, double right)  
								 */
								.reduce((a,b)->a+b);
		System.out.println("sum = " + sum);
		
	}
	
	@Test
	public void test11(){
		//假设,把流中的字符串的首字母变为大写
		Stream.of("hello","java","world")
			/*
			 * Function<T,R>  R apply(T t)
			 */
			.map(str -> Character.toUpperCase(str.charAt(0)) + str.substring(1))
			.forEach(System.out::println);
	}
	
	
	@Test
	public void test10(){
		//假设,把流中的数据都扩大2倍
		Stream.of(1,2,3,4,5)
			/*
			 * Function<T,R>  R apply(T t)
			 */
			.map(num -> num*2)
			.forEach(System.out::println);
	}
	
	@Test
	public void test09(){
		//对流中的数据进行去重、打印、统计最后的元素个数
		long c = Stream.of(11,20,1,3,4,44,23,11,20,34,2,2,5,6)
				.distinct()//中间操作1
				.peek(t -> System.out.println(t))//中间操作1
				.count();//终结
		System.out.println("数量是:"+c);
	}
	
	@Test
	public void test08(){
		//对流中的数据进行排序,取出第三大的
		Stream.of(11,20,1,3,4,44,23,11,20,34,2,2,5,6)
				.sorted((n1,n2)-> -Integer.compare(n1, n2))
				.distinct()
				.limit(3)
				.skip(2)
				.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test07(){
		//对流中的数据进行排序,取出最大的三个
		Stream.of(11,20,1,3,4,44,23,11,20,34,2,2,5,6)
				.sorted((n1,n2)-> -Integer.compare(n1, n2))
				.distinct()
				.limit(3)
				.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test06(){
		//对流中的数据进行排序,取出最小的三个
		Stream.of(11,20,1,3,4,44,23,11,20,34,2,2,5,6)
				.sorted()
				.distinct()
				.limit(3)
				.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test05(){
		Stream.of(1,2,1,3,4,2,5,6)
			.distinct()   //1,2,3,4,5,6
			.skip(3)		//4,5,6
			.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test04(){
		Stream.generate(Math::random)
				.limit(5)
				.forEach(num -> System.out.println(num));
	}
	
	@Test
	public void test03(){
		Stream.of(1,2,1,3,4,2,5,6)
			.distinct()
			.filter(num -> num<5)
			.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test02(){
		Stream.of(1,2,1,3,4,2,5,6)
			.distinct()
			.forEach(str -> System.out.println(str));
	}
	
	@Test
	public void test01(){
		Stream.of("hello","world","java","hi","atguigu")
				/*
				 * Predicate<T>:判断型接口   boolean test(T t)
				 */
			.filter(str -> str.length()<=5)
				/*
				 * Consumer<T> void accept(T t)
				 */
			.forEach(str -> System.out.println(str));
		
	}
}
class Employee{
	private String name;
	private double salary;
	public Employee(String name, double salary) {
		super();
		this.name = name;
		this.salary = salary;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + ", salary=" + salary + "]";
	}
	
}

4.3、终结操作

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

方法描述
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch(Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional findFirst()返回第一个元素
Optional findAny()返回当前流中的任意元素
long count()返回流中元素总数
Optional max(Comparator c)返回流中最大值
Optional min(Comparator c)返回流中最小值
void forEach(Consumer c)迭代
T reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 T
U reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 Optional
R collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。

package com.atguigu.test06;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

/*
 * 终结操作:
 * (1)void forEach(Consumer c):遍历流中的元素
 * (2)long count():统计流中的元素的总个数
 * (3)U reduce(BinaryOperator b) :对流中的元素,反复执行某个操作,得到最后的一个总结果
 * (4)boolean allMatch(Predicate p):是否都匹配
 * (5)boolean noneMatch(Predicate  p):是否都不匹配
 * (6)boolean anyMatch(Predicate p):任意一个匹配
 * (7)Optional<T> findFirst()
 * (8)Optional<T> max(Comparator c) 
 * Optional<T> min(Comparator c)
 * (9)R  collect(Collector c):收集
 *			把处理完的结果收集到集合等里面
 */
public class TestEnding {
	@Test
	public void test05(){
		List<Integer> list = Stream.of(1,2,3,4,5,6,7,8)
			.filter(num -> num%2==0)
			/*
			 * java.util.stream.Collector接口
			 * 它有一个工具类  java.util.stream.Collectors工具类
			 */
			.collect(Collectors.toList());
		System.out.println(list);
	}
	
	@Test
	public void test04(){
		Optional<Integer> first = Stream.of(1,2,3,4)
				.sorted((t1,t2) -> Integer.compare(t2, t1))
			.findFirst();
		System.out.println(first);
	}
	
	@Test
	public void test03(){
		//是否都是奇数
		boolean allMatch = Stream.of(1,21,3,5,7)
			/*
			 * Predicate<T>  boolean test(T t)
			 */
			.allMatch(num -> num%2!=0);
		System.out.println(allMatch);	
	}
	
	@Test
	public void test02(){
		Optional<Integer> max = Stream.of(1,2,3,4,5)
			/*
			 * BinaryOperator  T apply(T t1, T t2)
			 */
			.reduce((t1,t2) -> t1>t2?t1:t2);
		System.out.println(max);
	}
	
	@Test
	public void test01(){
		Optional<Integer> sum = Stream.of(1,2,3,4)
			/*
			 * BinaryOperator  T apply(T t1, T t2)
			 */
			.reduce((t1,t2) -> t1+t2);
		System.out.println(sum);
	}
}

5、有关创建流涉及到的几个接口或工具类

①Stream

②Arrays

③Collection

三、Optional类

Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

优点

1)显式的提醒你需要关注null的情况,对程序员是一种字面上的约束

2)将平时的一些显式的防御性检测给标准化了,并提供一些可串联操作

3)解决null会导致疑惑的概念

API

1、如何创建Optional对象?或者说如何用Optional来装值对象或null值

(1)static Optional empty() :用来创建一个空的Optional

(2)static Optional of(T value) :用来创建一个非空的Optional

(3)static Optional ofNullable(T value) :用来创建一个可能是空,也可能非空的Optional

2、如何从Optional容器中取出所包装的对象呢?

(1)T get() :要求Optional容器必须非空

​ T get()与of(T value)使用是安全的

(2)T orElse(T other) :

​ orElse(T other) 与ofNullable(T value)配合使用,

如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替

(3)T orElseGet(Supplier<? extends T> other) :

​ 如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替

(4) T orElseThrow(Supplier<? extends X> exceptionSupplier)

​ 如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException

3、其他方法

(1)boolean isPresent() :判断Optional容器中的值是否存在

(2)void ifPresent(Consumer<? super T> consumer) :

​ 判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做

(3) Optional map(Function<? super T,? extends U> mapper)

​ 判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做

package com.atguigu.test07;

import java.util.ArrayList;
import java.util.Optional;

import org.junit.Test;

public class TestOptional {
	@Test
	public void test9(){
		String str = "Hello";
		Optional<String> opt = Optional.ofNullable(str);
        //判断是否是纯字母单词,如果是,转为大写,否则保持不变
		String result = opt.filter(s->s.matches("[a-zA-Z]+")).
				map(s -> s.toLowerCase()).
				orElse(str);
		System.out.println(result);
	}
	
	
	@Test
	public void test8(){
		String str = null;
		Optional<String> opt = Optional.ofNullable(str);
		String string = opt.orElseThrow(()->new RuntimeException("值不存在"));
		System.out.println(string);
	}
	
	
	@Test
	public void test7(){
		String str = null;
		Optional<String> opt = Optional.ofNullable(str);
		String string = opt.orElseGet(String::new);
		System.out.println(string);
	}
	
	@Test
	public void test6(){
		String str = "hello";
		Optional<String> opt = Optional.ofNullable(str);
		String string = opt.orElse("atguigu");
		System.out.println(string);
	}
	
	@Test
	public void test5(){
		String str = null;
		Optional<String> opt = Optional.ofNullable(str);
//		System.out.println(opt.get());//java.util.NoSuchElementException: No value present
	}
	
	@Test
	public void test4(){
		String str = "hello";
		Optional<String> opt = Optional.of(str);

		String string = opt.get();
		System.out.println(string);
	}
	
	
	@Test
	public void test3(){
		String str = null;
		Optional<String> opt = Optional.ofNullable(str);
		System.out.println(opt);
	}
	
	@Test
	public void test2(){
		String str = "hello";
		Optional<String> opt = Optional.of(str);
		System.out.println(opt);
	}
	

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值