文章目录
Java8新特性-Lambda表达式
Lambda表达式
简介
Lambda表达式(也称闭包),是Java8中最受期待和欢迎的新特性之一。Lambda表达式本质是一个匿名函数,但是它并不是匿名类的语法糖,它让 Java 开始走向函数式编程,其实现原理区别于一般的匿名类中的匿名函数。在Java语法层面Lambda表达式允许函数作为一个方法的参数(函数作为参数传递到方法中),或者把代码看成数据。Lambda表达式可以简化函数式接口的使用。函数式接口就是一个只有一个抽象方法的普通接口像这样的接口就可以使用Lambda表达式来简化代码的编写。
语法糖是什么?
语法糖,又称糖衣语法,是英国计算机科学家发明的一个术语,指在计算机语言中加入某种语法,这种语法对语言的功能并没有影响,但是方便了程序员的操作,并且增加了代码的可读性,减少了出错的机会。
Java 是一个 “低糖语言”,因为在 JDK1.5 之前,语法糖很少出现。但是从 Java 7 开始 Java 语言层面上一直在添加各种糖,未来还会持续向着 “高糖” 的方向发展。
Java 中最常用的语法糖主要有泛型、switch 支持 String、自动装箱与拆箱、foreach 循环、内部类和枚举等。
解语法糖
语法糖主要是方便开发人员使用。但其实,Java 虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
举例:
public class Test { public static void main(String[] args) { String str = "world"; switch (str) { case "hello": System.out.println("hello"); break; case "world": System.out.println("world"); break; default: break; } } } //反编译之后 public class Test { public static void main(String args[]){ String str = "world"; String s; switch((s = str).hashCode()){ default: break; case 99162322: if(s.equals("hello")) System.out.println("hello"); break; case 113318802: if(s.equals("world")) System.out.println("world"); break; } } }
优点和缺点
Lambda表达式的目的:当一个接口中,只有一个抽象方法,那么匿名内部类的语法就会显的十分的笨拙,产生大量的代码,总结一句就是:代码冗余就是匿名内部类最大的弊端。在编写代码的时候,我们很多时候希望把功能作为参数传递到另一个方法当中,Lambda
表达式就是为此而生。
优点:
- 使用Lambda表达式可以简化接口匿名内部类的代码,可以减少类文件的生成,同时引入了强大的类型推断和方法引用特性,简单的功能甚至可以一行代码解决,解放匿名类的束缚。
- 把功能作为参数向下传递,为函数式编程提供了支持,让 Java 开始走向函数式编程。
缺点
- 使用Lambda表达式会减弱代码的可读性,而且Lambda表达式的使用局限性比较强,只能适用于接口只有一个抽象方法时使用,不方便调试。
下面我们来说一下Lambda表达式的基本概念
函数式接口
函数式接口的概念
- 有且只有一个抽象方法的接口被为函数式接口
- 只有函数式接口,才可以转换为lambda表达式
- 接口默认方法必须予以实现,它们不是抽象方法(默认方法将在下面会讲到)
- 们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
理解函数式接口
- Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即**java不但可以支持OOP还可以支持OOF(面向函数编程) **
- 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
- 简单的说,在Java8中,**Lambda表达式就是一个函数式接口的实例。**这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
- 以前用匿名实现类表示的现在都可以用Lambda表达式来写。
例子
Runnabale线程接口
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
自定义函数式接口
package com.song.Lambda.Server;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/7 20:28
* @Description TODO
*/
@FunctionalInterface // 这个可以不带 但是写上这个注解更直观
public interface MyFunction<T> {
T getValue(T value);
}
Java8内置的函数接口
四大核心接口
接口 | 参数 | 返回值 |
---|---|---|
Consumer<T> 消费型接口 | T | void |
Supplier<T> 供给型接口 | 无 | T |
Function<T,R> 函数型接口 | T | R |
Predicate<T> 断定型接口 | T | boolean |
UnaryOperator<T> 一元操作符 | T | T |
BinaryOperator<T> 二元操作符 | T,T | T |
消费性接口
Consumer 接口只有一个抽象方法 accept,参数列表只有一个泛型t,无返回值,重点在于内部消费
package com.song.inFunction;
import java.util.function.Consumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 11:16
* @Description TODO
*/
// Consumer 就像一个消费者,它只消费一个东西(也就是一个对象),并不返回状态,
public class ConsumerDemo01 {
public static void main(String[] args) {
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("Consumer消费型接口");
}
}
如果需要多个参数列表的话,可以考虑使用 ObjLongConsumer
ObjLongConsumer<String> objLongConsumer=new ObjLongConsumer<String>() {
@Override
public void accept(String s, long value) {
System.out.println(s+value);
}
};
objLongConsumer.accept("1",2);
供给型接口
Supplier 只有一个抽象方法 get,参数列表为空,有返回值,返回值得数据类型为T。
package com.song.inFunction;
import java.util.function.Supplier;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 11:26
* @Description TODO
*/
// 是一个对象提供器,但是它不需要参数就可以生成一个对象
public class SupplierDemo01 {
public static void main(String[] args) {
Supplier<String> supplier=new Supplier<String>() {
@Override
public String get() {
return "132";
}
};
String s = supplier.get();
System.out.println(s);
}
}
如果需要返回得数据为基本数据类型,可以考虑使用 LongSupplier
LongSupplier longSupplier=new LongSupplier() {
@Override
public long getAsLong() {
return 1;
}
};
函数型接口
Function<T, R> 只有一个抽象方法名为 apply,参数列表只有一个参数为T,有返回值,返回值的数据类型为R。
package com.song.inFunction;
import java.util.function.Function;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 11:30
* @Description TODO
*/
// 就是数学中的函数,例如 g=f(x),因此它是通过一个参数生成一个值,
public class FunctionDemo01 {
public static void main(String[] args) {
Function<String,Integer> function=new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
};
System.out.println(function.apply("123"));
}
}
如果需要多个入参,然后又返回值的话,可以考虑 BiFunction
断言型接口
断言型又名判断型。 Predicate 只有一个抽象方法 test,参数列表只有一个参数为 T,有返回值,返回值类型为 boolean。
package com.song.inFunction;
import java.util.function.Predicate;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 14:35
* @Description TODO
*/
public class PredicateDemo01 {
public static void main(String[] args) {
Predicate<String> predicate=new Predicate<String>() {
@Override
public boolean test(String s) {
return s!=null;
}
};
predicate.test(null);
}
}
注意点
-
该注解只能标记在"有且仅有一个抽象方法"的接口上。
-
JDK8接口中的静态方法和默认方法,都不算是抽象方法。
-
接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
-
该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
-
如果一个接口中包含不止一个抽象方法,那么不能使用@FunctionalInterface,编译会报错。
接口中的默认方法与静态方法
Java8之前的接口中只能定义全局常量,抽象方法,
Java8之后的接口中能定义全局常量,抽象方法,默认方法以及静态方法
默认方法
接口默认方法(下称默认方法)通过default
关键字声明,可以直接在接口中编写方法体。也就是默认方法既声明了方法,也实现了方法。
package com.song.DefaultMethod;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/7 23:14
* @Description TODO
*/
public interface Demo01<T> {
T get();
default String getName(){
return "hello Java8!";
}
}
接口中的默认方接口默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
- (情况一)选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略(父类与接口中定义了相同的名称的方法,默认实现父类的)
- **(情况二)**接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
如果继承一个定义了默认方法的接口,那么可以有如下的做法:
-
完全忽略父接口的默认方法,那么相当于直接继承父接口的默认方法的实现(方法继承)。
-
重新声明默认方法,这里特指去掉
default
关键字,用public abstract
关键字重新声明对应的方法,相当于让默认方法转变为抽象方法,子类需要进行实现(方法抽象)。 -
重新定义默认方法,也就是直接覆盖父接口中的实现(方法覆盖)。
结合前面一节提到的函数式接口,这里可以综合得出一个结论:函数式接口,也就是有且仅有一个抽象方法的接口,可以定义0个或者N(N >= 1)个默认方法。
静态方法
Java8 中,接口中允许添加静态方法。
package com.song.DefaultMethod;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/7 23:22
* @Description TODO
*/
public interface Demo02 {
Integer myFun();
default String getName(){
return "hello";
}
static void show(){
System.out.println("hello Java8");
}
}
使用Lambd表达式的前提
只适用于函数式接口,即接口有且只有一个抽象方法!!!
基础语法
在认识Lambda表达式基础语法之前,先来看一段用两种方式创建线程的代码
package com.song.LambdaDemo;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:02
* @Description TODO
*/
public class LambdaDemo01 {
public static void main(String[] args) {
// 普通的写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"run");
}
}).start();
// Lambda 简化写法
new Thread(() ->{
System.out.println(Thread.currentThread().getName()+"run");
}).start();
}
}
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符 或箭头操作符。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的参数列表 (其实就是接口中的抽象方法的形参列表)
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,(其实就是重写的抽象方法的方法体)
Lambda表达式重要的特征
可选参数类型声明
不需要声明参数类型,编译器可以统一识别参数值。 称为“类型推断”
package com.song.LambdaDemo;
import java.util.function.Consumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:17
* @Description TODO
*/
public class LambdaDemo02 {
public static void main(String[] args) {
Consumer<String> c1= (String s) -> {
System.out.println(s);
};
c1.accept("222");
Consumer<String> c2=(s) ->{
System.out.println(s);
};
c2.accept("333");
}
}
可选的参数圆括号
一个参数无需定义圆括号,但多个参数需要定义圆括号。例如:
package com.song.LambdaDemo;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:19
* @Description TODO
*/
public class LambdaDemo03 {
public static void main(String[] args) {
Consumer<String> c1= s ->{
System.out.println(s);
};
c1.accept("111");
Comparator<String> comparator= (s1,s2) -> {
int i = s1.compareTo(s2);
return i;
};
comparator.compare("111","222");
}
}
可选的Lambda体大括号
package com.song.LambdaDemo;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:22
* @Description TODO
*/
public class LambdaDemo04 {
public static void main(String[] args) {
Consumer<String> consumer= s -> System.out.println(s);
consumer.accept("111");
Comparator<String> comparator= (s1, s2) -> {
int i = s1.compareTo(s2);
return i;
};
comparator.compare("111","222");
}
}
可选的返回关键字
如果Lambda体不加{ }就不用写return,Lambda体加上{ }就需要添加return。
package com.song.LambdaDemo;
import java.util.Comparator;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:23
* @Description TODO
*/
public class LambdaDemo05 {
public static void main(String[] args) {
Comparator<Integer> comparator1= (i1,i2) ->{
int i = i1.compareTo(i2);
return i;
};
System.out.println(comparator1.compare(4, 5));
Comparator<Integer> comparator2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(2, 3));
}
}
目标类型与类型推断
目标类型
package com.song.LambdaDemo;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 15:42
* @Description TODO
*/
@FunctionalInterface
public interface Runnable {
void run();
static void main(String[] args) {
java.lang.Runnable runnable1= ()-> System.out.println(111);
com.song.LambdaDemo.Runnable runnable2 =() -> System.out.println(333);
runnable1.run();
runnable2.run();
}
}
定义了一个和java.lang.Runnable
完全一致的函数式接口com.song.LambdaDemo.Runnable
,上面main()
方法中,可以看到两个接口对应的Lambda
表达式的方法体实现也是完全一致,但是很明显最终可以使用不同类型的接口去接收返回值,也就是这两个Lambda
的类型是不相同的。而这两个Lambda
表达式返回值的类型是我们最终期待的返回值类型(expecting a data type of XX
),那么Lambda
表达式就是对应的被期待的类型,这个被期待的类型就是Lambda
表达式的目标类型。
为了确定Lambda
表达式的目标类型,Java
编译器会基于对应的Lambda
表达式,使用上下文或者场景进行综合推导,判断的一个因素就是上下文中对该Lambda
表达式所期待的类型。因此,只能在Java
编译器能够正确推断Lambda
表达式目标类型的场景下才能使用Lambda
表达式,这些场景包括:
- 变量声明。
- 赋值。
- 返回语句。
- 数组初始化器。
Lambda
表达式函数体。- 条件表达式(
condition ? processIfTrue() : processIfFalse()
)。 - 类型转换(Cast)表达式。
Lambda
表达式除了目标类型,还包含参数列表和方法体,而方法体需要依赖于参数列表进行实现,所以方法参数也是决定目标类型的一个因素。
方法引用
方法引用的概念
方法引用(Method Reference
)是用来直接访问类或者实例已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。
当Lambda表达式中只是执行一个方法调用时,不用Lambda
表达式,直接通过方法引用的形式可读性更高一些。
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
作用
- 方法引用的唯一用途是支持Lambda的简写。
- 方法引用提高了代码的可读性,也使逻辑更加清晰。
语法
使用::
操作符将方法名和对象或类的名字分隔开。::
是域操作符(也可以称作定界符、分隔符)。
常见的方法引用
常见的方法引用
方法引用 | 等价的Lambda表达式 |
---|---|
String::valueOf | x -> String.valueOf(x) |
Object::toString | x -> x.toString() |
x::toString | () -> x.toString() |
ArrayList::new | () -> new ArrayList<>() |
方法引用的类型归结如下:
类型 | 例子 |
---|---|
静态方法引用 类::静态方法名 | ClassName::methodName |
指定对象实例方法引用 对象::实例方法名 | instanceRef::methodName |
特定类型任意对象方法引用 类::实例方法名 | ContainingType::methodName |
超类方法引用 | supper::methodName |
构造器方法引用 | ClassName::new |
数组构造器方法引用 | TypeName[]::new |
可见其基本形式是:方法容器::方法名称或者关键字
。
静态方法的引用
package com.song.LambdaDemo;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 16:46
* @Description TODO
*/
public class StaticReference {
public static void main(String[] args) {
// 匿名内部类形式全写形式
Function<String,Integer> f1= new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
};
// Lambda表达式形式
Function<String,Integer> f2 = s -> Integer.valueOf(s);
// 方法引用的形式
Function<String,Integer> f3 = Integer::valueOf;
}
}
指定对象实例方法引用
package com.song.LambdaDemo;
import java.util.function.Function;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 16:53
* @Description TODO
*/
public class ParticularInstanceRef {
public Integer refMethod(String value) {
return Integer.parseInt(value); // 将字符串转换成int
}
public static void main(String[] args) {
ParticularInstanceRef pir=new ParticularInstanceRef();
// 匿名内部类的写法
Function<String,Integer> f1= new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return pir.refMethod(s);
}
};
// Lambda 表达式的写法
/*Function<String,Integer> f2= (s) ->
{
return pir.refMethod(s);
};*/
Function<String,Integer> f2= s -> pir.refMethod(s);
// 方法引用
Function<String,Integer> f3=pir::refMethod;
}
}
特定类型任意对象方法引用
注意点:
-
第一点:接口方法的参数比引用方法的参数多一个
-
第二点:接口方法的第一个参数恰巧是调用引用方法的对象(其引用方法所在类或其父类的实例)
举例1
package com.song.LambdaDemo;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:06
* @Description TODO
*/
public class Test {
public void a(){
System.out.println(111);
}
public static void main(String[] args) {
// 匿名内部类
MyInter myInter=new MyInter() {
@Override
public void b(Test test) {
test.a();
}
};
// lambda 表达式
MyInter myInter1= t -> t.a();
// 方法引用
MyInter myInter2= Test::a;
}
}
@FunctionalInterface
interface MyInter{
void b(Test test);
}
举例2
package com.song.LambdaDemo;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:17
* @Description TODO
*/
public class Test02 {
public void a(Integer param1,int param2){
}
public static void main(String[] args) {
MyInter1 myInter1= (t,param1,param2) -> t.a(param1,param2);
MyInter1 myInter=Test02::a;
}
}
@FunctionalInterface
interface MyInter1 {
//该接口参数比上述的a方法参数数量多一个,除去第一个,其它类型一致(可兼容,如可以一个int,一个Integer)
//且Test1::a的Test1是该入参类型Test1相同
void d(Test02 d,int param1,int param2);
}
实例3
package com.song.LambdaDemo;
import java.util.Arrays;
import java.util.Comparator;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:12
* @Description TODO
*/
public class Demo {
public static void main(String[] args) {
String[] stringArray = {"C", "a", "B"};
// 第一种写法
Comparator<String> comparator=new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
Arrays.sort(stringArray,comparator);
// 第一种写法 合起来
Arrays.sort(stringArray, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
// Lambda 表达式写法
Arrays.sort(stringArray,(o1,o2)->o1.compareTo(o2));
// 方法引用写法
Arrays.sort(stringArray, String::compareTo);
Arrays.sort(stringArray,String::compareToIgnoreCase);// 忽略大小写的比较
// [a, B, C]
System.out.println(Arrays.toString(stringArray));
}
}
超类方法引用
public class SupperRef {
public static void main(String[] args) throws Exception {
Sub sub = new Sub();
// 10086
System.out.println(sub.refMethod("10086"));
}
private static class Supper {
private Integer supperRefMethod(String value) {
return Integer.parseInt(value);
}
}
private static class Sub extends Supper {
private Integer refMethod(String value) {
Function<String, Integer> function = super::supperRefMethod;
// 等同于
// Function<String,Integer> function1 = (String s) -> super.supperRefMethod(s);
return function.apply(value);
}
}
}
构造器方法引用
package com.song.LambdaDemo;
import java.util.function.Function;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:34
* @Description TODO
*/
public class ConstructorRef {
public static void main(String[] args) {
Student student=new Student("张三");
// 匿名内部类的写法
Function<String,Student> f1 = new Function<String, Student>() {
@Override
public Student apply(String s) {
return new Student(s);
}
};
// Lambda表达式的写法
Function<String,Student> f2 = s -> new Student(s);
// 方法引用写法
Function<String,Student> f3= Student::new;
}
}
class Student{
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
数组构造器方法引用
package com.song.LambdaDemo;
import java.util.function.Function;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:38
* @Description TODO
*/
public class ArrayCon {
public static void main(String[] args) {
Integer[] integers=new Integer[10];
Function<Integer,Integer[]> f1=new Function<Integer, Integer[]>() {
@Override
public Integer[] apply(Integer integer) {
return new Integer[integer];
}
};
Function<Integer,Integer[]> f2= i -> new Integer[i];
Function<Integer,Integer[]> f3 = Integer[]::new;
f3.apply(10);
}
}
Lambda表达式对集合的用法
为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接。(这里只举一些常用的)
Collection接口
forEach( )方法演示:
package com.song.aggregate;
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:43
* @Description TODO
*/
public class AggregateDemo01 {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("a");
list.add("bc");
list.add("def");
list.add("hello");
//写法1:(不用Lambda表达式)
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//写法2:(用Lambda表达式)
list.forEach(s-> System.out.println(s));
// 写法3:引用 静态方法引用
list.forEach(System.out::println);
}
}
list接口
sort()方法的演示
public static void main(String[] args) {
ArrayList<String>list=new ArrayList<>();
list.add("hh");
list.add("hi");
list.add("def");
list.add("abc");
//写法1:(不用Lambda表达式)
list.sort(new Comparator<String>() {
@Override
public int compare(String o1,String o2) {
return o1.compareTo(o2);
}
});
//写法2:(用Lambda表达式)
list.sort(((o1, o2) -> o1.compareTo(o2)));
System.out.println("======分割线======");
//效果和写法1一样
list.forEach(s-> System.out.println(s));
list.forEach(System.out::println);
}
Map接口
HashMap 的 forEach()
package com.song.aggregate;
import java.util.HashMap;
import java.util.function.BiConsumer;
/**
* @author 小宋学java
* @version 1.0
* @Data 2022/12/9 17:50
* @Description TODO
*/
public class AggregateDemo03 {
public static void main(String[] args) {
HashMap<String,Integer> hashMap=new HashMap<>();
hashMap.put("k1",1);
hashMap.put("k2",2);
hashMap.put("k3",3);
hashMap.put("k4",4);
hashMap.put("k5",5);
hashMap.put("k6",6);
hashMap.put("k7",7);
hashMap.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println("key:"+integer+"value:"+s);
}
});
hashMap.forEach((k,v)-> System.out.println("key:"+k+"value:"+v));
}
}