lambda 表达式
匿名函数,简化代码。
简化前:1
2
3
4
5
6
7
8
9Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
for (String item : list) {
System.out.println(item);
}
使用 lambda 表达式简化后:1
2list.sort((a, b) -> b.compareTo(a));
list.forEach(System.out::println);
函数接口
定义:函数接口是只有一个抽象方法的接口,用于作为Lambda表达式的类型.
以下6个是 java 定义的6个常用函数接口
| 函数名 | 参数 | 返回类型 | 示例 |
|---|---|---|---|
| Predicate |
T | boolean | 判断合格 |
| Consumer |
T | void | 日志记录 |
| Function |
T | R | 获取对象成员 |
| Supplier |
None | T | 工厂方法 |
| UnaryOperator |
T | T | 小写转大写 |
| BinaryOperator |
(T,T) | T | 两数之和 |
Stream
- Stream是Java8中重要的特性之一,它是一组对集合的操作方法,使得程序员得以站在更高的抽象层次上对集合进行操作
- Stream中提供并行操作,使得对集合进行一些并行操作变得更加容易
示例
给字符串列表加前缀,并且转为数组,期间不能修改原来列表。
通常:1
2
3
4
5List<String> cityList = ....;
String[] cityArrays = new String[cityList.size()];
for (int i = 0; i < cityList.size(); i++) {
cityArrays[i] = pre + cityList.get(i);
}
使用Stream:1
2
3
4List<String> cityList = ....;
String[] cityArray = cityList.stream()
.map(city -> pre + city) // 惰性求值
.toArray(String[]::new); // 及早求值
使用Stream简化了代码,也使得业务流程更加清晰。
惰性求值是一个中间过程,它的返回值还是Stream。
及早求值是一个结束过程,它的返回值是相应的结果类型。
惰性求值一般表示Stream流还在继续,因此一条流中可以有多个惰性求值,而及早求值一般表示一条Stream流结束,因此一条流中最多只有一次及早求值。
注意:如果一条Stream调用链中,没有及早求值,该调用链实际是不执行的。
stream 方法
构建流对象的方式
Stream.of()
1
2
3
4
5
6public class StreamBuilders {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c", "d", "e", "f", "g");
stream.forEach(p -> System.out.println(p));
}
}
Collection.stream()
1
2
3
4
5
6
7public class StreamBuilders {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c", "d", "e", "f", "g");
Stream<String> stream = strings.stream();
stream.forEach(p -> System.out.println(p));
}
}Stream.generate()
1
2
3
4
5
6
7
8public class StreamBuilders {
public static void main(String[] args) {
Stream<Double> stream = Stream.generate(() -> {
return Math.random();
}).limit(5);
stream.forEach(p -> System.out.println(p));
}
}使用 iterate()
1
2
3
4
5
6public class StreamBuilders {
public static void main(String[] args) {
Stream<Integer> stream = Stream.iterate(10, i -> i + 10).limit(5);
stream.forEach(i -> System.out.println(i));
}
}使用 CharSequence.chars()
1
2
3
4
5
6public class StreamBuilders {
public static void main(String[] args) {
IntStream stream = "ABCDEFG_abcdefg".chars();
stream.forEach(p -> System.out.println(p));
}
}
转换流为集合
stream.collect(Collectors.toList()) 把 Stream 转换为 List
1
2
3
4
5
6
7
8public class StreamBuilders {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
List<Integer> oddNumbersList = stream.filter(i -> i % 2 != 0).collect(Collectors.toList());
System.out.print(oddNumbersList);
}
}stream.collect(Collectors.toSet()) 把 Stream 转换为 Set
1
2
3
4
5
6
7
8public class StreamBuilders {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
List<Integer> oddNumbersSet = stream.filter(i -> i % 2 != 0).collect(Collectors.toSet());
System.out.print(oddNumbersSet);
}
}stream.toArray(EntryType[]::new) 把 Stream 转换为数组
1
2
3
4
5
6
7
8public class StreamBuilders {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
Integer[] oddNumbersArray = stream.filter(i -> i % 2 != 0).toArray(Integer[]::new);
System.out.print(oddNumbersArray);
}
}
核心的流操作
我们先构建一个字符串集合,下面的例子都会基于这个集合进行操作。1
List<String> provinceNames = Arrays.asList("湖南", "湖北", "河南", "河北", "广东", "广西", "北京", "南京");
中间操作
中间操作返回流对象,所以你可以把多个中间操作串成一行。
filter()
- filter 方法接受一个断言对象,用来过滤流中的元素。
1
2
3
4provinceNames.stream().filter((p) -> p.startsWith("湖")).forEach(p -> System.out.print(p + ", "));
// 输出 :
// 湖南, 湖北,
- filter 方法接受一个断言对象,用来过滤流中的元素。
map()
- 这个中间操作使用给定的函数把流中的每个元素转换为另一个对象。下面的例子把每个字符串转化为另一个字符串(末尾多了一个人字)。但是你也可以把每个对象转换为其它类型的对象。
1
2
3
4provinceNames.stream().map(p -> p + "人, ").forEach(p -> System.out.print(p));
// 输出 :
// 湖南人, 湖北人, 河南人, 河北人, 广东人, 广西人, 北京人, 南京人,
- 这个中间操作使用给定的函数把流中的每个元素转换为另一个对象。下面的例子把每个字符串转化为另一个字符串(末尾多了一个人字)。但是你也可以把每个对象转换为其它类型的对象。
sorted()
- sorted 会对流中的元素进行排序,默认按自然序进行排序,除非你指定一个自定义的比较器(Comparator) 。
1
2
3
4provinceNames.stream().sorted().map(p -> p + "人, ").forEach(p -> System.out.print(p));
// 输出 :
// 北京人, 南京人, 广东人, 广西人, 河北人, 河南人, 湖北人, 湖南人,
- sorted 会对流中的元素进行排序,默认按自然序进行排序,除非你指定一个自定义的比较器(Comparator) 。
需要注意的是 sorted 仅仅对流中的元素进行排序,而不会影响后面的集合,集合中的元素排序保持不变。
末端操作
末端操作返回某一类型的结果,而不是流对象。
forEach()
- 该方法帮助迭代流中的元素并在元素上执行一些操作。这些操作可以是 lambda 表达式或者方法引用。
1
provinceNames.stream().forEach(System.out::println);
- 该方法帮助迭代流中的元素并在元素上执行一些操作。这些操作可以是 lambda 表达式或者方法引用。
collect()
- collect() 方法用来从流中抽取元素然后保存到一个集合或者数组中。
1
2
3
4List<String> provinceNamesOrdered = provinceNames.stream().sorted().collect(Collectors.toList());
System.out.print(provinceNamesOrdered);
// 输出 :
// [北京, 南京, 广东, 广西, 河北, 河南, 湖北, 湖南]
- collect() 方法用来从流中抽取元素然后保存到一个集合或者数组中。
match()
- 各种匹配操作用来检查流中的元素是否指定断言条件。所有的匹配操作都是末端操作,它们返回布尔结果。
1
2
3
4
5
6
7
8
9
10
11
12
13boolean matchedResult = provinceNames.stream().anyMatch((s) -> s.startsWith("湖"));
System.out.println(matchedResult);
matchedResult = provinceNames.stream().allMatch((s) -> s.startsWith("湖"));
System.out.println(matchedResult);
matchedResult = provinceNames.stream().noneMatch((s) -> s.startsWith("湖"));
System.out.println(matchedResult);
// 输出 :
// true
// false
// false
- 各种匹配操作用来检查流中的元素是否指定断言条件。所有的匹配操作都是末端操作,它们返回布尔结果。
count()
- count 末端操作返回流中元素个数。
1
2
3
4long totalMatched = provinceNames.stream().count();
System.out.println(totalMatched);
// 输出 : 8
- count 末端操作返回流中元素个数。
reduce()
- 该末端操作使用给定的函数对流中的元素进行归约。它是这样一个过程:每次迭代,将上一次的迭代结果(第一次时为 identity 的元素,如没有 identity 则为流中的第一个元素)与下一个元素一同执行一个二元函数。在 reduce 操作中,identity 是可选的,如果使用,则作为第一次迭代的第一个元素使用。归约的结果保存在 Optional 中。
1
2
3
4
5Optional<String> reduced = provinceNames.stream().reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// 输出 :
// 湖南#湖北#河南#河北#广东#广西#北京#南京
- 该末端操作使用给定的函数对流中的元素进行归约。它是这样一个过程:每次迭代,将上一次的迭代结果(第一次时为 identity 的元素,如没有 identity 则为流中的第一个元素)与下一个元素一同执行一个二元函数。在 reduce 操作中,identity 是可选的,如果使用,则作为第一次迭代的第一个元素使用。归约的结果保存在 Optional 中。
短路操作
流操作通常会在流中满足某一断言的所有元素上进行操作,有时我们希望在迭代过程中遇到匹配的元素时就终止操作,在外部迭代中,你需要写 if-else 代码块,在内部迭代中,有现成的方法可以使用,下面是两个这样的方法的示例:
anyMatch()
- 该操作只要遇到满足断言条件的元素就会返回 true,在此之后就不再处理任何其它的元素了。
1
2
3
4boolean matched = provinceNames.stream().anyMatch((s) -> s.startsWith("河"));
System.out.println(matched);
// 输出 : true
- 该操作只要遇到满足断言条件的元素就会返回 true,在此之后就不再处理任何其它的元素了。
findFirst()
- 该操作会返回流中的第一个元素,然后就不再处理其它元素了。
1
2
3
4String firstMatchedName = provinceNames.stream().filter((s) -> s.startsWith("广")).findFirst().get();
System.out.println(firstMatchedName);
// 输出 : 广东
- 该操作会返回流中的第一个元素,然后就不再处理其它元素了。
Optional 接口
Optional 被定义为一个简单的容器,其值可能是null或者不是null
在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null
在Java 8中,不推荐你返回null而是返回Optional
1 | String temp = "temp"; |
将电话号码列表转化为以逗号隔开的字符串,如果为空则抛出异常1
2
3String phoneNumber = subList.stream()
.reduce((a, b) -> a + "," + b)
.orElseThrow(Exception::new);