文章目录
一、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)
接口名 | 抽象方法 | 描述 |
---|---|---|
Consumer | 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 | void accept(T t, double value) | 接收一个对象和一个double值 |
ObjIntConsumer | void accept(T t, int value) | 接收一个对象和一个int值 |
ObjLongConsumer | void accept(T t, long value) | 接收一个对象和一个long值 |
3.2、供给型接口:无参有返回值
这类接口的抽象方法特点:无参,但是有返回值
经典代表
-
Supplier<T> 它的抽象方法 T get()
接口名 | 抽象方法 | 描述 |
---|---|---|
Supplier | T get() | 返回一个对象 |
BooleanSupplier | boolean getAsBoolean() | 返回一个boolean值 |
DoubleSupplier | double getAsDouble() | 返回一个double值 |
IntSupplier | int getAsInt() | 返回一个int值 |
LongSupplier | long getAsLong() | 返回一个long值 |
3.3、判断型接口:有参有返回值,但是返回值类型是boolean类型
这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。
经典代表
-
Predicate<T> 抽象方法 boolean test(T t)
接口名 | 抽象方法 | 描述 |
---|---|---|
Predicate | boolean test(T t) | 接收一个对象 |
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个对象 |
DoublePredicate | boolean test(double value) | 接收一个double值 |
IntPredicate | boolean test(int value) | 接收一个int值 |
LongPredicate | boolean 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类型对象结果 |
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结果 |
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);
}
}