之前写了一篇博客简单介绍了一下java 8发布新增的一些特性功能,java 8在2014年发布,距今也不少年了,但是lambda表达式使用并不熟练,现在一边学习,一边记录一下。
目录
一、Lambda表达式
二、方法引用
三、stream API使用
是java8新增的最重要的新功能之一。使用Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达式可以替代只有一个抽象方法的接口(函数式接口)实现,告别匿名内部类,代码看起来更简洁易懂。Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作。
Lambda表达式特点
1:函数式编程
2:参数类型自动推断
3:代码量少,简洁
学习Lambda表达式需要熟悉java泛型,平时有意识地使用lambda表达式,学习java中固有的一些函数式接口,多用stream API。
函数式接口(函数式接口都可以使用Lambda表达式):只有一个抽象方法的接口
注意:如果自定义的接口使用Object类中的方法作为抽象方法,则该接口不是函数式接口。例如在接口中定义一个抽象方法:public int hashCode();
在java8中可以用@FuctionalInterface注解来验证函数式接口。
常用的函数式接口:
Runnable接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Runnable接口是函数式接口因此可以使用lambda表达式。
Callable接口:
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Comparator接口:
还有jdk中其他常用的函数式接口:
在java.util.function包下的类几乎都是函数式接口:
其中里面常用的接口有
Supplier接口:可以表示为一个输出(获取一个结果),返回T类型
@FunctionalInterface
public interface Supplier {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
Consumer:可以表示为一个输入(操作一个输入的参数)
@FunctionalInterface
public interface Consumer {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer andThen(Consumer after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
BiConsumer:可以表示为两个输入(有两个输入参数T和U)
@FunctionalInterface
public interface BiConsumer {
/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
/**
* Returns a composed {@code BiConsumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code BiConsumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default BiConsumer andThen(BiConsumer after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
Function接口:可以表示为通过一个输入参数,获取一个输出(即一个输入,一个输出),传入一个T类型,获取一个R类型数据
@FunctionalInterface
public interface Function {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default Function compose(Function before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default Function andThen(Function after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static Function identity() {
return t -> t;
}
}
UnaryOperator接口:该接口继承了Funcition接口,可以表示为输入一个参数,获取一个输出数据(不同的是,输入和输出的类型相同了,都是T类型)
@FunctionalInterface
public interface UnaryOperator extends Function {
/**
* Returns a unary operator that always returns its input argument.
*
* @param the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static UnaryOperator identity() {
return t -> t;
}
}
BiFunction接口:表示两个输入(输入类型不同),一个输出(输入和输出类型都不同)
@FunctionalInterface
public interface BiFunction {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default BiFunction andThen(Function after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BinaryOperator接口:传入两个参数类型(输入),获取返回一个结果(输出),输入和输出类型相同
@FunctionalInterface
public interface BinaryOperator extends BiFunction {
/**
* Returns a {@link BinaryOperator} which returns the lesser of two elements
* according to the specified {@code Comparator}.
*
* @param the type of the input arguments of the comparator
* @param comparator a {@code Comparator} for comparing the two values
* @return a {@code BinaryOperator} which returns the lesser of its operands,
* according to the supplied {@code Comparator}
* @throws NullPointerException if the argument is null
*/
public static BinaryOperator minBy(Comparator comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/**
* Returns a {@link BinaryOperator} which returns the greater of two elements
* according to the specified {@code Comparator}.
*
* @param the type of the input arguments of the comparator
* @param comparator a {@code Comparator} for comparing the two values
* @return a {@code BinaryOperator} which returns the greater of its operands,
* according to the supplied {@code Comparator}
* @throws NullPointerException if the argument is null
*/
public static BinaryOperator maxBy(Comparator comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
其他的在这个包下还有一些接口名称带有类型的,可以顾名思义它的作用,如DoubleConsumer接口,则表示输入的参数类型为double类型,其他的类似,可以参考源码理解
@FunctionalInterface
public interface DoubleConsumer {
/**
* Performs this operation on the given argument.
*
* @param value the input argument
*/
void accept(double value);
/**
* Returns a composed {@code DoubleConsumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code DoubleConsumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default DoubleConsumer andThen(DoubleConsumer after) {
Objects.requireNonNull(after);
return (double t) -> { accept(t); after.accept(t); };
}
}
labmda表达式语法,只有函数式接口可以使用lambda表达式(正因为函数式接口中只有一个抽象方法,所以lambda表达式不用写方法名,可以推断出来),lambda表达式是对象,是一个函数式接口的实例。
lambda表达式语法:LambdaParameter -> LambdaBody
例如 Runnable r=() -> System.out.println();
args -> expr
对应
(object... args)-> {函数式接口抽象方法的实现逻辑}
左边括号里边的参数个数,根据函数式接口里面抽象方法的参数个数决定。
当只有一个参数的时候,()可以省略,当方法体逻辑很简单的时候,{}和return 也可以省略
Lambda表达式示例:
() -> {} // 无参,无返回值
() -> { System.out.println(1); } // 无参,无返回值
() -> System.out.println(1) // 无参,无返回值(上面的简写)
() -> { return 100; } // 无参,有返回值
() -> 100 // 无参,有返回值(上面的简写)
() -> null // 无参,有返回值(返回null)
(int x) -> { return x+1; } // 单个参数(可以写出参数类型,不写类型,会自动推断,因此可以写成下边的形式)有返回值
(int x) -> x+1 // 单个参数,有返回值(上面的简写,省略了return)
(x) -> x+1 // 单个参数,有返回值(不指定参数类型,因为可以根据接口中的抽象方法参数类型推断出来,多个参数必须用括号)
x -> x+1 // 单个参数,有返回值(不指定参数类型)
注意事项:
多个参数类型时,要加上(),并且参数类型不能只省略一部分,
如:(x,int y)-> x+y
参数类型不能使用final修饰符,
如:(x, final y)-> x+y
不能将lambda表达式赋给一个非函数式接口
如:Object obj=()-> "hello"
可以将lambda表达式强转为一个有返回值的函数式接口
Object obj = (Supplier)() -> "hello"
不需要也不允许使用throws语句来声明它可能会抛出的异常
实例演示:
package com.xiaomifeng1010.rbacboot.common.util;
import org.apache.commons.lang3.StringUtils;
import java.io.Closeable;
import java.util.Observer;
import java.util.concurrent.Callable;
import java.util.function.*;
/**
* @author xiaomifeng1010
* @version 1.0
* @date: 2020/3/19 18:52
*/
public class LambdaExpression {
public static void main(String[] args) throws Exception {
// 无参无返回值接口Runnable,如果不使用lambda表达式,则是下边这样的
Runnable runnable2=new Runnable() {
@Override
public void run() {
}
};
// 无参无返回值接口Runnable,使用lambda表达式,效果等同上边的
Runnable runnable=() -> {};
// 如果方法体中只有一行逻辑实现的语句,大括号可以省略
Runnable r=() -> System.out.println("无参无返回值的接口Runnable");
Closeable closeable = () -> System.out.println("closeable也是无参无返回值");
closeable.close();
// 无参有返回值接口Callable,如果不使用lambda表达式
Callable callable=new Callable () {
@Override
public String call() {
return "call方法";
}
};
//无参有返回值接口Callable,使用lambda表达式,效果等同上边的
Callable callable2 = () -> {
return "call方法";
};
// 因为方法体中只有一句返回语句,可以省略大括号和return
Callable callable3 = () -> "call方法";
System.out.println(callable.call());
System.out.println(callable2.call());
System.out.println(callable3.call());
// 有参无返回值自定义接口UserMapper,不使用lambda表达式时
UserMapper userMapper=new UserMapper() {
@Override
public void insert(User user) {
System.out.println("插入一条数据");
}
};
// 有参无返回值,使用lambda表达式时
UserMapper userMapper2=(User user) -> System.out.println("插入一条数据");
//只有一个参数时,参数括号也可以省略,类型也可以省略(会自动推断)
UserMapper userMapper3= user -> System.out.println("插入一条数据");
// 有参有返回值的自定义接口OrderMapper,不使用lambda表达式
OrderMapper orderMapper=new OrderMapper() {
@Override
public int insert(Order order) {
return 0;
}
};
// 有参有返回值的自定义接口OrderMapper,使用lambda表达式(只有一个参数可省略括号和类型,方法体简单,只有一行可省略
// 大括号和return
OrderMapper orderMapper2=order -> 0;
System.out.println("orderMapper插入了"+orderMapper.insert(new Order())+"行数据");
System.out.println("orderMapper2插入了"+orderMapper2.insert(new Order())+"行数据");
// Function接口的使用示例,输入a是Integer类型,返回类型是String类型
Function function=a -> "function接口返回值";
// Function接口的使用示例,输入a是Integer类型,返回类型是String类型,由方法获取String.valueOf(b)返回String类型
Function function2 = b -> String.valueOf(b);
System.out.println("function接口传入10,返回:"+function.apply(10));
System.out.println("function2接口传入10,返回:"+function.apply(10));
// BiFunction输入两个不同类型参数(当然也可以相同类型参数),输出一个类型数据
BiFunction biFunction=(a,b) -> String.valueOf(a)+String.valueOf(b);
System.out.println("biFunction传入a,b值后:"+biFunction.apply(10,20.56));
// BinaryOperator输入的两个相同类型参数,输出相同类型的数据类型
BinaryOperator binaryOperator=(a,b) -> 10;
System.out.println("binaryOperator输入a和b之后:"+binaryOperator.apply(5,6));
// Supplier接口获取一个输出
Supplier supplier = () -> "supplier";
System.out.println("supplier不需要传入参数,就可以获取一个数据:"+supplier.get());
// Consumer输入一个类型参数,无返回值
Consumer consumer = (String s) -> {
System.out.println("consumer");
};
// 简写为:
Consumer consumer2 = s-> System.out.println("consumer");
// 注意函数式接口的方法中有返回值的,使用lambda表达式方法体中必须要有返回值,且返回值类型要相同,而函数式接口抽象方法
// 没有返回值的,使用lambda表达式的方法体可以使用有返回值的方法。例如Runnable接口的抽象方法run()没有返回值;
// Integer.parseInt()会返回int类型
Runnable runnable4 =() -> Integer.parseInt("10");
// 但是直接写10,是不行的
// Runnable runnable1 = () -> 10;
// 说明:为什么可以直接用方法,而不能直接使用int值,因为虽然Integer.parseInt("10")会返回一个int值,但是run
// 方法是无返回值的,所以调用Integer.parseInt("10")可以只是用他的方法逻辑,不用返回值,但是直接写10,就是直接给了
// 一个返回值,是不可以的
}
}
/**
* 自定义一个接口
*/
@FunctionalInterface
interface UserMapper{
void insert(User user);
}
@FunctionalInterface
interface OrderMapper{
int insert(Order order);
}
class User{
}
class Order {
}
写lambda表达式:
1.看抽象方法参数;2,看返回类型
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用(因为如果方法体还需要写其他逻辑,就不能直接使用方法引用)。
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符“::” 将方法名和对象或类的名字分隔开来。
如下四种主要使用情况:
类型 |
语法 |
对应的lambda表达式 |
静态方法引用 |
类名::staticMethod |
(args) -> 类名.staticMethod(args) |
实例方法引用 |
inst::instMethod |
(args) -> inst.instMethod(args) |
对象方法引用 |
类名::instMethod |
(inst,args) ->类名.instMethod(args) |
构造方法引用 |
类名::new |
(args) -> new 类名(args) |
如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用;
如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用;
抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用;
如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用。
实例演示:
package com.xiaomifeng1010.rbacboot.common.util;
import java.util.function.*;
/**
* @author xiaomifeng1010
* @version 1.0
* @date: 2020/3/19 21:32
*/
public class MethodReference {
public static void main(String[] args) {
// 静态方法引用实例
BinaryOperator binaryOperator=(a,b) -> Math.pow(a,b);
// Math.pow(a,b)方法需要两个输入参数,返回一个数据,且输入参数类型和返回值类型相同,完全符合BinaryOperator
// 接口特点
BinaryOperator binaryOperator2=Math::pow;
// 构造方法引用
// Supplier接口的get()方法不需要输入参数,返回一个值(输出),可以用
// String类(注意这里的String类不是lang包下的类,而是com.sun.org.apache.xpath.internal.operations.String)的构造方法实现
Supplier supplier=() -> new String();
Supplier supplier2=String::new;
// 类型也可以使用数组类型
Function function6=integer -> new Integer[integer];
Function function7=Integer[]::new;
// 实例方法引用
MethodReference mr = new MethodReference();
Function function3=str -> mr.toUpper(str);
// 或者直接简写为(下边这种写法不用写MethodReference mr = new MethodReference()
Function function5=new MethodReference()::toUpper;
// 例子2,较特殊
Function function=str -> str.toUpperCase();
// 方法引用写法,因为toUpperCase()方法不是静态方法,但是直接使用String引用toUpperCase()方法
Function function2=String::toUpperCase;
// 对象方法引用实例
// 抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。
// 如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用
// lambda表达式方法体有现成方法实现时,lambda表达式完全写法(方法体中用类名.实例方法)
Consumer consumer=(Too too) -> new Too().too();
Consumer consumer2=Too::too;
// 接口中有两个参数的情况,BiConsumer接口接收两个输入参数,无返回值
BiConsumer biConsumer=(Too too2,String str) -> new Too().top(str);
BiConsumer biConsumer2=Too::top;
// 接口中三个参数类型
BiFunction biFunction=(Too too2,String str) -> new Too().tos(str);
BiFunction biFunction2=Too::tos;
// 说明:如果接口中的抽象方法中没有输入参数,不能使用对象方法引用
}
public String toUpper(String str){
return str.toUpperCase();
}
}
class Too{
public void too(){
}
public void top(String str){
}
public String tos(String str){
return str;
}
}
Stream是一组用来处理数组、集合的API
Stream特性
1:不是数据结构,没有内部存储
2:不支持索引访问
3:延迟计算
4:支持并行
5:很容易生成数组或集合(List,Set)
6:支持过滤,查找,转换,汇总,聚合等操作
Stream运行机制
Stream分为 源source,中间操作,终止操作
流的源可以是一个数组、一个集合、一个生成器方法,一个I/O通道等等。
一个流可以有零个和或者多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用。一个流只会有一个终止操作。
Stream只有遇到终止操作,它的源才开始执行遍历操作。
Stream常用API
中间操作:
过滤 filter
去重 distinct
排序 sorted
截取 limit、skip
转换 map/flatMap
其他 peek
终止操作:
循环 forEach
计算 min、max、count、 average
匹配 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny
汇聚 reduce
收集器 toArray collect
Stream的创建
1、通过数组
2、通过集合来
3、通过Stream.generate方法来创建
4、通过Stream.iterate方法来创建
5、其他API创建
实例演示:
package com.xiaomifeng1010.rbacboot.common.util;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @author xiaomifeng1010
* @version 1.0
* @date: 2020/3/20 0:03
*/
public class StreamAPI {
/**
* 通过数组创建Stream
*/
static void generate1(){
String letter="a,b,c,d,e,f,g";
String[] array=letter.split(",");
Stream stream=Stream.of(array);
stream.forEach(x -> System.out.println("generate1方法运行后:"+x));
}
/**
* 通过集合创建Steam
*/
static void generate2() {
List list= Arrays.asList("a","b","c","d");
Stream stream=list.stream();
stream.forEach(x -> System.out.println("依次输出:"+x));
}
/**
* 通过Steam的静态方法generate()方法
*/
static void generate3() {
// generate()方法中参数类型是函数式接口Supplier,产生一个无止境的steam流
Stream stream= Stream.generate(()->1);
// 会死循环(会一直调用supplier接口的get方法)
// stream.forEach(x -> System.out.println("generate3方法运行后:"+x));
// 因此为了避免死循环,可以在终止操作前使用中间操作limit截断流
stream.limit(10).forEach(x -> System.out.println("generate3方法运行后只取10个元素:"+x));
}
/**
* 通过Steam的静态方法iterate()方法
*/
static void generate4(){
// iterate()方法,第一个参数是初始元素,第二个参数类型是函数式接口UnaryOperator(输入和输出类型相同)
// UnaryOperator输入的x后,一直累加1,无限制累加1循环,生成无止境steam流,初始值为1,然后一直往后加1
Stream stream= Stream.iterate(1,x -> x+1);
// 会死循环(会一直调用iterate不停迭代)
// stream.forEach(x -> System.out.println("generate4方法运行后:"+x));
// 因此为了避免死循环,可以在终止操作前使用中间操作limit截断流,取前十个数
stream.limit(10).forEach(x -> System.out.println("generate4方法运行后取前十位数:"+x));
}
/**
* 通过String类的chars()方法创建
*/
static void generate5(){
String letters="abcd";
IntStream stream=letters.chars();
// 流的中间操作步骤可以有多步,或O部,为0则没有中间操作,可以直接使用终止操作,如直接操作stream的终止操作forEach:
// stream.forEach(x -> System.out.println(x));
// 可以使用方法引用,会逐个输出a,b,c,d的ASCII码值(int类型)
stream.forEach(System.out::println);
}
public static void main(String[] args) {
// 第一种和第二种创建流的方式使用较多
// generate1();
// generate2();
// generate3();
// generate4();
// generate5();
// 演示stream中间操作,但是在终止操作前会延迟计算,并不会真正计算,需要终止操作才能进行计算
// Arrays.asList(1,2,3,4,5).stream().filter(x -> x%2==0);
// 加上终止操作(过滤掉其他的数字,只保留偶数输出),filter()方法中参数是函数式接口Predicate,
// boolean test(T t);
Arrays.asList(1,2,3,4,5).stream().filter(x -> x%2==0).forEach(System.out::println);
// 过滤出来满足偶数的2,4,6的和,有返回值,可以终止
int sum= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).mapToInt(x -> x).sum();
System.out.println("和为:"+sum);
// 终止操作求最大值6
int max=Arrays.asList(1,2,3,4,5,6).stream().max((a,b) -> a-b).get();
System.out.println("最大值:"+max);
// 查找满足过滤条件(偶数)中任一个
int anyone= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).findAny().get();
System.out.println("查找满足过滤条件的任一个数据:"+anyone);
// 查找满足过滤条件(偶数)的第一个(按排序从大到小 comparator中方法体用第二个参数减第一个参数)
int findFirst= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).sorted((a,b) -> b-a).findFirst().get();
System.out.println("查找满足过滤条件的第一个数据:"+findFirst);
// 根据字符串长短排序(长度从小到大排序,并迭代输出)
Arrays.asList("com","xiaomifeng1010","cn","admin").stream().sorted((a,b) -> a.length()-b.length()).forEach(System.out::println);
// 从1-50里面的所有偶数查找出来,存放到一个list中
List list=Stream.iterate(1,x -> x+1).limit(50).filter(x -> x%2==0).collect(Collectors.toList());
System.out.println(list);
// 去重操作
Arrays.asList(1,3,4,5,6,3,5,7).stream().distinct().forEach(System.out::println);
// 去重操作,将stream流转换成set集合类型(达到去重目的)
Set set= Arrays.asList(1,3,4,5,6,3,5,7).stream().collect(Collectors.toSet());
System.out.println("set集合元素:"+set);
// 产生1-50数据,分页效果(从大到小排序后,跳过10个数据,放在list中)
List list2= Stream.iterate(1,x -> x+1 ).limit(50).sorted((a,b) -> b-a).skip(10).collect(Collectors.toList());
System.out.println("从50跳过10个数据后:"+list2);
// 转换操作
String str="11,22,33,44,55";
// int sum2=Stream.of(str.split(",")).mapToInt(x -> Integer.parseInt(x)).sum();
// 写成方法引用
int sum2=Stream.of(str.split(",")).mapToInt(Integer::parseInt).sum();
System.out.println("字符串转换为int后求和:"+sum2);
// 字符串对象,转换为Product对象
String productName="iphone,ipad,macbook,flashlight";
// Stream.of(productName.split(",")).map(x -> new Product(x)).forEach(System.out:: println);
// 方法引用
Stream.of(productName.split(",")).map(Product::new).forEach(System.out:: println);
// 演示peek操作,peek 操作接收的是一个 Consumer 函数。顾名思义 peek 操作会按照 Consumer
// 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性
Stream.of(productName.split(",")).peek(x -> x="hello"+x).forEach(System.out::println);
// 当前的线程是主线程main,在peek方法中会输出main线程(串行方式,一直由main方法执行,即单线程)
// 由于stream流支持并行,使用parallel(中间操作)开启并行(多个线程执行)
// Optional max2=Stream.iterate(1,x -> x+1).limit(200).peek(x -> System.out.println(Thread.currentThread().getName()))
// .parallel().max(Integer::compare);
// System.out.println(max);
// 并行流转换成串行流 (使用sequential()方法)
// Optional max3=Stream.iterate(1,x -> x+1).limit(200).parallel().peek(x -> System.out.println(Thread.currentThread().getName()))
// .sequential().max(Integer::compare);
// System.out.println(max);
//
// }
}
class Product{
private String name;
public Product(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
'}';
}
}
控制台输出结果:
单元测试:实战应用
package com.xiaomifeng1010.rbacboot.common.util;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Test;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author xiaomifeng1010
* @version 1.0
* @date: 2020/3/21 12:47
*/
public class Java8LambdaTest {
/**
* 将请求的地址url中参数和值取出来存放到map中
* url:http:localhost:8080/index.do?itemId=1&userId=1000&type=2&token=134jojmgoijeo4&key=index
*/
@Test
public void test1() {
String queryString ="itemId=1&userId=1000&type=2&token=134jojmgoijeo4&key=index";
// 将字符串queryString按照&分割为数组,并创建stream流
Map params= Stream.of(queryString.split("&"))
// 转换为(返回包含输出结果类型元素的stream,进一步按照等号分割的数组,保存在stream中)
.map(str -> str.split("="))
// collect方法收集,内部Collectors.toMap()转换为map类型
// toMap()方法中的参数为两个Function接口(第一个参数Function接口是获取map的key,
// 第一个参数Function接口是获取map的value
// Function接口中输入参数s是数组,输出是数组元素,key是数组第一个元素,value是数组第二个元素
// 因为是元素依次输入,所以其实每次的数组都是只有两个元素
.collect(Collectors.toMap(s -> s[0],s -> s[1]));
System.out.println(params);
}
@Test
public void test2 () {
// 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有id,在存放到list中
List ids = ComputerBooks().stream().map( ComputerBook -> ComputerBook.getId()).collect(Collectors.toList());
System.out.println(ids);
// 方法引用ComputerBook::getId
ids = ComputerBooks().stream().map(ComputerBook::getId).collect(Collectors.toList());
System.out.println(ids);
// 获取ComputerBook对象所有id,然后以逗号方式连接拼接成字符串
String str = ComputerBooks().stream().map(ComputerBook -> ComputerBook.getId()+"").collect(Collectors.joining(","));
System.out.println(str);
// 获取ComputerBook对象所有id,然后以逗号方式连接拼接成字符串,并且以括号包裹
str = ComputerBooks().stream().map(ComputerBook -> ComputerBook.getId()+"").collect(Collectors.joining(",", "(", ")"));
System.out.println(str);
str = ComputerBooks().stream().map(ComputerBook -> "'"+ComputerBook.getId()+"'").collect(Collectors.joining(",", "(", ")"));
System.out.println(str);
}
@Test
public void test3 () {
// 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有类型,在存放到list中
// list中的泛型类型取决于map()方法转换输出类型(如本例中getType()方法)
List list = ComputerBooks().stream().map(ComputerBook::getType).collect(Collectors.toList());
System.out.println(list);
// 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有类型(不重复的),在存放到list中
list = ComputerBooks().stream().map(ComputerBook::getType).distinct().collect(Collectors.toList());
System.out.println(list);
// 上边的也可以通过存放在set达到去重
Set set = ComputerBooks().stream().map(ComputerBook::getType).collect(Collectors.toSet());
System.out.println(set);
}
@Test
public void test4 () {
// 以ComputerBook类型的价格排序,价格由低到高排序
// ComputerBooks().stream().sorted((ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice())).forEach(System.out::println);;
// Comparator compa = (ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice());
// 以ComputerBook类型的价格排序,价格由高到低排序(之前的排序规则反转一下)
// ComputerBooks().stream().sorted(compa.reversed()).forEach(System.out::println);
// 价格相同情况下,再比较出版日期
// Comparator compa = (ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice());
// ComputerBooks().stream().sorted(compa.thenComparing((ComputerBook1,ComputerBook2) -> ComputerBook1.getPublishDate().isAfter(ComputerBook2.getPublishDate()) ? -1 : 1)).forEach(System.out::println);
// ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice)).forEach(System.out::println);
// ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice).reversed()).forEach(System.out::println);
ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice).reversed().thenComparing(Comparator.comparing(ComputerBook::getPublishDate).reversed())).forEach(System.out::println);
}
@Test
public void test5 () {
// 将ComputerBook的id作为key,ComputerBook作为value存放到map中
// Map ComputerBooksMap = ComputerBooks().stream().collect(Collectors.toMap(ComputerBook -> ComputerBook.getId(), ComputerBook -> ComputerBook));
// System.out.println(ComputerBooksMap);
Map ComputerBooksMap = ComputerBooks().stream().collect(Collectors.toMap(ComputerBook::getId, ComputerBook -> ComputerBook));
System.out.println(ComputerBooksMap);
}
@Test
public void test6 () {
// 统计平均价格(ComputerBook::getPrice对象方法引用)
Double avg = ComputerBooks().stream().collect(Collectors.averagingDouble(ComputerBook::getPrice));
System.out.println(avg);
}
@Test
public void test7 () {
// 最高价的图书
Optional computerBook = ComputerBooks().stream().collect(Collectors.maxBy(Comparator.comparing(ComputerBook::getPrice)));
System.out.println(computerBook);
// 最低价的图书
computerBook = ComputerBooks().stream().collect(Collectors.minBy(Comparator.comparing(ComputerBook::getPrice)));
System.out.println(computerBook);
// 出版日期最早的书
computerBook = ComputerBooks().stream().collect(Collectors.minBy(Comparator.comparing(ComputerBook::getPublishDate)));
System.out.println(computerBook);
// 出版日期最晚的书
computerBook = ComputerBooks().stream().collect(Collectors.maxBy(Comparator.comparing(ComputerBook::getPublishDate)));
System.out.println(computerBook);
// 价格最高且日期最晚出版的图书(例子中有两本150的图书)
Comparator comp = Comparator.comparing(ComputerBook::getPrice);
computerBook = ComputerBooks().stream().collect(Collectors.maxBy(comp.thenComparing(Comparator.comparing(ComputerBook::getPublishDate))));
System.out.println(computerBook);
}
@Test
public void test8 () {
// 分组统计(按照类型),map中key为类型type
// Map> ComputerBooksMap = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType));
// ComputerBooksMap.keySet().forEach(key -> {
// System.out.println(key);
// System.out.println(ComputerBooksMap.get(key));
// System.out.println("---------------------");
// });
// 按照类型分组统计,并计算每种类型的数量
// Map ComputerBooksCount = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.counting()));
// System.out.println(ComputerBooksCount);
// 按照类型分组统计,并计算每种类型图书的价格合计
// Map ComputerBooksSum = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.summingDouble(ComputerBook::getPrice)));
// System.out.println(ComputerBooksSum);
// 按照类型分组统计,并计算每种类型图书的平均价格
// Map ComputerBooksSum = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.averagingDouble(ComputerBook::getPrice)));
// System.out.println(ComputerBooksSum);
// 按照类型分组统计,并计算每种类型图书价格的最大值
// Map> ComputerBooksMaxPrice = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.maxBy(Comparator.comparing(ComputerBook::getPrice))));
// System.out.println(ComputerBooksMaxPrice);
// 按照类型分组统计,并计算每种类型图书价格的最小值
// Map> ComputerBooksMinPrice = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.minBy(Comparator.comparing(ComputerBook::getPrice))));
// System.out.println(ComputerBooksMinPrice);
// 每种类型出版时间最晚的
Map> ComputerBooksMaxPubDate = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.maxBy(Comparator.comparing(ComputerBook::getPublishDate))));
System.out.println(ComputerBooksMaxPubDate);
}
@Test
public void test9 () {
// 取出价格80元及以上的图书,按照出版日期从大到小排序
ComputerBooks().stream().filter(ComputerBook -> ComputerBook.getPrice() >= 80).sorted(Comparator.comparing(ComputerBook::getPublishDate).reversed()).forEach(System.out::println);;
}
private List ComputerBooks(){
List books = new ArrayList<>();
books.add(new ComputerBook(1, "tomcat", 70d, "服务器", LocalDate.parse("2014-05-17")));
books.add(new ComputerBook(2, "jetty", 60d, "服务器", LocalDate.parse("2015-12-01")));
books.add(new ComputerBook(3, "nginx", 65d, "服务器", LocalDate.parse("2016-10-17")));
books.add(new ComputerBook(4, "java", 66d, "编程语言", LocalDate.parse("2011-04-09")));
books.add(new ComputerBook(5, "ruby", 80d, "编程语言", LocalDate.parse("2013-05-09")));
books.add(new ComputerBook(6, "php", 40d, "编程语言", LocalDate.parse("2014-08-06")));
books.add(new ComputerBook(7, "html", 44d, "编程语言", LocalDate.parse("2011-01-06")));
books.add(new ComputerBook(8, "oracle", 150d, "数据库", LocalDate.parse("2013-08-09")));
books.add(new ComputerBook(9, "mysql", 66d, "数据库", LocalDate.parse("2015-04-06")));
books.add(new ComputerBook(10, "ssh", 70d, "编程语言", LocalDate.parse("2016-12-04")));
books.add(new ComputerBook(11, "设计模式", 81d, "其他", LocalDate.parse("2017-04-06")));
books.add(new ComputerBook(12, "重构", 62d, "其他", LocalDate.parse("2012-04-09")));
books.add(new ComputerBook(13, "敏捷开发", 72d, "其他", LocalDate.parse("2016-09-07")));
books.add(new ComputerBook(14, "从技术到管理", 42d, "其他", LocalDate.parse("2016-02-19")));
books.add(new ComputerBook(15, "算法导论", 66d, "其他", LocalDate.parse("2010-05-08")));
books.add(new ComputerBook(16, "oracle 12c", 150d, "数据库", LocalDate.parse("2017-05-08")));
return books;
}
}
@Data
@AllArgsConstructor
class ComputerBook {
private int id;
private String name;
private double price;
private String type;
private LocalDate publishDate;
}
@Data和@AllArgsConstrucor注解需要引入lombok依赖,以及idea中安装lombok插件。