打怪升级之小白的大数据之旅(三十)<JavaSE最终章之java8新特性>

打怪升级之小白的大数据之旅(三十)

JavaSE最终章之java8新特性

上次回顾

上一章节对java的反射机制进行了分享,本章是JavaSE的最终章,java8的新特性进行分享,Java8新特性主要是Lambda和Stream,Lambda在内部类的时候提到过,这次来详细介绍一下

Java8新特性

  • Java8的新特性有很多,之前我们在学习接口时,学习了接口的静态方法和默认方法,在学习常用类时,学习了新版的日期时间API
  • 今天我们来学习Java8最具革命性的两个新特性:Lambda表达式和StreamAPI。然后带领大家用Optional类解决最令人头疼的空指针异常

Lambda

函数式编程思想

  • 在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类似的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。
  • 对于调用者来做,关注这个方法具备什么样的功能。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

函数式编程思想与面向对象思想的区别

  • 面向对象的思想:
    • 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
  • 函数式编程思想:
    • 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
  • Java8引入了Lambda表达式之后,Java也开始支持函数式编程

还记得我们前面学到的匿名内部类的使用吗?

public class Demo01Runnable {
	public static void main(String[] args) {
    	// 匿名内部类
		Runnable task = new Runnable() {
			@Override
			public void run() { // 覆盖重写抽象方法
				System.out.println("多线程任务执行!");
			}
		};
		new Thread(task).start(); // 启动线程
	}
}
  • 当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程
  • 本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动
  • 在上面使用Runnable定义一个线程的方式总结如下:
    • Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
    • 为了指定run的方法体,不得不需要Runnable接口的实现类;
    • 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
    • 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
    • 而实际上,似乎只有方法体才是关键所在

举个栗子:在这里插入图片描述

  • 当我们需要从家乡到北上广时,可以选择高铁、汽车、汽车或11路公交(徒步),而我们的最终目的就是到北上广,如果到达的方式并不重要,所以我们一直在探索有没有比高铁更好的方式,比如坐飞机
  • 而现在这种飞机(甚至是飞船)已经诞生:2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门在这里插入图片描述
    好了,下面使用lambda来完成对上面的程序
public class Demo02LambdaRunnable {
	public static void main(String[] args) {
		new Thread(() -> System.out.println("多线程任务执行!")).start(); // 启动线程
	}
}

怎么样,是不是感觉代码少了很多?从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定,下面我来说明一下lambda的语法

Lambda语法

  • Lambda表达式是用来给【函数式接口】的变量或形参赋值用的。
  • 其实本质上,Lambda表达式是用于实现【函数式接口】的“抽象方法”
  • Lambda的底层实现是匿名内部类,这里再强调一下
  • 语法格式:
    (形参列表) -> {Lambda体}
    

说明:

  • (形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄
  • {Lambda体}就是实现这个抽象方法的方法体
  • ->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)

优化:Lambda表达式可以精简

  • 当{Lambda体}中只有一句语句时,可以省略{}和{;}
  • 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么return也可以省略,但是如果{;}没有省略的话,return是不能省略的
  • (形参列表)的类型可以省略
  • 当(形参列表)的形参个数只有一个,那么可以把数据类型和()一起省略,但是形参名不能省略
  • 当(形参列表)是空参时,()不能省略
  • 示例代码:
    public class TestLambdaGrammer {
    	@Test
    	public void test1(){
    		//用Lambda表达式给Runnable接口的形参或变量赋值
    		/*
    		 * 确定两件事,才能写好lambda表达式
    		 * (1)这个接口的抽象方法长什么样:
    		 * 		public void run()
    		 * (2)这个抽象方法的实现要干什么事
    		 * 		例如:我要打印“hello lambda"
    		 */
    		Runnable r = () -> {System.out.println("hello lambda");};
    	}
    	
    	@Test
    	public void test2(){
    		//lambda体省略了{;}
    		Runnable r = () -> System.out.println("hello lambda");
    	}
    	
    	@Test
    	public void test3(){
    		String[] arr = {"hello","Hello","java","chai"};
    		
    		//为arr数组排序,但是,想要不区分大小写
    		/*
    		 * public static <T> void sort(T[] a,Comparator<? super T> c)
    		 * 这里要用Lambda表达式为Comparator类型的形参赋值
    		 * 
    		 * 两件事:
    		 * (1)这个接口的抽象方法:  int compare(T o1, T o2)
    		 * (2)这个抽象方法要做什么事?
    		 * 		例如:这里要对String类型的元素,不区分大小写的比较大小
    		 */
    //		Arrays.sort(arr, (String o1, String o2) -> {return o1.compareToIgnoreCase(o2);});
    		
    		//省略了{return ;}
    //		Arrays.sort(arr, (String o1, String o2) ->  o1.compareToIgnoreCase(o2));
    		
    		//省略了两个String
    		Arrays.sort(arr, (o1, o2) ->  o1.compareToIgnoreCase(o2));
    		
    		for (String string : arr) {
    			System.out.println(string);
    		}
    	}
    	
    	@Test
    	public void test4(){
    		ArrayList<String> list = new ArrayList<>();
    		list.add("hello");
    		list.add("java");
    		list.add("world");
    		
    		/*
    		 * JDK1.8给Collection系列的集合,准确的讲是在Iterable接口中,增加了一个默认方法
    		 * 		default void forEach(Consumer<? super T> action) 
    		 * 这个方法是用来遍历集合等的。代替原来的foreach循环的。
    		 * 
    		 * 这个方法的形参是Consumer接口类型,它是函数式接口中消费型接口的代表
    		 * 我现在调用这个方法,想要用Lambda表达式为Consumer接口类型形参赋值
    		 * 
    		 * 两件事:
    		 * (1)它的抽象方法:  void  accept(T t)
    		 * (2)抽象方法的实现要完成的事是什么
    		 * 		例如:这里要打印这个t
    		 */
    //		list.forEach((String t) -> {System.out.println(t);});
    		
    		//省略{;}
    //		list.forEach((String t) -> System.out.println(t));
    		
    		//省略String
    //		list.forEach((t) -> System.out.println(t));
    		
    		//可以省略形参()
    		list.forEach(t -> System.out.println(t));
    	}
    }
    

介绍完Lambda表达式,接下来就介绍一下Lambda的四大分类

函数式接口

  • lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法
  • Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。基本可以满足我们的开发需求。当然你也可以定义自己的函数式接口
  • 重点哈,使用lambda的前提是:
    • 它首先有一个接口
    • 接口中只有一个抽象方法

下面我根据lambda的四大类型为大家介绍一下lambda的用法

消费型接口

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

接口名抽象方法描述
Consumer<T>| void accept(T t)接收一个对象用于完成功能
BiConsumer<T,U>| void accept(T t, U u)接收两个对象用于完成功能
DoubleConsumer| void accept(double value)接收一个double值
IntConsumer| void accept(int value)接收一个int值
LongConsumer| void accept(long value)接收一个long值
ObjDoubleConsumer<T>void accept(T t, double value)接收一个对象和一个double值
ObjIntConsumer<T>void accept(T t, int value)接收一个对象和一个int值
ObjLongConsumer<T>void accept(T t, long value)接收一个对象和一个long值
供给型接口

供给型接口的抽象方法特点:无参,但是有返回值

接口名抽象方法描述
Supplier<T>T get()返回一个对象
BooleanSupplierbooleangetAsBoolean()返回一个boolean值
DoubleSupplierdoublegetAsDouble()返回一个double值
IntSupplierint| getAsInt()返回一个int值
LongSupplierlonggetAsLong()返回一个long值
判断型接口

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

接口名抽象方法描述
Predicate<T>boolean test(T t)接收一个对象
BiPredicate<T,U>boolean test(T t, U u)接收两个对象
DoublePredicatebooleantest(double value)接收一个double值
IntPredicatebooleantest(int value)接收一个int值
LongPredicatebooleantest(long value)接收一个long值
功能型接口

功能型接口的抽象方法特点:既有参数又有返回值

接口名抽象方法描述
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结果
自定义函数式接口
  • 四大类型是java内置的接口,当我们并不需要前面介绍的接口,需要自己定义一个可以使用lambda表达式的接口怎么办呢?
  • 还是那句话:只要确保接口中有且仅有一个抽象方法即可
  • 语法格式
    修饰符 interface 接口名称 {
        public abstract 返回值类型 方法名称(可选参数信息);
        // 其他非抽象方法内容
    }
    
  • 示例代码
    package com.test01;
    
    public class Demo {
        // 主方法
        public static void main(String[] args) {
            invokeCalc(1, 2, (int a,int b)-> {return a+b;});
            invokeCalc(1, 2, (int a,int b)-> a-b);
            invokeCalc(1, 2, (int a,int b)-> {return a*b;});
            invokeCalc(1, 2, (int a,int b)-> {return a/b;});
            invokeCalc(1, 2, (int a,int b)-> {return a%b;});
            invokeCalc(1, 2, (int a,int b)-> {return a>b?a:b;});
        }
    
        // 定义接口方法
        public static void invokeCalc(int a, int b, Calculator calculator) {
            int result = calculator.calc(a, b);
            System.out.println("结果是:" + result);
        }
    }
    
    // 自定义接口
    interface Calculator {
        int calc(int a, int b);
    }
    

方法引用于构造器引用

在python等语言中,有语法糖的概念,它们也有自己的lambda表达式,比如在python中

# 获取arr列表中能被三整数的数
arr = [2, 18, 9, 22, 17, 24, 8, 12, 27]
print filter(lambda x: x % 3 == 0, foo)
[18, 9, 24, 12, 27]

在java中同样也有类似的写法,就是通过方法引用和构造器引用的方式,lambda是简化函数式接口的变量与形参赋值的语法,而方法引用和构造器引用是为了简化Lambda表达式

需要具备的条件

  • Lambda体只有一句语句,并且是通过调用一个对象或类的现有的方法来完成的
  • Lambda表达式的形参正好是给该方法的实参
  • 示例:
    // System.out对象,调用println()方法来完成Lambda体
    t->System.out.println(t)
    // Math类,调用random()静态方法来完成Lambda体
    () -> Math.random() 都是无参
    
方法引用

语法格式:

(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);
	}
构造器引用

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

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

语法格式:

类名::new
数组类型名::new

示例代码

public class TestMethodReference {
    @Test
	public void teset04() {
		Stream<Integer> stream = Stream.of(1,2,3);
		Stream<int[]> map = stream.map(int[]::new);
	}
	//这个方法是模仿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 test3(){
		/*
		 * 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 teset02() {
		Stream<String> stream = Stream.of("1.0","2.3","4.4");
		
//		Stream<BigDecimal> stream2 = stream.map(num -> new BigDecimal(num));
		
		Stream<BigDecimal> stream2 = stream.map(BigDecimal::new);
	}
	
	@Test
	public void test1(){
//		Supplier<String> s = () -> new String();//通过供给型接口,提供一个空字符串对象
		
		//构造器引用
		Supplier<String> s = String::new;//通过供给型接口,提供一个空字符串对象
	}

}

Stream

  • Stream 是 Java8 中处理集合的关键抽象概念,主要是为了,使用Stream API 对集合数据进行操作
  • 我们知道集合是为了存储数据,而Stream则是为了处理数据
  • Stream自己不会存储元素,它不会改变源对象,每次处理后会返回一个新的Stream,它的操作是延迟执行的,也就说它会等到需要结果时候才会执行

Stream操作步骤

  • 创建操作:通过一个数据源(例如集合、数组)获取一个流
  • 中间操作:对数据源的数据进行处理,可以执行多次,在终止操作前,不会真正执行
  • 终止操作:执行终止操作后,就会执行中间操作并将产生的结果保存到新的Stream对象中,然后结束Stream在这里插入图片描述

创建操作

1、创建 Stream方式一:通过集合

  • Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
    • public default Stream stream() : 返回一个顺序流
    • public default Stream parallelStream() : 返回一个并行流

2、创建 Stream方式二:通过数组

  • Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
    • public static <T> Stream<T> stream(T[] array): 返回一个流
      重载形式,能够处理对应基本类型的数组:
    • public static IntStream stream(int[] array):返回一个整型数据流
    • public static LongStream stream(long[] array):返回一个长整型数据流
    • public static DoubleStream stream(double[] array):返回一个浮点型数据流

3、创建 Stream方式三:通过Stream的of()

  • 可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
    • public static<T> Stream<T> of(T... values) : 返回一个顺序流

4、创建 Stream方式四:创建无限流

  • 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
    • public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f):返回一个无限流
    • public static<T> Stream<T> generate(Supplier<T> s) :返回一个无限流

示例代码:

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.junit.Test;

public class Test07StreamCreate {
	@Test
	public void test06(){
		/*
		 * Stream<T> iterate(T seed, UnaryOperator<T> f)  
		 * UnaryOperator接口,SAM接口,抽象方法:
		 * 
		 * UnaryOperator<T> extends Function<T,T>
		 * 		T apply(T t)
		 */
		Stream<Integer> stream = Stream.iterate(1, num -> num+=2);
//		stream = stream.limit(10);
		stream.forEach(System.out::println);
	}
	
	@Test
	public void test05(){
		Stream<Double> stream = Stream.generate(Math::random);
		stream.forEach(System.out::println);
	}
	
	@Test
	public void test04(){
		Stream<Integer> stream = Stream.of(1,2,3,4,5);
		stream.forEach(System.out::println);
	}
	
	@Test
	public void test03(){
		String[] arr = {"hello","world"};
		Stream<String> stream = Arrays.stream(arr);
	}
	
	@Test
	public void test02(){
		int[] arr = {1,2,3,4,5};
		IntStream stream = Arrays.stream(arr);
	}
	
	@Test
	public void test01(){
		List<Integer> list = Arrays.asList(1,2,3,4,5);
		
		//JDK1.8中,Collection系列集合增加了方法
		Stream<Integer> stream = list.stream();
	}
}

中间操作

中间操作的API

方法描述
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)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

示例代码:

import java.util.Arrays;
import java.util.stream.Stream;

import org.junit.Test;

public class Test08StreamMiddle {
	
	@Test
	public void test12(){
		String[] arr = {"hello","world","java"};
		Arrays.stream(arr)
			.flatMap(t -> Stream.of(t.split("|")))//Function<T,R>接口抽象方法 R apply(T t)  现在的R是一个Stream
			.forEach(System.out::println);
	}
	
	
	@Test
	public void test11(){
		String[] arr = {"hello","world","java"};
		
		Arrays.stream(arr)
			.map(t->t.toUpperCase())
			.forEach(System.out::println);
	}
	
	@Test
	public void test10(){
		Stream.of(1,2,3,4,5)
			.map(t -> t+=1)//Function<T,R>接口抽象方法 R apply(T t)
			.forEach(System.out::println);
	}
	
	@Test
	public void test09(){
		//希望能够找出前三个最大值,前三名最大的,不重复
		Stream.of(11,2,39,4,54,6,2,22,3,3,4,54,54)
			.distinct()
			.sorted((t1,t2) -> -Integer.compare(t1, t2))//Comparator接口  int compare(T t1, T t2)
			.limit(3)
			.forEach(System.out::println);
	}
	
	@Test
	public void test08(){
		long count = Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
			.distinct()
			.peek(System.out::println)  //Consumer接口的抽象方法  void accept(T t)
			.count();
		System.out.println("count="+count);
	}
	
	
	@Test
	public void test07(){
		Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
			.skip(5)
			.distinct()
			.filter(t -> t%3==0)
			.forEach(System.out::println);
	}

	@Test
	public void test06(){
		Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
			.skip(5)
			.forEach(System.out::println);
	}
	
	@Test
	public void test05(){
		Stream.of(1,2,2,3,3,4,4,5,2,3,4,5,6,7)
			.distinct()  //(1,2,3,4,5,6,7)
			.filter(t -> t%2!=0) //(1,3,5,7)
			.limit(3)
			.forEach(System.out::println);
	}
	
	
	@Test
	public void test04(){
		Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
			.limit(3)
			.forEach(System.out::println);
	}
	
	
	@Test
	public void test03(){
		Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
			.distinct()
			.forEach(System.out::println);
	}
	
	
	@Test
	public void test02(){
		Stream.of(1,2,3,4,5,6)
			.filter(t -> t%2==0)
			.forEach(System.out::println);
	}
	
	@Test
	public void test01(){
		//1、创建Stream
		Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
		
		//2、加工处理
		//过滤:filter(Predicate p)
		//把里面的偶数拿出来
		/*
		 * filter(Predicate p)
		 * Predicate是函数式接口,抽象方法:boolean test(T t)
		 */
		stream = stream.filter(t -> t%2==0);
		
		//3、终结操作:例如:遍历
		stream.forEach(System.out::println);
	}
}

终止操作

终止操作后,Stream就会自动销毁,因此,Stream的终止操作只能使用一次

方法描述
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch(Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional<T> findFirst()返回第一个元素
Optional<T> findAny()返回当前流中的任意元素
long count()返回流中元素总数
Optional<T> max(Comparator c)返回流中最大值
Optional<T> 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中元素做汇总的方法

示例代码:

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

import org.junit.Test;

public class Test09StreamEnding {
	
	@Test
	public void test14(){
		List<Integer> list = Stream.of(1,2,4,5,7,8)
				.filter(t -> t%2==0)
				.collect(Collectors.toList());
		
		System.out.println(list);
	}
	
	
	@Test
	public void test13(){
		Optional<Integer> max = Stream.of(1,2,4,5,7,8)
			 .reduce((t1,t2) -> t1>t2?t1:t2);//BinaryOperator接口   T apply(T t1, T t2)
		System.out.println(max);
	}
	
	@Test
	public void test12(){
		Integer reduce = Stream.of(1,2,4,5,7,8)
			 .reduce(0, (t1,t2) -> t1+t2);//BinaryOperator接口   T apply(T t1, T t2)
		System.out.println(reduce);
	}
	
	@Test
	public void test11(){
		Optional<Integer> max = Stream.of(1,2,4,5,7,8)
				.max((t1,t2) -> Integer.compare(t1, t2));
		System.out.println(max);
	}
	
	@Test
	public void test10(){
		Optional<Integer> opt = Stream.of(1,2,4,5,7,8)
				.filter(t -> t%3==0)
				.findFirst();
		System.out.println(opt);
	}
	
	@Test
	public void test09(){
		Optional<Integer> opt = Stream.of(1,2,3,4,5,7,9)
				.filter(t -> t%3==0)
				.findFirst();
		System.out.println(opt);
	}
	
	@Test
	public void test08(){
		Optional<Integer> opt = Stream.of(1,3,5,7,9).findFirst();
		System.out.println(opt);
	}
	
	@Test
	public void test04(){
		boolean result = Stream.of(1,3,5,7,9)
			.anyMatch(t -> t%2==0);
		System.out.println(result);
	}
	
	
	@Test
	public void test03(){
		boolean result = Stream.of(1,3,5,7,9)
			.allMatch(t -> t%2!=0);
		System.out.println(result);
	}
	
	@Test
	public void test02(){
		long count = Stream.of(1,2,3,4,5)
				.count();
		System.out.println("count = " + count);
	}
	
	@Test
	public void test01(){
		Stream.of(1,2,3,4,5)
				.forEach(System.out::println);
	}
}

总结

大数据基础知识的Java部分到今天就结束了,下一期我会制作一个JavaSE阶段的思维导图,并对整个内容做一个总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值