JDK8-2-流(2)- 流操作
上篇 JDK8-2-流(1)-简介 中简单介绍了什么是流以及使用流的好处,本篇主要介绍流的操作类型以及如何操作。
如何返回一个流
① collection.stream
即调用集合 java.util.Collection 下的 stream 方法
List<String> list = Arrays.asList("a","b","c");
list.stream();
② 数组 Arrays.stream
String[] a = new String[]{"a", "b", "c"};
Arrays.stream(a);
③ Stream.of
String[] b = new String[]{"a", "b", "c"};
Stream.of(b);
这种方式与②类似,它内部也是调用 Arrays.stream 方法
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
操作流
以上篇中例子说明:
menu.stream()
.filter(dish -> dish.getCalories() < 400)
.sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName)
.collect(Collectors.toList());
filter,sorted,map 方法称为中间操作,collect 称为终端操作
中间操作
诸如filter或sorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。
filter
过滤流中元素,保留需要的元素
Stream<T> filter(Predicate<? super T> predicate);
.filter(dish -> dish.getCalories() < 400)
map
将类型为 T 的流转换成类型为 R 的流
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
例如,将 Dish 类型的流转换成 String 类型的流
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(Collectors.toList());
distinct
JDK8-2-流(2.1)- 流操作-distinct
sorted
对流中的元素排序
Stream<T> sorted(Comparator<? super T> comparator);
REPEATED_DISHES.stream()
.sorted(Comparator.comparing(Dish::getCalories))
.collect(Collectors.toList());
limit (截断流)
表示截断流中元素,只保留前面 maxSize 个
Stream<T> limit(long maxSize);
List<String> strList = Arrays.asList("a", "b", "c").stream().limit(1).collect(Collectors.toList());
System.out.println(strList);
结果:
[a]
skip (跳过元素)
表示跳过流中前 n 个元素,返回一个扔掉了前n个元素的流,如果流中元素不足n个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!
Stream<T> skip(long n);
List<String> strList2 = Arrays.asList("a", "b", "c").stream().skip(1).collect(Collectors.toList());
System.out.println(strList2);
结果:
[b, c]
假设代码改成如下这样,跳过前 5 个元素,
List<String> strList2 = Arrays.asList("a", "b", "c").stream().skip(5).collect(Collectors.toList());
由于集合中一共只有3个元素,所以结果返回一个空的列表
[]
flatMap (流的扁平化处理)
JDK8-2-流(2.2)- 流操作-flatMap
终端操作
终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如List、Integer,甚至void。
forEach
遍历流中的元素
void forEach(Consumer<? super T> action);
List<String> str = Arrays.asList("Hello", "World");
str.stream().forEach(s -> System.out.println(s));
结果:
Hello
World
count
统计流中元素个数
long count();
List<String> str = Arrays.asList("Hello", "World");
long length = str.stream().count();
System.out.println(length);
结果:
2
min
筛选出流中最小元素,入参为 Comparator ,返回一个 Optional,关于 Optional 可以参考JDK8-10-Optional(1)
Optional<T> min(Comparator<? super T> comparator);
List<Integer> numbers = Arrays.asList(3, 2, 5, 1);
Optional<Integer> optional = numbers.stream().min((Comparator.comparingInt(o -> o)));
System.out.println(optional.get());
结果:
1
max
和 min 类似,max 表示筛选出流中最大元素,入参为 Comparator ,返回一个 Optional
Optional<T> max(Comparator<? super T> comparator);
List<Integer> numbers = Arrays.asList(3, 2, 5, 1);
Optional<Integer> optional = numbers.stream().max((Comparator.comparingInt(o -> o)));
System.out.println(optional.get());
结果:
5
collect
意为将流的元素转换成集合,主要可以转换成如下三种集合:
- Collectors.toList() 转换成 List
- Collectors.toSet() 转换成 Set
- Collectors.toMap 转换成 Map
Collectors.toList
将字符串列表转换成 Integer 列表
List<Integer> lenList = Arrays.asList("Hello", "World")
.stream()
.map(String::length)
.collect(Collectors.toList());
Collectors.toSet
将流中元素转换成 Set 集合
Set<String> strList = Arrays.asList("Hello", "World", "Hello")
.stream()
.collect(Collectors.toSet());
System.out.println(strList);
结果:
[Hello, World]
Collectors.toMap
将流中元素转换成 Map
如下例:
将 Dish 流中元素转成 key 为 name,value 为 type 的 Map
public static void toMapTest() {
List<Dish> dishes = Arrays.asList(
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER));
Map<String, String> map = dishes.stream().collect(Collectors.toMap(Dish::getName, d -> d.getType().toString()));
System.out.println(map);
}
注意:如果程序在转换过程中发现有重复的 key 值,按照上面那种写法会报如下错误:
所以需要对 key 去重,如下:
private static <T> Predicate<T> distinctByKey(Function<? super T, String> keyExtractor) {
Set<String> set = ConcurrentHashMap.newKeySet();
return t -> set.add(keyExtractor.apply(t));
}
Map<String, String> map = dishes.stream()
.filter(distinctByKey(Dish::getName))
.collect(Collectors.toMap(Dish::getName, d -> d.getType().toString()));
打印结果:
{chicken=MEAT, french fries=OTHER}
allMatch
判断流中的元素是否都匹配某个条件
boolean allMatch(Predicate<? super T> predicate);
如下,判断数组中数字是否都为偶数:
boolean allNumbersAreEven = Arrays.asList(1, 2, 3)
.stream()
.allMatch(a -> a % 2 == 0);
System.out.println(allNumbersAreEven);
结果:
false
anyMatch
判断流中是否有元素满足某个条件
boolean anyMatch(Predicate<? super T> predicate);
如下,判断数组中数字是否有一个为偶数:
public static void anyMatchTest() {
boolean allNumbersAreEven = Arrays.asList(1, 2, 3)
.stream()
.anyMatch(a -> a % 2 == 0);
System.out.println(allNumbersAreEven);
}
结果:
true
noneMatch
判断流中元素是不是都不满足条件,如果都不满足则返回 true,否则返回 false
boolean noneMatch(Predicate<? super T> predicate);
如下,判断数组中数字是否都不是偶数:
public static void noneMatchTest() {
boolean flag = Arrays.asList(1, 3, 5)
.stream()
.noneMatch(a -> a % 2 == 0);
System.out.println(flag);
}
由于数组中数字都为 奇数 ,而判断条件是偶数,所以数组中没有数字满足条件,所以结果为 true :
true
findAny (查找任意元素)
返回流中任意一个元素的 Optional 包装对象
Optional<T> findAny();
Optional<Integer> optional = Arrays.asList(1, 2, 3)
.stream()
.findAny();
System.out.println(optional.get());
findFirst (查找第一个元素)
返回流中第一个元素的 Optional 包装对象
Optional<T> findFirst();
Optional<Integer> optional = Arrays.asList(1, 2, 3)
.stream()
.findFirst();
System.out.println(optional.get());
reduce (归约)
JDK8-2-流(2.3)- 流操作-reduce (归约)