Lambda And Stream
一、函数式编程
面向对象编程是对数据进行抽象,函数式编程是对行为进行抽象,能编写出易读的代码,便于维护、可靠性高
1.函数式接口
标椎@FunctionalInterface
注解的接口,接口中只能有一个抽象方法(覆盖
java.lang.Object的公共方法的抽象方法不计入),但可以有多个默认实现
//标记注解 如果接口不满足条件会在编译时报错
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
eg.
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//自定义接口
@FunctionalInterface
interface A {
void f1();
//默认实现
default void f2() {
System.err.println("111");
}
}
//匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("r1 run...");
}
};
//lambda表达式
//Runnable r2 = () -> System.out.println("r2 run...");
//可以进行强制类型转换
Object r2 = (Runnable) () -> System.out.println("r2 run...");
Thread t1 = new Thread(r1);
//Thread t2 = new Thread(r2);
Thread t2 = new Thread((Runnable) r2);
2.Lambda表达式写法
@FunctionalInterface
interface B {
int doubleNum(int number);
}
B b1 = (number) -> number * 2;
//一个参数可省略小括号 方法体只有一行 可以省略return
B b2 = number -> number * 2;
//参数可以声明类型
B b3 = (int number) -> number * 2;
//多行方法体需要用大括号包裹
B b4 = (int number) -> {
System.out.println("doubleNum()...");
return number * 2;
};
3.常用函数接口
Function
//一个参数一个返回值 T ---> 参数类型 R ---> 返回值
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
...
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Function<String, String> function = (str) -> {
System.out.println("请输入一个字符串:");
return scanner.nextLine();
};
System.out.println("你输入的是:" + function.apply("1"));
}
/*
请输入一个字符串:
dssad
你输入的是:dssad
*/
Predicate
//一个参数 返回值为布尔类型 T ---> 参数类型 返回值为boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
...
}
public static void main(String[] args) {
Predicate<String> predicate = (str) -> {
return str.isEmpty();
};
System.out.println(predicate.test(""));
}
//true
Consumer
//一个参数 没有返回值 T ---> 参数类型 没有返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
public static void main(String[] args) {
Consumer<String> consumer = (str) -> {
System.out.println("str = " + str);
};
consumer.accept("asdasxax");
}
//str = asdasxax
Supplier
//没有参数 一个返回值 T ---> 返回值类型 没有参数
@FunctionalInterface
public interface Supplier<T> {
T get();
}
public static void main(String[] args) {
Supplier<String> supplier = () -> {return "asd";};
System.out.println(supplier.get());
}
//asd
UnaryOperator
//一个参数 一个返回值 参数类型和返回值类型相同
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
public static void main(String[] args) {
//参数和返回值类型都是Integer
UnaryOperator<Integer> unaryOperator = num -> num * 2;
System.out.println(unaryOperator.apply(2));
}
//4
BiFunction
//两个参数 一个返回值
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> biFunction = (n1, n2) -> n1 + n2;
System.out.println(biFunction.apply(1, 2));
}
//3
BinaryOperator
//两个参数 一个返回值 参数和返回值类型相同
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
public static void main(String[] args) {
BinaryOperator<Integer> binaryOperator = (n1, n2) -> n1 + n2;
System.out.println(binaryOperator.apply(1, 2));
}
//3
4.方法引用
在lambda表达式中,当方法体中只有一个方法调用且方法参数是箭头左边的参数
时,可以将lambda表达式缩写为方法引用,使用方法引用可以避免生成lambda$0
函数优化性能
eg.
//缩写为方法引用
Consumer<Integer> consumer1 = c -> System.out.println(c);
Consumer<Integer> consumer2 = System.out::println;
consumer1.accept(1); //1
consumer2.accept(1); //1
public class Person {
private String name;
private int age;
private String gender;
public Person() {
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//静态方法
public static void eat(String food){
System.out.println("人在吃" + food + "...");
}
//普通方法
public int play(String ball){
System.out.println(this.name + "在打" + ball + "...");
return this.age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
//1.静态方法 方法引用 使用类名::方法
Consumer<String> consumer = Person::eat;
consumer.accept("苹果");
//2.普通方法 方法引用
//1)使用实例::方法
Person person = new Person("张三", 18, "男");
Supplier<String> supplier = person::toString;
System.out.println(supplier.get());
//2)使用类名::方法
BiFunction<Person, String, Integer> biFunction = Person::play;
System.out.println(biFunction.apply(person, "篮球"));
//3.构造函数方法引用 类名::new
Supplier<Person> supplier1 = Person::new;
System.out.println(supplier1.get());
/*
人在吃苹果...
Person{name='张三', age=18, gender='男'}
张三在打篮球...
18
Person{name='null', age=0, gender='null'}
*/
注意:
在调用类中普通成员方法时,会将调用者作为参数传入this
5.变量引用
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
//list = null; //lambda表达式中引用外部的变量必须被final 无法进行修改
Consumer<String> consumer = s -> System.out.println(s + list);
consumer.accept("list:");
//list:[1,2]
二、Stream流式编程
整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理
接口继承关系:
1.创建流
数据源类型 | 方法 |
---|---|
集合 | Collection.stream / parallelStream |
数组 | Arrays.stream |
数值 | IntStream / LongStream / DoubleStream.range / rangeClosed Random.ints / longs / doubles |
自定义 | Stream.generate / iterate |
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
//1.从集合创建流
System.out.println("list stream: " + list.stream().collect(Collectors.toList()));
System.out.println("list parallelStream: " + list.parallelStream().collect(Collectors.toList()));
System.out.println("----------------------------------------------");
//2.从数组创建流
System.out.println("array stream: " + Arrays.toString(Arrays.stream(new int[]{1, 2, 3}).toArray()));
System.out.println("----------------------------------------------");
//3.创建数值流
System.out.println("intStream of :" + Arrays.toString(IntStream.of(1, 2, 3).toArray()));
//range [) rangeClosed []
System.out.println("intStream range :" + Arrays.toString(IntStream.range(1, 10).toArray()));
System.out.println("intStream rangeClosed :" + Arrays.toString(IntStream.rangeClosed(1, 10).toArray()));
System.out.println("----------------------------------------------");
//4.Random创建流 需要短路操作需要短路操作(eg.设置个数) 否则会一直创建
Random random = new Random();
System.out.println("random ints :" + Arrays.toString(random.ints().limit(10).toArray()));
System.out.println("----------------------------------------------");
//5.自定义流 需要短路操作
System.out.println("stream generate :" + Arrays.toString(Stream.generate(random::nextInt).limit(10).toArray()));
System.out.println("stream iterate :" + Arrays.toString(Stream.iterate(1, n -> n * 2).limit(10).toArray()));
/*
list stream: [1, 2]
list parallelStream: [1, 2]
----------------------------------------------
array stream: [1, 2, 3]
----------------------------------------------
intStream of :[1, 2, 3]
intStream range :[1, 2, 3, 4, 5, 6, 7, 8, 9]
intStream rangeClosed :[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
----------------------------------------------
random ints :[2062767666, 1550545935, 2043182851, -1626969904, -393174629, -1303175049, 1427478572, 1595123337, -317670513, 748686049]
----------------------------------------------
stream generate :[1315597540, -811133039, -599412873, -876639279, -205436262, -1114456430, 1742542673, 148824684, -1706310645, 1880649223]
stream iterate :[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
*/
2.中间操作和终止操作
中间操作返回值仍然为流,分为:
- 无状态操作:不依赖其他操作
- 有状态操作:需要在其他操作计算完毕后执行
方法 | |
---|---|
无状态 | map / mapToXxx flatMap / flatMapToXxx filter peek unordered |
有状态 | distinct sorted limit / skip |
String str = "hello world";
//打印字符串中每个单词的长度
System.out.println("map:");
Stream.of(str.split(" ")).map(s -> s.length()).forEach(System.out::println);
System.out.println("----------------------------------------------");
//打印字符串中的每一个单词
//intStream、longStream等不是Stream的子类 需要调用boxed方法进行装箱
System.out.println("flatMap:");
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).forEach(s -> {
System.out.print((char)s.intValue());
});
System.out.println("\n----------------------------------------------");
//打印字符串中所有单词中的'l'字符
//filter过滤出满足条件的元素
System.out.println("filter:");
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).filter(s -> s == 'l').forEach(s -> {
System.out.print((char)s.intValue());
});
System.out.println("\n----------------------------------------------");
//用于Debug 是一个中间操作
System.out.println("peek:");
Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println);
System.out.println("\n----------------------------------------------");
//将前面的计算结果去重
System.out.println("distinct:");
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).distinct().forEach(s -> {
System.out.print((char)s.intValue());
});
System.out.println("\n----------------------------------------------");
//将前面的计算结果排序
System.out.println("sorted:");
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).sorted().forEach(s -> {
System.out.print((char)s.intValue());
});
System.out.println("\n----------------------------------------------");
//将跳过指定数目的元素
System.out.println("skip:");
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).skip(1).forEach(s -> {
System.out.print((char)s.intValue());
});
/*
map:
5
5
----------------------------------------------
flatMap:
helloworld
----------------------------------------------
filter:
lll
----------------------------------------------
peek:
hello
hello
world
world
----------------------------------------------
distinct:
helowrd
----------------------------------------------
sorted:
dehllloorw
----------------------------------------------
skip:
elloworld
*/
终止操作分为:
- 短路操作:无需等待所有结果计算完即可关闭流
- 非短路操作
方法 | |
---|---|
短路 | forEach / forEachOrdered collect / toArray reduce min / max / count |
非短路 | findFirst / findAny allMatch / anyMatch / noneMatch |
String str = "hello world";
//对于并行流 forEach输出会乱序 forEachOrdered保证顺序
System.out.println("forEach:");
str.chars().parallel().forEach(c -> System.out.print((char) c));
System.out.println("\n----------------------------------------------");
System.out.println("forEachOrdered:");
str.chars().parallel().forEachOrdered(c -> System.out.print((char) c));
System.out.println("\n----------------------------------------------");
//将结果收集为集合输出
System.out.println("collect:");
List<String> words = Stream.of(str.split(" ")).collect(Collectors.toList());
System.out.println(words);
System.out.println("----------------------------------------------");
//对元素进行处理 返回Optional对象
System.out.println("reduce:");
Optional<String> reduce = Stream.of(str.split(" ")).reduce((s1, s2) -> s1 + "|" + s2);
//使用get()获取结果可能会抛出异常 推荐使用orElse()出现异常返回初始值""
//System.out.println(reduce.get());
System.out.println(reduce.orElse(""));
//简写方式
System.out.println(Stream.of(str.split(" ")).reduce("", (s1, s2) -> s1 + "|" + s2));
System.out.println("----------------------------------------------");
//找出字符串中最长的单词
System.out.println("max:");
Optional<String> max = Stream.of(str.split(" ")).max((o1, o2) -> o1.length() - o2.length());
System.out.println(max.orElse(""));
System.out.println("----------------------------------------------");
//找到任何一个终止 找到第一个终止
System.out.println("findAny、findFirst:");
Random random = new Random();
OptionalInt any = random.ints().findAny();
OptionalInt first = random.ints().findFirst();
System.out.println(any.orElse(0));
System.out.println(first.orElse(0));
System.out.println("----------------------------------------------");
//allMatch 所有元素满足条件
//anyMatch 任意元素满足条件
//noneMatch 所有元素都不满足条件
System.out.println("allMatch、anyMatch、noneMatch:");
System.out.println(IntStream.rangeClosed(1, 10).allMatch(i -> i == 1));
System.out.println(IntStream.rangeClosed(1, 10).anyMatch(i -> i == 1));
System.out.println(IntStream.rangeClosed(1, 10).noneMatch(i -> i == 0));
/*
forEach:
wo rehldlol
----------------------------------------------
forEachOrdered:
hello world
----------------------------------------------
collect:
[hello, world]
----------------------------------------------
reduce:
hello|world
|hello|world
----------------------------------------------
max:
hello
----------------------------------------------
findAny、findFirst:
1453443251
654947878
----------------------------------------------
allMatch、anyMatch、noneMatch:
false
true
true
*/
惰性求值:如果对流的处理没有终止操作则不会执行任何操作
class A {
public static void print(int num) {
System.out.println("打印" + num + "...");
}
}
IntStream.rangeClosed(1, 10).peek(A::print).forEach(System.out::println);
System.out.println("-----------------------------------");
System.out.println("惰性求值:");
IntStream.rangeClosed(1, 100).peek(A::print);
/*
打印1...
1
打印2...
2
打印3...
3
打印4...
4
打印5...
5
打印6...
6
打印7...
7
打印8...
8
打印9...
9
打印10...
10
-----------------------------------
惰性求值:
*/
3.并行流
多次调用parallel / sequential 以后面的为准
并行使用的线程池为ForkJoinPool.commonPool
,默认线程数为当前机器的核数
class B {
public static void show(int num) {
System.out.println(Thread.currentThread().getName() + "打印" + num + "...");
}
}
//设置线程数
//System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "10");
System.out.println("count :" + IntStream.rangeClosed(1, 10)
.parallel().peek(B::show)
//.sequential().peek(B::show)
.count());
/*
main打印7...
ForkJoinPool.commonPool-worker-1打印3...
main打印6...
ForkJoinPool.commonPool-worker-1打印4...
ForkJoinPool.commonPool-worker-3打印2...
ForkJoinPool.commonPool-worker-7打印10...
ForkJoinPool.commonPool-worker-6打印1...
ForkJoinPool.commonPool-worker-4打印8...
ForkJoinPool.commonPool-worker-2打印9...
ForkJoinPool.commonPool-worker-5打印5...
count :10
------------------------------
main打印1...
main打印1...
main打印2...
main打印2...
main打印3...
main打印3...
main打印4...
main打印4...
main打印5...
main打印5...
main打印6...
main打印6...
main打印7...
main打印7...
main打印8...
main打印8...
main打印9...
main打印9...
main打印10...
main打印10...
count :10
*/
推荐使用自己的线程池,默认线程池可能被其他程序使用产生阻塞
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
forkJoinPool.submit(() -> {
System.out.println(IntStream.rangeClosed(1, 10)
.parallel().peek(B::show)
.count());
});
TimeUnit.SECONDS.sleep(5);
/*
ForkJoinPool-1-worker-4打印2...
ForkJoinPool-1-worker-1打印10...
ForkJoinPool-1-worker-6打印8...
ForkJoinPool-1-worker-9打印7...
ForkJoinPool-1-worker-8打印1...
ForkJoinPool-1-worker-11打印9...
ForkJoinPool-1-worker-15打印6...
ForkJoinPool-1-worker-13打印5...
ForkJoinPool-1-worker-2打印3...
ForkJoinPool-1-worker-4打印4...
10
*/
4.收集器
Person person1 = new Person("张三", 18, "男");
Person person2 = new Person("李四", 19, "男");
Person person3 = new Person("王五", 21, "男");
Person person4 = new Person("李华", 25, "女");
ArrayList<Person> list = new ArrayList<>();
list.add(person1);
list.add(person2);
list.add(person3);
list.add(person4);
//统计最大值、最小值、平均值、数量和总和
System.out.println("summarizingInt:");
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(Person::getAge));
System.out.println(summaryStatistics);
System.out.println("-----------------------------------");
//按照性别分块 分块是一种特别的分组(只有两组)
System.out.println("partitioningBy:");
Map<Boolean, List<Person>> genderPartitions = list.stream().collect(Collectors.partitioningBy(p -> p.getGender() == "男"));
System.out.println(genderPartitions);
System.out.println("-----------------------------------");
//按照年龄分组
System.out.println("groupingBy:");
Map<Integer, Long> ageGroups = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
System.out.println(ageGroups);
/*
summarizingInt:
IntSummaryStatistics{count=4, sum=83, min=18, average=20.750000, max=25}
-----------------------------------
partitioningBy:
{false=[Person{name='李华', age=25, gender='女'}], true=[Person{name='张三', age=18, gender='男'}, Person{name='李四', age=19, gender='男'}, Person{name='王五', age=21, gender='男'}]}
-----------------------------------
groupingBy:
{18=1, 19=1, 21=1, 25=1}
*/
5.原理分析
//中间操作
IntStream intStream = new Random().ints().limit(10)
//无状态
.peek(i -> {
System.out.println("peek: " + i);
})
//有状态
//.sorted()
//无状态
.filter(i -> {
System.out.println("filter: " + i);
return i > 0;
});
//终止操作
intStream.count();
所有操作都是链式调用,每一个中间操作返回一个流,流中有一个属性
sourceStage指向Head,整个链路类似
Head -> nextStage -> nextStage -> ··· -> null
有状态中间操作依赖于前面计算的结果,会把无状态操作分隔开
parallel()、sequetial()也是中间操作,但它们不返回流对象,只修改sourceStage
的parallel字段
//中间操作
IntStream intStream = new Random().ints().limit(10)
.parallel()
//无状态
.peek(i -> {
System.out.println(Thread.currentThread().getName() + "peek: " + i);
})
//有状态
.sorted()
//无状态
.filter(i -> {
System.out.println(Thread.currentThread().getName() + "filter: " + i);
return i > 0;
});
//终止操作
intStream.count();