一、什么是Lambda:函数式接口的一种实现
Lambda概述:
- Lambda表达式也被称为箭头函数、匿名函数、闭包
- Lambda表达式体现的是轻量级函数式编程思想,是函数式编程思想的一种体现;允许把函数作为一个方法的参数(函数作为参数传递到方法中),体现出轻量级函数式编程思想。
- 在方法中用函数式接口作为参数,可以通过传递lambda表达式(实现类)对参数进行处理
- ‘->’符号式Lambda表达式核心操作符号,符号左侧是操作参数。符号右侧是操作表达式
- JDK8新特性(运行环境jdk8及以上)
Model Code as Data (编码即数据的开发模式)
编码即数据,尽可能轻量级的将代码封装为数据;(我们经常有代码质量控制的要求,让代码的编写更加趋于数据的有效处理也就是编码即数据的开发模式)
解决方案:接口&实现类(匿名内部类)
存在问题:语法冗余,this关键字、变量捕获、数据控制等
为什么要用Lambda表达式:
- 对现有解决方案的语义优化;
- 并不是解决未知问题的新技术;
- 需要根据实际需求考虑性能问题;
使用示例:
一,创建线程(匿名内部类):
传统解决方案:接口&实现类(匿名内部类)
存在问题:语法冗余、this关键字、变量捕获、数据控制等;
public static void main (String[] args){
// 1. 传统模式下,新线程的创建
new Thread (new Runnable() {
@Override
public void run() {
System.out.println("threading..." + Thread.currentThread().getId())
}
}).start();
// 2. lambda表达式优化线程模式
new Thread(()->{
System.out.println("lambda threading..." + Thread.currentThread().getId());
}).start();
}
二、函数式接口(function interface):
函数式接口就是java类型系统中的接口
函数式接口只包含一个接口方法的特殊接口,接口也可以同时存在默认方法、静态方法、来自Object继承的方法
语义化检测注解:@FunctionalInterface(用于在语义上检测函数式接口的合法性)
示例:
/**
* 用户身份认证标记接口
*/
@FunctionalInterface
public interface IUserCredential {
/**
* 通过用户账号,验证用户身份信息的接口
* @param username 要验证的用户账号
* @return 返回身份信息[系统管理员、用户管理员、普通用户]
*/
String verifyUser(String username);
default String getCredential(String username) {
if ("admin".equals(username)) {
return "admin + 系统管理员用户";
} else if("manager".equals(username)){
return "manager + 用户管理员用户";
} else {
return "commons + 普通会员用户";
}
}
String toString();
/**
* 消息合法性验证方法
* @param msg 要验证的消息
* @return 返回验证结果
*/
static boolean verifyMessage(String msg) {
if (msg != null) {
return true;
}
return false;
}
}
Lambda核心就是一个函数式接口的实现; 符合函数式的接口定义的接口才能使用lambda,lambda实际就是函数式接口的实现(只有一个抽象方法),写法只需要写参数和实现,调用的时候还是通过定义的抽象方法调用
函数式接口只包含一个操作方法,Lambda只能操作一个方法
匿名内部类实现接口的抽象方法,可以使用Lambda对冗余代码简化
// 匿名内部类,实现接口的抽象方法
IUserCredential ic = new IUserCredential() {
@Override
public String verifyUser(String username) {
return "admin".equals(username)?"管理员":"会员";
}
};
// lambda表达式是函数式接口的一种简单实现
IUserCredential ic2 = (username) -> {
return "admin".equals(username)?"lbd管理员": "lbd会员";
};
Java类型系统内建函数式的接口:
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
more
JDK 1.8 新增加的函数接口:
java.util.function
jdk8提供了java.util.function包,提供了常用的函数式功能接口
/*
java.util.function提供了大量的函数式接口
Predicate 接收参数T对象,返回一个boolean类型结果
Consumer 接收参数T对象,没有返回值
Function 接收参数T对象,返回R对象
Supplier 不接受任何参数,直接通过get()获取指定类型的对象
UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象
BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象
*/
Predicate<String> pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("manager"));
Consumer<String> con = (String message) -> {
System.out.println("要发送的消息:" + message);
};
con.accept("lambda expression.");
Function<String, Integer> fun = (String gender) -> {
return "male".equals(gender)?1:0;
};
System.out.println(fun.apply("male"));
Supplier<String> sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get());
UnaryOperator<String> uo = (String img)-> {
img += "[100x200]";
return img;
};
System.out.println(uo.apply("原图--"));
BinaryOperator<Integer> bo = (Integer i1, Integer i2) -> {
return i1 > i2? i1: i2;
};
System.out.println(bo.apply(12, 13));
三、Lambda表达式基本语法
基本语法
- 声明:就是和lambda表达式绑定的接口类型
- 参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
- 操作符:->
- 执行代码块:包含在一对大括号中,出现在操作符号的右侧
[接口声明] = (参数) -> {执行代码块};
// 没有参数,没有返回值的lambda表达式绑定的接口
interface ILambda1{
void test();
}
// 带有参数,没有返回值的lambda表达式
interface ILambda2{
void test(String name, int age);
}
// 带有参数,带有返回值的lambda表达式
interface ILambda3 {
int test(int x, int y);
}
ILambda1 i1 = () -> System.out.println("hello boys!");
i1.test();
ILambda2 i21 = ( n, a) -> {
System.out.println(n + "say: my year's old is " + a);
};
i21.test("jerry", 18);
ILambda2 i22 = (n, a) ->
System.out.println(n + " 说:我今年" + a + "岁了.");
i22.test("tom", 22);
ILambda3 i3 = (x, y) -> {
int z = x + y;
return z;
};
System.out.println(i3.test(11, 22));
ILambda3 i31 = (x, y) -> x + y;
System.out.println(i31.test(100, 200));
总结:
- lambda表达式,必须和接口进行绑定。
- lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中的参数进行推导。
- lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。 如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。
变量捕获
- 匿名内部类型变量捕获
- lambda表达式变量捕获
// 1. 匿名内部类型中对于变量的访问
String s1 = "全局变量";
public void testInnerClass() {
String s2 = "局部变量";
new Thread(new Runnable() {
String s3 = "内部变量";
@Override
public void run() {
// 访问全局变量
// System.out.println(this.s1);// this关键字~表示是当前内部类型的对象(报错)
System.out.println(s1);
System.out.println(s2);// 局部变量的访问,不能对局部变量进行数据的修改final
// s2 = "hello";
System.out.println(s3);
System.out.println(this.s3);
}
}).start();
}
// 2. lambda表达式变量捕获
public void testLambda() {
String s2 = "局部变量lambda";
new Thread(() -> {
String s3 = "内部变量lambda";
// 访问全局变量
// 不再建立对象域
System.out.println(this.s1);// this关键字,表示的就是所属方法所在类型的对象
// 访问局部变量
System.out.println(s2);
// s2 = "hello";// 不能进行数据修改,默认推导变量的修饰符:final
System.out.println(s3);
s3 = "labmda 内部变量直接修改";
System.out.println(s3);
}).start();
}
总结:Lambda表达式优化了匿名内部类类型中的this关键字,不再单独建立对象作用域,表达式本身就是所属类型对象的一部分,在语法语义上使用更加简洁。
类型检查
对于语法相同的表达式,Jvm在运行的过程中,在底层通过解释及重构,进行类型的自动推导。
- 表达式类型检查
- 参数类型检查
@FunctionalInterface
interface MyInterface<T, R> {
R strategy (T t, R r);
}
public static void test(MyInterface<String, List> inter) {
List<String> list = inter.strategy("hello", new ArrayList());
System.out.println(list);
}
public static void main(String[] args) {
test(new MyInterface<String, List>() {
@Override
public List strategy(String s, List list) {
list.add(s);
return list;
}
});
test((x, y) -> {
y.add(x);
return y;
// x.add(y);
// return x;
});
/*
(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表达式-> MyInterface类型
这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing)
(x,y)->{..} --> MyInterface.strategy(T r, R r)--> MyInterface<String, List> inter
--> T==String R==List --> lambda--> (x, y) == strategy(T t , R r)--> x==T==String y==R==List
*/
方法重载
interface Param1 {
void outInfo(String info);
}
interface Param2 {
void outInfo(String info);
}
// 定义重载的方法
public void lambdaMethod(Param1 param) {
param.outInfo("hello param1 imooc!");
}
public void lambdaMethod(Param2 param) {
param.outInfo("hello param2 imooc");
}
test.lambdaMethod(new Param1() {
@Override
public void outInfo(String info) {
System.out.println(info);
}
});
test.lambdaMethod(new Param2() {
@Override
public void outInfo(String info) {
System.out.println("------");
System.out.println(info);
}
});
/*
lambda表达式存在类型检查-> 自动推导lambda表达式的目标类型
lambdaMethod() -> 方法 -> 重载方法
-> Param1 函数式接口
-> Param2 函数式接口
调用方法-> 传递Lambda表达式-> 自动推导->
-> Param1 | Param2
*/
// 报错 Ambigus Method call
// test.lambdaMethod( (String info) -> {
// System.out.println(info);
// });
总结:
方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。
因为类型检查,两个重载方法都是函数式接口的时候,不能用Lambda表达式,只能用匿名内部类实现
出现方法重载的类型中参数都是函数式接口的情况,需使用匿名内部类实现替代lambda表达式。
底层构建原理
- Lambda表达式底层解析运行原理
- Lambda表达式在JVM底层解析成私有静态方法和匿名内部类型
- 通过实现接口的匿名内部类型中接口方法调用静态实现方法,完成Lambda表达式的执行;
public class Test{
public static void main(String args[]){
ITest it = (message) -> System.out.println(message);
it.markUp("lambda!");
// new Test$$Lambda$1().markUp("lambda");
}
}
interface ITest{
void markUp(String msg);
}
javac Test.java
- javap -p Test.class (javap反解析工具 -p显示所有类与成员)
java -Djdk.internal.lambda.dumpProxyClasses Test
Compiled from "Test.java"
public class Test {
public Test();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.String){
System.out.println(message);
};
}
finnal class Test$$Lambda$1 implements ITest{
private Test$$Lambda$1(){
}
public void markUp(java.lang.String msg){
Test.lambda$main$0(msg);
}
}
- 声明一个私有静态方法,对Lambda表达式做一个具体的方法实现
- 声明一个final内部类型并实现接口
- 在实现接口后的重写方法中利用外部类调用该私有静态方法
方法引用
方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用是结合Lambda表达式的一种语法特性
- 静态方法引用
- 实例方法引用
- 构造方法引用
class Person {
private String name;
private String gender;
private int age;
// 静态方法引用
public static int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
class PersonUtil {
// 增加一个实例方法
public int comprareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
}
interface IPerson {
// 抽象方法:通过指定类型的构造方法初始化对象数据
Person initPerson(String name, String gender, int age);
}
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("shuke", "男", 29));
list.add(new Person("tom", "男", 16));
list.add(new Person("jerry", "男", 20));
list.add(new Person("beita", "女", 30));
// 1.匿名内部类实现
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// 2.lambda表达式实现
Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());
// 3.静态方法引用实现
Collections.sort(list, Person::compareByAge);
// 4.实例方法引用
PersonUtil pu = new PersonUtil();
Collections.sort(list, pu::comprareByName);
list.forEach(System.out::println);
// 5.构造方法引用:绑定函数式接口
IPerson ip = Person::new;
Person person = p1.initPerson("tom", "男", 18);
System.out.println(person);
}
Stream:对集合、数组、容器等元素转换为sream可以进行数据运算
底层主要是做数据运算的
两句话理解Stream:
1.Stream是元素的集合,这点让Stream看起来用些类似Iterator;
2.可以支持顺序和并行的对原Stream进行汇聚的操作;
- 新添加的Stream流—是一个来自数据源的元素队列并支持聚合操作。把真正的函数式编程风格引入到Java中。
- 不存储数据,也不修改原始源。
- Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
- Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
- 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
- 元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
这里的stream,并不是我们io中的数据流stream,也不是集合元素,也不是数据结构,不能存储数据,这里的stream是和数据算法及运算有关的,jdk8中stream流的引入,是针对多个数据: 数组、容器、集合等存储批量数据的容器,聚合操作时的复杂和容易流程,提出了一套新的API,可以结合Lambda 表达式,通过串行和并行两种不同的方式,完成对批量数据的正常操作;
// 1. for循环实现 List<String> list = new ArrayList<String>(); for (String s : list) { if (s.length() > 3) { lista.add(s); } } System.out.println(lista);
// 2. 迭代器实现
List<String> listb = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String s = it.next();
if(s.length() > 3) {
listb.add(s);
}
}
System.out.println(listb);
// 3. stream实现
List listc = list.stream().filter(s->s.length()>3)
.collect(Collectors.toList());
System.out.println(listc);
几者关系
lambda表达式是传统方法的语法糖,简化并且改造传统内部类实现设计方案的另一种实现模式。
方法引用又是lambda基础上的语法糖,和Stream没有关系,简化方法调用的。
Stream是针对数据和集合的强化优化操作,可以和lambda结合起来简化编码过程。
Stream API
- Stream聚合操作
- API:intermediate中间/记录操作【无状态、有状态】
- API:terminal终结/结束操作【非短路、短路】
<p>Stream常见操作API介绍
* 1. 聚合操作
*
* 2. stream的处理流程
* 数据源
* 数据转换
* 获取结果
* 3. 获取Stream对象
* 1. 从集合或者数组中获取[**]
* Collection.stream(),如accounts.stream()
* Collection.parallelStream()
* Arrays.stream(T t)
* 2. BufferReader
* BufferReader.lines()-> stream()
* 3. 静态工厂
* java.util.stream.IntStream.range()..
* java.nio.file.Files.walk()..
* 4. 自定构建
* java.util.Spliterator
* 5. 更多的方式..
* Random.ints()
* Pattern.splitAsStream()..
* 4. 中间操作API{intermediate}
* 操作结果是一个Stream,中间操作可以有一个或者多个连续的中间操作,需要注意的是,中间操作
* 只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
* 中间操作:就是业务逻辑处理。
* 中间操作过程:无状态:数据处理时,不受前置中间操作的影响。
* map/filter/peek/parallel/sequential/unordered
* 有状态:数据处理时,受到前置中间操作的影响。
* distinct/sorted/limit/skip
* 5. 终结操作|结束操作{Terminal}
* 需要注意:一个Stream对象,只能有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的处理结果。
* 终结操作:非短路操作:当前的Stream对象必须处理完集合中所有 数据,才能得到处理结果。
* forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
* 短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果。
* anyMatch/allMatch/noneMatch/findFirst/findAny等
* Short-circuiting,无限大的Stream-> 有限大的Stream。
* </p>
Stream操作集合元素
类型转换:其他类型(创建、获取)——>Stream对象
类型转换:Stream对象->其他类型
Stream常见API操作
// 1. 批量数据 -> Stream对象
// 多个数据
Stream stream = Stream.of("admin", "tom", "jerry");
// 数组
String [] strArrays = new String[] {"xueqi", "biyao"};
Stream stream2 = Arrays.stream(strArrays);
// 列表
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
Stream stream3 = list.stream();
// 集合
Set<String> set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
Stream stream4 = set.stream();
// Map
Map<String, Integer> map = new HashMap<>();
map.put("tom", 1000);
map.put("jerry", 1200);
map.put("shuke", 1000);
Stream stream5 = map.entrySet().stream();
//2. Stream对象对于基本数据类型的功能封装
//int / long / double
IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println); //只做一次拆箱装箱
IntStream.range(1, 5).forEach(System.out::println);
IntStream.rangeClosed(1, 5).forEach(System.out::println);
// 3. Stream对象 --> 转换得到指定的数据类型
// 数组
Object [] objx = stream.toArray(String[]::new);
// 字符串
String str = stream.collect(Collectors.joining()).toString();
System.out.println(str);
// 列表
//List<String> listx = (List<String>) stream.collect(Collectors.toList());
System.out.println(listx);
// 集合
//Set<String> setx = (Set<String>) stream.collect(Collectors.toSet());
System.out.println(setx);
// Map
//Map<String, String> mapx = (Map<String, String>) stream.collect(Collectors.toMap(x->x, y->"value:"+y));
System.out.println(mapx);
// 1. 批量数据 -> Stream对象
// 多个数据
Stream stream = Stream.of("admin", "tom", "damu");
// 数组
String [] strArrays = new String[] {"xueqi", "biyao"};
Stream stream2 = Arrays.stream(strArrays);
// 列表
List<String> list = new ArrayList<>();
list.add("少林");
list.add("武当");
list.add("青城");
list.add("崆峒");
list.add("峨眉");
Stream stream3 = list.stream();
// 集合
Set<String> set = new HashSet<>();
set.add("少林罗汉拳");
set.add("武当长拳");
set.add("青城剑法");
Stream stream4 = set.stream();
// Map
Map<String, Integer> map = new HashMap<>();
map.put("tom", 1000);
map.put("jerry", 1200);
map.put("shuke", 1000);
Stream stream5 = map.entrySet().stream();
// 2. Stream对象对于基本数据类型的功能封装
// int / long / double 传统的方式会频繁的拆箱和装箱的操作,Stream在底层封装只需要做一次拆箱和装箱
// IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);
// IntStream.range(1, 5).forEach(System.out::println);
// IntStream.rangeClosed(1, 5).forEach(System.out::println);
// 3. Stream对象 --> 转换得到指定的数据类型
// 数组
// Object [] objx = stream.toArray(String[]::new);
// 字符串
// String str = stream.collect(Collectors.joining()).toString();
// System.out.println(str);
// 列表
// List<String> listx = (List<String>) stream.collect(Collectors.toList());
// System.out.println(listx);
// 集合
// Set<String> setx = (Set<String>) stream.collect(Collectors.toSet());
// System.out.println(setx);
// Map
// Map<String, String> mapx = (Map<String, String>) stream.collect(Collectors.toMap(x->x, y->"value:"+y));
// System.out.println(mapx);
// 4. Stream中常见的API操作
List<String> accountList = new ArrayList<>();
accountList.add("xongjiang");
accountList.add("lujunyi");
accountList.add("wuyong");
accountList.add("linchong");
accountList.add("luzhishen");
accountList.add("likui");
accountList.add("wusong");
// map() 中间操作,map()方法接收一个Functional接口
// accountList = accountList.stream().map(x->"梁山好汉:" + x).collect(Collectors.toList());
// filter() 添加过滤条件,过滤符合条件的用户
// accountList = accountList.stream().filter(x-> x.length() > 5).collect(Collectors.toList());
// forEach 增强型循环
// accountList.forEach(x-> System.out.println("forEach->" + x));
// accountList.forEach(x-> System.out.println("forEach->" + x));
// accountList.forEach(x-> System.out.println("forEach->" + x));
// peek() 中间操作,迭代数据完成数据的依次处理过程
// accountList.stream()
// .peek(x -> System.out.println("peek 1: " + x))
// .peek(x -> System.out.println("peek 2:" + x))
// .forEach(System.out::println);
// accountList.forEach(System.out::println);
// Stream中对于数字运算的支持
List<Integer> intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20);
// skip() 中间操作,有状态,跳过部分数据
// intList.stream().skip(3).forEach(System.out::println);
// limit() 中间操作,有状态,限制输出数据量
// intList.stream().skip(3).limit(2).forEach(System.out::println);
// distinct() 中间操作,有状态,剔除重复的数据
// intList.stream().distinct().forEach(System.out::println);
// sorted() 中间操作,有状态,排序
// max() 获取最大值
Optional optional = intList.stream().max((x, y)-> x-y);
System.out.println(optional.get());
// min() 获取最小值
// reduce() 合并处理数据
Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
System.out.println(optional2.get());
}
案例
问题一:将实例List转化为Map
对于List<Table>来说,我需要将其形变为Map<Table.id,Table>,用如下流处理代码
//Table类
public class DmTable {
private Integer id;
private String tableName;
private String tableComment;
private Integer datasourceId;
private Integer directoryId;
private Boolean partitionFlag;
private Integer columnNum;
// ......
}
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, b -> b);
// 等效于
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, Function.identity()));// 静态方法 实现 return t -> t;
问题二:将集合分成若干类别
使用问题一中的Table类,对于List<Table>,我需要将其按照partitionFlag分类,Collector提供两种方法partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的Map。
Map<Boolean, List<Table>> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true));
有的时候,我们关注的不光是元素还有元素的个数,流处理可以再进行后期处理。
Map<Boolean, List<Table>> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true,Collectors.counting()));
可输出符合要求的个数。
groupingBy()可对字符串长度分组。
List<String> strings=Arrays.asList(“this”,”is”,”a”,”test”);
Map<Integer, List<String>> stringsMap = strings
.stream().collect(Collectors.groupingBy(String::length);
结果输出多分类的map,key值为字符串长度。
注意:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8新增方法只适合处理内存中的数据。
问题三:从list中得到某个特定的对象
获得List<Table>中columnNum最多的table对象
tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size() - 1);
添加中间操作reversed() 可获取最小columnNum的对象
问题四: 得到Map<Table,Table.columnNum>中最大columnNum的table
List<Map.Entry<Table, Integer>> list = new ArrayList(tableMap.entrySet());
Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));
list.get(0).getKey();
性能与安全
- 串行Stream的性能小于传统的for循环、 迭代器
- 并行Stream的性能与传统的for循环、 迭代器差不多,在处理对象(复杂数据类型)的情况下,并行性能最佳
// 整数列表
List<Integer> lists = new ArrayList<Integer>();
// 增加数据
for (int i = 0; i < 1000; i++){
lists.add(i);
}
// 串行Stream
List<Integer> list2 = new ArrayList<>();
lists.stream().forEach(x->list2.add(x));
System.out.println(lists.size());
System.out.println(list2.size());
// 并行Stream 线程不安全 丢失
List<Integer> list3 = new ArrayList<>();
lists.parallelStream().forEach(x-> list3.add(x));
System.out.println(list3.size());
// collect 当并行执行时可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离
List<Integer> list4 = lists.parallelStream().collect(Collectors.toList());
System.out.println(list4.size());