Skip to content

1 lambda表达式

lambda表达式是一个可传递的代码块,可以在以后一次或多次执行,它采用了一种简洁的方式来定义代码块

lambda表达式类似于方法,可以将其理解成匿名函数,它可以有参数和返回值

Lambda表达式可以作为方法的参数

1.1 基本语法

java
(参数1, 参数2...) -> { 代码块 }

如果没有参数,直接写为:()->

如果只有一个参数,一个语句,可以简写为:参数 -> 语句

java
interface MyInter1 {
	void show();
}

interface MyInter2 {
	void show(int a);
}

interface MyInter3 {
	int add(int a, int b);
}

public class LambdaTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 无参无返回值 
		MyInter1 myInter = () -> {
			System.out.println("haha");
		};
		myInter.show();
		
		// 有参无返回值
		MyInter2 myInter2 = (int a) -> {
			System.out.println(a);
		};
		myInter2.show(30);
		
		// 类型可以省略
		MyInter2 myInter22 = (a) -> {
			System.out.println(a);
		};
		myInter22.show(30);
		
		// 如果只有一个参数,语句块只有一条语句,参数的括号和语句块的大括号可以省略
		MyInter2 myInter222 = a -> System.out.println(a);
		myInter222.show(40);
		
		// 有参有返回值
		MyInter3 myInter3 = (a, b) -> {return a + b;};
		int sum = myInter3.add(12, 20);
		System.out.println(sum);
		
		// 只有一条return语句, return可以省略,大括号可以省略
		MyInter3 myInter33 = (a, b) -> a + b;
		sum = myInter33.add(15, 20);
		System.out.println(sum);
	}

}

1.2 lambda表达式作为参数使用

java
interface CallInterface {
	void call(String info);
}

public class LambdaTest3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		test((info) -> {
			System.out.println(info);
		});
		
	}
	
	public static void test(CallInterface callInterface) {
		callInterface.call("hahahaha");
	}

}

1.3 jdk中使用lambda例子

java
List<Integer> list = new ArrayList<Integer>();
list.add(12);
list.add(23);
list.add(5);
list.forEach((n) -> {System.out.println(n);});
// 可以简写为
list.forEach(n -> System.out.println(n));

// 多线程
Thread thread = new Thread(() -> {
    for(int i = 0; i < 20; i++) {
        System.out.println(Thread.currentThread() + ":" + i);
    }
});
thread.start();

// 排序
Integer[] ints = {98, 243, 35, 13, 57, 243};
List<Integer> list = Arrays.asList(ints);
Collections.sort(list2, (o1,o2) -> o1 - o2);
Collections.sort(list2, (o1,o2) -> {return o1 - o2;});

2 Stream

Stream流是 JDK8中新增的一个处理集合的关键抽象概念。

2.1 生成Stream对象

Java中提供了多种方法生成流对象,通过集合生成Stream对象是最常用的方式。

JDK8中,集合新增了两个流方法,分别是 stream() 和 parallelStream(),通过该方法可以获取Stream对象。比如:

java
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();

2.2 Stream的操作

流的操作类型主要分为两种

1)中间操作 :其目的主要是打开流,做出某种操作后,返回一个新的流,然后交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时进行。

2)结束操作: 一个流有且只能有一个结束操作,当这个操作执行后,流就被关闭了。

中间操作无状态unordered、filter、map、mapToInt、mapToLong、mapToDouble、flatMap、flatMapToInt、flatMapToLong、flatMapToDouble、peek
有状态distinct、sorted、limit、skip
结束操作非短路操作forEach、forEachOrdered、toArray、collect、max、min、count、reduce
短路操作anyMatch、allMatch、noneMatch、findFirst、findAny

无状态:指元素的处理不受之前元素的影响;

有状态:指该操作只有拿到所有元素之后才能继续下去。

非短路操作:指必须处理所有元素才能得到最终结果;

短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

2.2.1 中间操作

1)filter筛选

java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
// 为了方便演示,转为集合类型
List<Integer> collect = integerList.stream().filter(i -> i > 3).collect(Collectors.toList());
System.out.println(collect);

2)distinct去除重复元素

java
List<Integer> integerList = Arrays.asList(1, 1, 2, 2, 3, 4, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();

3)limit返回指定流个数

取指定个数的数据

java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);

4)map流映射

所谓流映射就是将接受的元素映射成另外一个元素

java
List<String> stringList = Arrays.asList("zhangsan", "lisi",  "wangwu", "haha");
Stream<Integer> stream = stringList.stream().map(item -> item.length());

5)flatMap流转换

将一个流中的每个值都转换为另一个流

java
List<String> list = Arrays.asList("I love you", "I hate you");
List<String> strList = list.stream()
        .map(item -> item.split(" "))
        .flatMap(item -> Arrays.stream(item))
        .collect(Collectors.toList());

2.2.2 结束操作

1)统计流中元素个数

java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

2)findFirst查找第一个元素

java
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
// orElse() 值存在就返回,不存在,返回执行值,比如null
Integer value = integerList.stream().filter(i -> i > 3).findFirst().orElse(null);

3)返回集合

java
List<Integer> collect = integerList.stream().filter(i -> i > 3).collect(Collectors.toList());

4)通过groupingBy进行分组

java
Map<String, List<Employee>> collect = list.stream().collect(Collectors.groupingBy(Employee::getSex));

5)reduce将流中的元素组合起来

假设我们对一个集合中的值进行求和

java
// 第一个参数是初始值,第二个参数是元素结合起来的算法
int sum = integerList.stream().reduce(0, (a, b) -> (a + b));

3 ::语法 了解

方法引用,方法引用通过一对双冒号:: 来表示,方法引用相当于对特定写法的lambda表达式的简化,是函数式接口的另一种书写方式。

lambda表达式可用方法引用代替的场景:

lambda表达式的代码块中包含一条语句,且该语句仅调用一个已经存在的方法

规则:

通用规则:引用的方法的返回值和参数,与函数式接口中方法和参数一致

针对实例方法引用:lambda表达式的第一个入参为实例方法的调用者,后面的入参与实例方法的入参一致

引用方式:

静态方法引用:

类名::静态方法名, 如 Integer::parseInt

实例方法引用:

对象名::实例方法,如 str::substring

类名::实例方法,如 Employee::getAge

构造方法引用:

类名::new, 如 User::new

附录

Java中的函数式接口

函数式接口的定义是: 只包含一个抽象方法的接口,称为函数式接口;

其实我们的Lambda表达式就是对函数式接口的一种简写方式,所以只有是函数式接口,我们才能用Lambda表达式;再换句话说,Lambda表达式需要函数式接口的支持,函数式接口我们可以自己定义,JDK也给我们提供了一些现成的函数式接口。