Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性,可以使代码变的更加简洁紧凑。
函数式接口
所谓的函数式接口,就是一个接口里面只能有一个抽象方法。但不限于default、static和重载的方法,再jdk8中引入了@FunctionalInterface注解,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
public class Lambda {
public static void main(String[] args) {
String str = "hello lambda";
// 原始方式
Lambda.printSomething(str, new Printer() {
@Override
public void printer(String str) {
System.out.println(str);
}
});
// lambad方式
Lambda.printSomething(str,(String s) -> {System.out.println(s);});
// lambda简化
Lambda.printSomething(str, s -> System.out.println(s));
// lambda引用
Lambda.printSomething(str,System.out::println);
// 两个参数
MyMath myMath = (x,y) -> x+y;
System.out.println(myMath.add(10, 20));
}
public static void printSomething(String str,Printer printer){
printer.printer(str);
};
}
// 函数型接口
@FunctionalInterface
interface Printer{
void printer(String str);
}
@FunctionalInterface
interface MyMath{
int add(int x,int y);
}
简化表达式
-
可选参数:参数可以不写类型,当只有一个参数的时候无需定义圆括号,但多个参数需要定义圆括号。
-
可选大括号:如果主体包含了一个语句,就不需要使用大括号。
-
可选返回值:如果主体只有一个表达式返回值则编译器会自动返回值,也就是说我们不用显示的返回,可以省略大括号和return
(String s) -> {System.out.print(s); }可以简化为
s -System.out.print(s)
(int a, int b) -> { return a * b; }; 可以简化为
(a, b) -> a - b;
-
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
-
不能在Lambda 表达式前当中不允许声明一个与局部变量同名的参数或者局部变量。
public static int t1 =1;
public static void main(String[] args) {
int t2 = 1;
t2 = 2; //报错,相当于加了final
t1 = 2; // 正确
int x = 1; //报错,同名变量
MyMath myMath1 = (x,y)->x+t1;
MyMath myMath2 = (x,y)->x+t2;
int y = 1; //正确
}
Stream API
-
Stream常见操作
- filter§ 过滤元素
- map ()映射处理元素(返回新list)
- sort (comparator)排序元素
list.stream().sorted((p11, p22) -> (p11.getAge() - p22.getAge()))
list.stream().sorted(Comparator.comparing(Person::getAge))
- foreach 遍历获取元素
forEach相当于for循环,遍历每一个直接元素(对象引用)
Consumer<Person> p = i -> i.setAge(i.getAge()+3);
list.stream().forEach(p);
- toArray(Integer[] :: new) 结果转数组
- count() 输出元素个数
- summaryStatistics() 配合mapToInt,[count,sum,min,average,max]
- sum,min,average,max 同上
- max(comparator)、min(comparator) 求出元素最大最小值,返回Optional
- limit(x) 取出前x个元素
- skip(x) 取出跳过前x个元素
- distinct() 去重元素[对象去重需要实现hashcode和equals按顺序比较,同HashSet]
- anyMatch§ 查看是否包含一个元素符合条件,返回boolean
- allMatch§ 查看是否所有元素都符合条件,返回boolean
allMatch:有一个为false即为false,全为true即为true,
anyMatch: 有一个为true即为true,全为false即为false
noneMatch: 有一个为true即为false,全为false即为true,
如果你for循环要跳出可以采用allMatch
list.stream().allMatch(i->{
System.out.println("do something");
return i.equals('条件')
})
- noneMatch§ 查看是否所有元素都不符合条件,返回booleanp
- findFirst() 获取集合的第一个元素,返回Optional,通过**get()**方法可以获取Optional的元素,无则抛出异常;**isPresent()**方法可以判断Optional中是否有值;**ifPresent(handle)**方法可以当存在值时对其进行操作,**orElse(new T())**如果没有元素存在则返回一个默认值
of()传入值不可以为null,ofNullable可以
使用get()可以获取值,但前提是值不能为null,否则抛异常
你可以通过isPresent()判断值是否为空,null为false,否则true
在ifPresent(……)写你的逻辑,当前对象为参数,只有对象不为空的时候才会执行逻辑
orElse() 如果Optional入的值为null,则返回参数值,否则返回传入的值
orElseGet(Person::new) ;orElseGet(()->p);
上述两种方式在对象为空时都会执行参数,但如果参数不为空,orElse也会执行参数,而orElseGet不会
Person person = Optional.ofNullable(p1).orElseGet(()->get());
Person person2 = Optional.ofNullable(p1).orElse(get());
orElseThrow() 当对象为空时将会抛出异常,否则返回原参数对象
Optional.of(get()).orElseThrow(()->new RuntimeException("666"));
fPresentOrElse(Consumer,Runnable)。如果对象不为null,会执行 Consumer 的动作,否则运行 Runnable。
Optional.ofNullable(p1).ifPresentOrElse(i->{
System.out.println("a");
},()->{
System.out.println("b");
});
当filter条件为真时继续返回this,否则返回空对象
Optional.ofNullable(p1).filter(i->i.getAge()>20).orElseThrow(()->new RuntimeException("小于20"));
map() 对值应用(调用)作为参数的函数,然后将返回的值包装在 Optional 中
flatMap() 也需要函数作为参数,并对值调用这个函数,自己返回Optional对象
list.stream().findFirst()……
-
parallel() 并行操作,不建议对filter,map等有状态即依赖元素进行操作,可能会导致数据结果不正确,默认为**sequential()**串行
-
Collectors 常见操作
- toList / toSet 转集合
- toMap 转map(Key,Value,[ Key冲突],[Map类型])
Collectors.toCollection(LinkedList::new)//可以指定返回List的类型
Map<String, Person> collect = list.stream().collect(Collectors.toMap(Person::getName, Function.identity(),(v1,v2)->v2));
//以name作为key,本身作为值( Function.identity()等价于i->i),如果key冲突,覆盖
- joining(delimiter,prefix,suffix) 连接为字符串
- groupingBy 分组,可以返回单个属性,也可以返回多个属性以对象的形式(重写equals)
List<String> MyList1 = Arrays.asList("kobe", "jams", "curry", "cyyt");
MyList1.stream()
.filter(s -> s.startsWith("c")) //过滤
.map(String::toUpperCase) //映射处理
.sorted((o1, o2) -> o2.compareTo(o1)) // 排序
.collect(Collectors.toList()); //转list
MyList1.stream().forEach( i -> System.out.println(i)); // 遍历
MyList1.stream().collect(Collectors.joining()); // 输出 kobejamscurrycyyt
MyList1.stream().collect(Collectors.joining(",")); // 输出 kobe,jams,curry,cyyt
MyList1.stream().collect(Collectors.joining(",","[","]")); // 输出 [kobe,jams,curry,cyyt]
MyList1.stream().limit(2).collect(Collectors.toList()); // 输出[kobe, jams]
MyList1.stream().skip(2).collect(Collectors.toList()); // 输出[curry, cyyt]
Arrays.asList(MyList1.stream().collect(Collectors.joining(",")).split(","))
//输出字符串后再转为list集合
list.stream().map(Person::getName).collect(Collectors.joining(",")) // 集合以逗号分隔连接成字符串
Filter与谓词逻辑
Employee e1 = new Employee(1, 89, "M", "Jack", "Marry");
Employee e2 = new Employee(2, 24, "F", "Rose", "Tom");
Employee e3 = new Employee(3, 29, "M", "Davil", "Bob");
Employee e4 = new Employee(4, 35, "F", "Join", "Rern");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4);
employees.stream().filter(Employee.ageAndGenderF).forEach(e -> System.out.println(e));
System.out.println("******或or********");
employees.stream().filter(Employee.ageAndGenderM.or(Employee.ageAndGenderF)).forEach(e -> System.out.println(e));
System.out.println("*******与and********");
employees.stream().filter(Employee.ageAndGenderM.and(Employee.ageAndGenderF)).forEach(e -> System.out.println(e));
System.out.println("*******非1<> 整体取反********");
employees.stream().filter(Employee.ageAndGenderM.and(Employee.ageAndGenderF).negate()).forEach(e -> System.out.println(e));
System.out.println("*******非2<> 局部取反********");
employees.stream().filter(Employee.ageAndGenderM.and((Employee.ageAndGenderF).negate())).forEach(e -> System.out.println(e));
public class Employee {
private Integer id;
private Integer age;
private String gender;
private String firstName;
private String lastName;
//谓词逻辑
public static Predicate<Employee> ageAndGenderF = e -> e.getAge() > 15 && e.getGender().equals("F");
public static Predicate<Employee> ageAndGenderM = e -> e.getAge() > 15 && e.getGender().equals("M");
public static Predicate<Employee> ageAndGenderFM = ageAndGenderM.or((ageAndGenderF).and(ageAndGenderM))
}
Map
返回整数
List<String> strings = Arrays.asList("Moner", "JacK", "Tom");
int[] ints = strings.stream().mapToInt(String::length).toArray();[5,4,3]
如果返回只和传的参数是同一对象可以是用peek,注意该操作会修改原来的集合
List<Employee> collect1 = employees.stream().map(i -> {
i.setId(i.getId() + 1);
return i;
}).collect(Collectors.toList());
List<Employee> collect = employees.stream().peek(i -> i.setId(i.getId() + 1)).collect(Collectors.toList());
排序
最后一个配合.thenComparing()可以实现多个条件的排序
employees.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).collect(Collectors.toList()); //排序1
employees.stream().sorted(Comparator.comparing(Employee::getAge)).collect(Collectors.toList());//排序2
employees.stream().sorted(Comparator.comparing(Employee::getAge).reversed()[thenComparing(...)]).collect(Collectors.toList());//排序3倒序
list.stream().sorted(Comparator.comparing(Person::getAge).thenComparing(Comparator.comparing(Person::getName).reversed())).collect(Collectors.toList());
// 按年龄升序,如果一样按姓名降序
关于reversed的使用
- 都是正序,无需
- 都是倒序,最后加reversed
- 先倒后正,先加reversed后不加
- 先正后到,先加reversed后也加,代表第一个倒序,然后最后一个都是倒序,第一个就变成了正序
归并
- 第一个参数为初始值,total为结果,i为当前元素
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 可以不加初始值,不加默认是第一个元素,返回Optional
int reduce1 = list.stream().reduce(10, (total, i) -> total + i);
int reduce2 = list.stream().reduce(10, Integer::sum/max/min);
int reduce3 = employees.stream().map/mapToInt(Employee::getAge).reduce(10, (total, i) -> total + i);// 集合操作,先转为int
int reduce3 = employees.stream().mapToInt(Employee::getAge).sum()/max()/min()/average() // 使用mapToInt进行数值运算
int reduce3 = employees.stream().reduce(10, (total, i) -> total + i.getAge(), Integer::sum);
// 下面两句等价
list.stream().map(Person::getAge).reduce(BigDecimal.ZERO, (sum, i) -> sum.add(i));
list.stream().map(Person::getAge).reduce(BigDecimal.ZERO, BigDecimal::add);
// 你还可以自定义逻辑
list.stream().map(Person::getAge).reduce(BigDecimal.ZERO,Client1::add);
public static BigDecimal add(BigDecimal o1,BigDecimal o2){
// 代码
return o1.add(o2);
}
方法引用
-
构造器引用 类名::new i -> new 构造方法(i)
-
静态方法引用 类名::方法名 i -> 类名.staticMethod(i)
-
实例方法引用 对象::方法名 i -> i.method()[需要为i类的无参方法]
分组
java分组将会调用分组条件的equals条件进行比较,相同即为一组
list.stream().collect(Collectors.groupingBy(Person::getSex));//按照性别分组
list.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.counting()));// 分组并得到每组的数量
list.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.maxBy(Comparator.comparing(Person::getAge))));// 取出每组年龄最大的
list.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.summingInt(Person::getAge)))// 分组求和
// 多字段分组
list.stream().collect(Collectors.groupingBy(i -> Arrays.asList(i.getName(), i.getSex())));// 多字段分组
list.stream().collect(Collectors.groupingBy(i -> i.getName() + i.getSex()));// 自定义分组条件
其他操作
- 数组使用Stream
- Stream.of(…value)
- Arrays.stream(Array[])
- Arrays.stream(Array[],start,end) // 带截取功能的转换,含头不含尾,同Arrays.copyOfRange(Array[],start,end)
Stream.of(1, 2, 3, 4).reduce(0, Integer::max); // 可变参数
Integer[] arr = {1,2,3,4};
Stream.of(1,2,3,4).collect(Collectors.toList()); // 数组转list等价于of(arr)
Arrays.stream(arr).collect(Collectors.toList()); // 效果同上
- 数值流转换为流(int[]->Integer[])
- Stream stream = intStream.boxed();
- 使用Stream的静态方法:iterate()、generate()、range()
Integer[] ints = Stream.iterate(10, x -> x + 1).limit(10).toArray(Integer[]::new);
List<Double> lists = Stream.generate(Math::random).limit(10).collect(Collectors.toList());
IntStream.range(1,5).forEach(i->{……})// 创建一个1-5(含头不含尾)的数值流