打怪升级之小白的大数据之旅(三十)
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
接口名 | 抽象方法 | 描述 |
| 接收一个对象用于完成功能 | |
| 接收两个对象用于完成功能 | |
| 接收一个double值 | |
| 接收一个int值 | |
| 接收一个long值 | |
| void accept(T t, double value) | 接收一个对象和一个double值 |
| void accept(T t, int value) | 接收一个对象和一个int值 |
| void accept(T t, long value) | 接收一个对象和一个long值 |
供给型接口
供给型接口的抽象方法特点:无参,但是有返回值
接口名 | 抽象方法 | 描述 |
| T get() | 返回一个对象 |
| getAsBoolean() | 返回一个boolean值 |
| getAsDouble() | 返回一个double值 |
| 返回一个int值 | |
| getAsLong() | 返回一个long值 |
判断型接口
判断型接口的抽象方法特点:有参,但是返回值类型是boolean结果
接口名 | 抽象方法 | 描述 |
| boolean test(T t) | 接收一个对象 |
| boolean test(T t, U u) | 接收两个对象 |
| test(double value) | 接收一个double值 |
| test(int value) | 接收一个int值 |
| test(long value) | 接收一个long值 |
功能型接口
功能型接口的抽象方法特点:既有参数又有返回值
接口名 | 抽象方法 | 描述 |
Function<T,R> | R apply(T t) | 接收一个T类型对象,返回一个R类型对象结果 |
UnaryOperator | T apply(T t) | 接收一个T类型对象,返回一个T类型对象结果 |
DoubleFunction | R apply(double value) | 接收一个double值,返回一个R类型对象 |
IntFunction | R apply(int value) | 接收一个int值,返回一个R类型对象 |
LongFunction | R apply(long value) | 接收一个long值,返回一个R类型对象 |
ToDoubleFunction | double applyAsDouble(T value) | 接收一个T类型对象,返回一个double |
ToIntFunction | int applyAsInt(T value) | 接收一个T类型对象,返回一个int |
ToLongFunction | long applyAsLong(T value) | 接收一个T类型对象,返回一个long |
DoubleToIntFunction | int applyAsInt(double value) | 接收一个double值,返回一个int结果 |
DoubleToLongFunction | long applyAsLong(double value) | 接收一个double值,返回一个long结果 |
IntToDoubleFunction | double applyAsDouble(int value) | 接收一个int值,返回一个double结果 |
IntToLongFunction | long applyAsLong(int value) | 接收一个int值,返回一个long结果 |
LongToDoubleFunction | double applyAsDouble(long value) | 接收一个long值,返回一个double结果 |
LongToIntFunction | int applyAsInt(long value) | 接收一个long值,返回一个int结果 |
DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个double值,返回一个double |
IntUnaryOperator | int applyAsInt(int operand) | 接收一个int值,返回一个int结果 |
LongUnaryOperator | long applyAsLong(long operand) | 接收一个long值,返回一个long结果 |
BiFunction<T,U,R> | R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 |
BinaryOperator | T 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 |
DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个double值,返回一个double结果 |
IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个int值,返回一个int结果 |
LongBinaryOperator | long 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的终止操作只能使用一次
方法 | 描述 |
| 检查是否匹配所有元素 |
| 检查是否至少匹配一个元素 |
| 检查是否没有匹配所有元素 |
| 返回第一个元素 |
| 返回当前流中的任意元素 |
| 返回流中元素总数 |
| 返回流中最大值 |
| 返回流中最小值 |
| 迭代 |
| 可以将流中元素反复结合起来,得到一个值。返回 T |
| 可以将流中元素反复结合起来,得到一个值。返回 Optional |
| 将流转换为其他形式。接收一个 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阶段的思维导图,并对整个内容做一个总结