开发使用peek ()和map()方法(包括 map()方法的基本版本)从对象中提取数据的代码。使用Collectors类collect方法和组/分区数据将结果保存到集合中。使用Stream API的merge()和flatMap()方法。
peek()
执行提供的 Consumer,并返回一个新的流,其元素与原始流的元素相同。
Stream<T> peek(Consumer<? super T> action)
大多数情况下,这个方法与 System.out.println ()一起用于调试目的(查看流中的内容)
System.out.format("\n%d",
IntStream.of(1, 2, 3, 4, 5, 6)
.limit(3)
.peek( i ->
System.out.format("%d ", i) )
.sum() );
Stream.of("A", "B", "C", "D").forEach(i-> System.out.println(i+i));
List<String> collect1 = Stream.of("A", "B", "C", "D").peek(i -> i = i + i).collect(Collectors.toList());
System.out.println(collect1);
输出还是ABCD没有改变
peek ()用于查看流在管道的特定点上的元素,以任何方式更改流都被认为是不好的做法
peek和forEach打印
- peek ()是一个中间操作,还可以调用其他Stream方法
forEach()是终端结束操作,之后不能调用Stream方法。
map()
转换流中元素的值或类型
<R> Stream<R> map(Function<? super T,? extends R> mapper)
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
使用Function接口用于转换成不同类型的元素,除了map方法外。其他转换为基本类型
Stream.of('a', 'b', 'c', 'd', 'e')
.map(c -> (int) c)
.forEach(i -> System.out.format("%d ", i));
IntStream.of(100, 110, 120, 130, 140)
.mapToDouble(i -> i / 3.0)
.forEach(i -> System.out.format("%.2f ", i));
flatMap()
将一个流的元素“扁平化”(或合并)为一个(新的)流:
<R> Stream<R> flatMap(Function<? super T,
? extends Stream<? extends R>> mapper)
DoubleStream flatMapToDouble(Function<? super T,
? extends DoubleStream> mapper)
IntStream flatMapToInt(Function<? super T,
? extends IntStream> mapper)
LongStream flatMapToLong(Function<? super T,
? extends LongStream> mapper)
flatMap ()如果返回null,则empty的Stream
//将Stream<List<Character>> 转换为Stream<Character>
stream.flatMap(Collection::stream)
.map(c -> (int) c)
.forEach(i -> System.out.format("%d ", i));
结果如上图所示,如果不转化因为对象类型不对,所以map()内的方法无法执行
map和flatmap的区别
- map ()和 flatMap ()都以一个函数作为参数,但是每个函数都有不同的参数
- flatMap ()用于将多个stream合并后取出里面元素生成一个新的stream
Reduction
将许多元素组合成单个值或对象的操作,它是通过多次应用一个操作来完成的。有2大方法
reduce()
collect()
reduce()
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity,
BinaryOperator<T> accumulator)
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
- 累加
1,无初始值
对 n 元素求和,找到 n 个数的最大元素,或者计数元素
1.传统方法
int[] numbers = {1, 2, 3, 4, 5, 6};
int sum = 0;
for(int n : numbers) {
sum += n;
}
2.Stream流
OptionalInt total = IntStream.of(1, 2, 3, 4, 5, 6)
.reduce( (sum, n) -> sum + n );
2.有初始值
int total = IntStream.of(1, 2, 3, 4, 5, 6)
.reduce( 0,
(sum, n) -> sum + n ); // 21
int total = IntStream.of(1, 2, 3, 4, 5, 6)
.reduce( 4,
(sum, n) -> sum + n ); // 25
可以获取类型为 t 的元素,并返回类型为 t 的简化值
3.返回不同类型
<U> U reduce(U identity,
BiFunction<U,? super T, U> accumulator,
BinaryOperator<U> combiner)
等价于
U result = identity;
for (T element : stream) {
result = accumulator.apply(result, element)
}
return result;
- 例
我们想要得到一个流中所有字符串长度的和,所以我们得到字符串(类型 t) ,并且我们想要一个整数结果(类型 u)。
int length =
Stream.of("Parallel", "streams", "are", "great")
.reduce(0,
(accumInt, str) ->
accumInt + str.length(), //accumulator
(accumInt1, accumInt2) ->
accumInt1 + accumInt2);//combiner
int length =
Stream.of("Parallel", "streams", "are", "great")
.reduce(0,
(Integer accumInt, String str) ->
accumInt + str.length(), //accumulator
(Integer accumInt1, Integer accumInt2) ->
accumInt1 + accumInt2);//combiner
accumulator 函数向 accumulator 函数添加了一个映射(转换)步骤,所以 reduce ()的这个版本可以写成 map ()和 reduce ()方法的其他版本的组合
int length3 =
Stream.of("Parallel", "streams", "are", "great")
.mapToInt(String::length)
.reduce(0,Integer::sum);
reduce()使用情况划分
- 2个参数版本
返回一个相同类型的值,使用两个参数版本 - 3个参数版本
1.处理并行Stream
- 使用一个函数作为映射器和累加器比使用单独的映射和缩减函数更有效率
collect()
其操作的元素对象可进行累加
// Collectors 类中预定义的收集器
<R,A> R collect(Collector<? super T,A,R> collector)
//自定义收集器
<R> R collect(Supplier<R> supplier,
BiConsumer<R,? super T> accumulator,
BiConsumer<R,R> combiner)
三参数版本的collect
等价于
R result = supplier.get();
for (T element : stream) {
accumulator.accept(result, element);
}
return result;
List<Integer> list =
Stream.of(1, 2, 3, 4, 5)
.collect(
() -> new ArrayList<>(),// Creating the container
(l, i) -> l.add(i), // Adding an element
(l1, l2) -> l1.addAll(l2) // Combining elements
);
1个参数版本collect(Collectors.method所有方法均为静态)
常见的Collectors方法
- toList
- toSet
- toCollection
- toMap
- joining 返回String
将元素append成String - groupingBy 返回Map<K, List
>
将元素T根据分类函数放入映射的K,V - partitioningBy 返回Map<Boolean, List
>
List<Integer> list =
Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.toList()); // [1, 2, 3, 4, 5]
所有这些方法都是静态的,我们可以使用静态导入
import static java.util.stream.Collectors.*;
...
List<Integer> list =
Stream.of(1, 2, 3, 4, 5)
.collect(toList()); // [1, 2, 3, 4, 5]
使用
- 收集元素到一个集合
Set<Integer> set =
Stream.of(1, 1, 2, 2, 2)
.collect(toSet()); // [1, 2]
- 创建另一个集合实现:
Deque<Integer> deque =
Stream.of(1, 2, 3)
//利用方法引用构造方法生成新的类型集合
.collect( Collectors.toCollection(ArrayDeque::new)
); // [1, 2, 3,]
3.拼接String
String s = Stream.of("a", "simple", "string")
.collect(Collectors.joining()); // "asimplestring"
4.分个字符串
String s1= Stream.of("a", "simple", "string")
.collect(Collectors.joining(" ")); // " a simple string"
toMap ()
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
2个入参
toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper)
Function函数接口都以流的一个元素作为参数,并返回 Map 条目的键或值
Map<Integer, Integer> collect = Stream.of(1, 2, 3, 4, 5).collect(toMap(Function.identity(), Function.identity()));
如果多个v映射到同一个k,则会抛出异常
3个入参版本
toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
第三个参数是一个函数,它定义了当存在重复键时应该做什么
在此例中,定义的是当k相同时,其V为所对应的原始数据的和,即K为1的V为1+1=2
Map<Integer, Integer> collect2 = Stream.of(1, 2, 3, 4, 1).collect(Collectors.toMap(Function.identity(), Function.identity(),(Integer::sum)));
4个入参版本
数加上一个返回一个新的空 Map 的参数,结果将插入到这个参数中
toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
可以将默认的实现(HashMap)改为 ConcurrentHashMap
groupingBy ()
类似于 sql groupby 样式的方式将Stream的元素分组
版本一
groupingBy(Function<? super T,? extends K> classifier)
它接受一个Function,该Function将 t 类型的元素分类,将它们分组到一个列表中,并在 Map 中返回结果,其中键(k)是函数返回的值
- 例
对数字进行的十位数为一组,进行分类
1.传统方法
List<Integer> stream =
Arrays.asList(2, 34, 54, 23, 33, 20, 59, 11, 19, 37);
Map<Integer, List<Integer>> map = new HashMap<>();
for (Integer i : stream) {
int key = i / 10 * 10;
List<Integer> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(i);
}
2.stream
Map<Integer, List<Integer>> map1 =
Stream.of(2, 34, 54, 23, 33, 20, 59, 11, 19, 37)
.collect(groupingBy(i -> i / 10 * 10));
niubility!!!省时省力
版本二
groupingBy(Function<? super T,? extends K> classifier,
Collector<? super T,A,D> downstream)
接受另一个Collecor进行计算
Map<Integer, Long> map =
Stream.of(2, 34, 54, 23, 33, 20, 59, 11, 19, 37)
.collect(
groupingBy(i -> i/10 * 10,
counting()
)
);
对分组后的v值再进行计算
版本三
添加了一个 Supplier 参数,可以选择结果 Map 的类型
groupingBy(Function<? super T,? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T,A,D> downstream)
传递一个TreeMap,实现排序
Map<Integer, Map<String, List<Integer>>> map5 =
Stream.of(2,34,54,23,33,20,59,11,19,37)
.collect(groupingBy(i -> i/10 * 10,
TreeMap::new,
groupingBy(i ->
i%2 == 0 ? "偶数" : "奇数")
)
);
partitioningBy()
partitioningBy ()将返回一个带有boolean作为键类型的 Map,这意味着只有两个组,一个用于 true,另一个用于 false
版本一
partitioningBy(Predicate<? super T> predicate)
根据 Predicate 将元素分区,并将它们组织成 Map < Boolean,List < t > 。
Map<Boolean, List<Integer>> map =
Stream.of(45, 9, 65, 77, 12, 89, 31)
.collect(partitioningBy(i -> i < 50));
版本二
接受另一个Collecor进行计算
partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream)
Map<Boolean, Set<Integer>> map =
Stream.of(45, 9, 65, 77, 12, 89, 31, 12)
.collect(
partitioningBy(i -> i < 50,
toSet()
)
);
总结
-
peek()执行提供的使用者并返回一个与原始流元素相同的新流。大多数情况下,此方法用于调试目的。
-
map()用于通过提供的函数转换流元素的值或类型。
-
flatMap()用于“展平”或将流的内容合并到另一个(新)流中。与map()不同的是,flatMap()本身在流对象上使用一个函数;而map()对其流中的对象执行函数。map()和flatMap()都返回某种流。
-
归约是一种将许多元素合并为一个值或对象的操作。
-
reduce()使用累加函数、可选标识和可选组合器函数对流的元素执行缩减。
-
Collectors类提供了静态方法(如toList()和toMap())来从流创建集合或映射,以及一些计算方法。
-
Collectors.groupingBy()使用给定的函数作为分类器对流的元素进行分组。它还可以接收下游收集器以创建另一级别的分类。
-
Collectors.partitioningBy()方法根据条件(谓词)对流中的元素进行分组(或分区)。
Comments | 0 条评论