Stream
Stream
1. 概念
-
util包下的Stream是Java8的新特性,与io包下的 InputStream 和 OutputStream 是完全不同的概念。Java8的Stream是对集合(Collection)对象功能的增强,专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。
-
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
-
而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
2. 构成
当我们使用一个流的时候,通常包括三个基本步骤:
- 获取一个数据源(source)
- 数据转换
- 执行操作获取想要的结果
每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

①数据源
有多种方式生成数据源
- 从集合、数组
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream()
- Stream.of(T values):T可以为集合或者数组
- BufferedReader
- java.io.BufferedReader.lines()
- 静态工厂
- java.util.stream.IntStream.range()
- java.nio.file.Files.walk()
需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long>、Stream<Double>,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的Stream。
Java 8 中还没有提供其它数值型 Stream,因为这将导致扩增的内容较多。而常规的数值型聚合运算可以通过上面三种 Stream 进行。
②操作类型
- Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
- Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
还有一种操作被称为 short-circuiting。用以指:
- 对于一个intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新Stream。
- 对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。
③示例
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
-
widgets.stream()为获取数据源 -
filter(w -> w.getColor() == RED)和mapToInt(w -> w.getWeight())为intermediate 操作 -
sum()为Terminal操作
④总结
简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect)。
⑤插件
Java Stream Debugger
插件地址:https://plugins.jetbrains.com/plugin/9696-java-stream-debugger
可以查看stream链中间操作的数据状态。

3.Intermediate
常见的Intermediate操作如下:
- Intermediate:filter、map (mapToInt, flatMap 等)、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
- Short-circuiting:anyMatch、 allMatch、 noneMatch、findFirst、 findAny、 limit
①filter
过滤,可以通过 filter 方法将一个流转换成另一个子集流。
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素。
示例:
设置元素的长度必须大于5
public static void main(String[] args) {
Stream<String> original = Stream.of("Java", "C", "Python", "Hadoop", "Spark");
Stream<String> result = original.filter(s -> s.length() >= 5);
result.forEach(System.out::println);
}
Python
Hadoop
Spark
②map
映射,如果需要将流中的元素映射到另一个流中,可以使用map方法。该接口需要传入一个Function函数式接口,可以将当前流中的T类型数据转换为另一种R类型的流。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
Function接口只有一个方法,就是将传入的T类型转换成R类型。
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
示例:
将字符串流转换成整数流
public static void main(String[] args) {
String[] stringArray = {"1", "2","3","4"};
Stream<String> stringStream = Arrays.stream(stringArray);
Stream<Object> objectStream = stringStream.map(new Function<String, Object>() {
@Override
public Object apply(String s) {
return Integer.valueOf(s);
}
});
}
1
2
3
4
lambda
public static void main(String[] args) {
String[] stringArray = {"1", "2","3","4"};
Stream<String> stringStream = Arrays.stream(stringArray);
Stream<Integer> integerStream = stringStream.map(Integer::valueOf);
integerStream.forEach(System.out::println);
}
③flatMap
扁平的映射,可以将一个2维的集合映射成一个一维,相当于他映射的深度比map深了一层 。
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
示例:
["Hello","World"]操作,返回["H","e","l","o","W","r","d"]
public static void main(String[] args) {
String[] words = new String[]{"Hello","World"};
Stream<String[]> doubleStream = Arrays.stream(words).map(word -> word.split(""));
Stream<String> singleStream = doubleStream.flatMap(Arrays::stream);
singleStream.forEach(System.out::print);
}
helloword
总结:使用flatMap方法的效果是,各个数组并不是分别映射一个流,而是映射成流的内容,所有使用map(Array::stream)时生成的单个流被合并起来,即扁平化为一个流。
④distinct
按照 Object.equals(Object)进行去重
Stream<T> distinct();
⑤sorted
通过元素的compareTo()方法进行排序,元素所在的类必须要实现Comparable接口
Stream<T> sorted();
如果不想让元素所在类实现Comparable接口,可以传入一个比较器
Stream<T> sorted(Comparator<? super T> comparator);
⑥peek
有点像迭代的forEach,传入每个元素,做一些操作,没有返回值。
与map的区别要注意,map是有返回值的,可以将传入类型转成其它类型。
与forEach的区别要注意,forEach是Terminal中的操作,结束该stream链,而peek是intermediate操作,返回的还是Stream对象。
Stream<T> peek(Consumer<? super T> action);
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
⑦limit
取前maxSize个
对集合进行截取,参数maxSize是limit返回的流中的最大元素数。
Stream<T> limit(long maxSize);
⑧skip
跳过前N个
此方法以一个长(N)作为参数,并在删除前N个元素后返回流。
如果N的值很大,则在有序并行管道上skip()可能会非常昂贵,因为skip(N)被约束为跳过遇到顺序中的前N个元素,而不仅仅是任何n个元素。
Stream<T> skip(long n);
⑨parallel
将该stream设置为并行流,返回一个并行流。
Returns whether this stream, if a terminal operation were to be executed, would execute in parallel. Calling this method after invoking an terminal stream operation method may yield unpredictable results.
Returns:
true if this stream would execute in parallel if executed
S parallel();
⑩sequential
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
S sequential();
⑪unordered
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
S unordered();
4.Terminal
常见的Terminal操作如下:
- Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、noneMatch、 findFirst、 findAny、 iterator
①forEach
-
该方法接收一个Consumer接口函数,会将每一个流元素交给函数进行处理。
-
Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据。
-
如果将forEach()方法与并行流一起使用,则不会维护遇到顺序。 该操作将在每个元素上执行,但是它们的顺序不会固定。
void forEach(Consumer<? super T> action);
②forEachOrdered
- 如果要按遇到顺序对流元素执行某些操作,则应使用forEachOrdered()方法。
void forEachOrdered(Consumer<? super T> action);
③toArray
将流转换成数组
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}
示例:
List<String> strs = Arrays.asList("a", "b", "c");
String[] dd = strs.stream().toArray(str -> new String[strs.size()]);
String[] dd1 = strs.stream().toArray(String[]::new);
Object[] obj = strs.stream().toArray();
④reduce
减少,缩小。根据指定的计算模型将Stream中的值计算得到一个最终结果
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);
接收两个参数T和U,返回一个R
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
示例一:
Optional<T> reduce(BinaryOperator<T> accumulator)实现累加器。
该函数式接口需要两个参数,返回一个结果(reduce中返回的结果会作为下次累加器计算的第一个参数),也就是累加器。
@Test
public void reduceTest() {
Optional accResult = Stream.of(1, 2, 3, 4).reduce((acc, item) -> {
System.out.println("acc : " + acc);
acc += item;
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("--------");
return acc;
});
System.out.println(accResult);
}

示例二:
T reduce(T identity, BinaryOperator<T> accumulator)
- 提供一个跟Stream中数据同类型的初始值identity,通过累加器accumulator迭代计算Stream中的数据,得到一个跟Stream中数据相同类型的最终结果
public class ReduceDemo {
@Test
public void reduceTest() {
int accResult = Stream.of(1, 2, 3, 4)
.reduce(100, (acc, item) -> {
System.out.println("acc : " + acc);
acc += item;
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("--------");
return acc;
});
System.out.println(accResult);
}
}

⑤collect
可以收集流中的数据到集合或者数组中去。
最为核心,需要单独开一章节。
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
⑥min-max
需要传入一个comparator
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
示例:
Optional min = stream.min(Integer::compareTo);
将Integer的compareTo方法传入compare方法中,并调用compareTo方法
返回值为Optional类
Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都非常了解的异常。
这个类型的对象可能包含值,也可能为空。
⑦count
返回此流的元素总数
long count();
⑧anyMatch-allMatch-noneMatch
- anyMatch,断条件里的元素,任意一个元素匹配,返回true
- allMatch,判断条件里的元素,所有的匹配,返回true
- noneMatch,判断条件里的元素,所有的都不匹配,返回true
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Predicate中放入一些条件
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*/
boolean test(T t);
⑨findFirst-findAny
- findFirst,返回第一个元素
- findAny,随机的返回一个元素
Optional<T> findFirst();
Optional<T> findAny();
5.静态方法
①concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
示例:
public static void main(String[] args) {
Stream<String> original1 = Stream.of("Java", "C", "Python");
Stream<String> original2 = Stream.of("Hadoop", "Spark");
Stream<String> result = Stream.concat(original1, original2);
result.forEach(System.out::println);
}
Java
C
Python
Hadoop
Spark
②generate
传递生成流元素的供应商(Supplier),返回一个新的无限顺序无序的流(Stream)。
由于长度是无限的,通常和limit搭配使用。
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
示例:
Stream<Integer> stream = Stream.generate(() -> new Random().nextInt(10));
stream.forEach(e -> System.out.println(e));
2
5
1
.
.
.
public class LimitGenerateDemo {
public static void main(String[] args) {
Stream.generate(() -> new Random().nextInt(10)).limit(3)
.forEach(e -> System.out.println(e));
Stream.generate(() -> new Random().nextBoolean()).limit(3)
.forEach(e -> System.out.println(e));
Stream.generate(() -> "Hello World!").limit(3)
.forEach(e -> System.out.println(e));
}
}
③iterate
指定一个常量seed,生成从seed到常量f(由UnaryOperator返回的值得到)的流。
由于长度是无限的,通常和limit搭配使用。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
示例:
public static void main(String[] args) {
Stream.iterate(0, n -> n + 1).limit(5).forEach(a -> {
System.out.println(a);
});
}
0
1
2
3
4
5
6.collect详解
Java-Collectors常用的20个方法 - niocoder - 博客园 (cnblogs.com)
可以收集流中的数据到集合或者数组中去。需要传入一个Collector接口。
<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);
Collectors类中有很多静态方法,可以提高各式各样的Collector对象
①转换
- toList:用于将元素累积到
List集合中。它将创建一个新List集合(不会更改当前集合)。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
integers.stream().map(x -> x*x).collect(Collectors.toList());
// output: [1,4,9,16,25,36,36]
- toSet:用于将元素累积到
Set集合中。它会删除重复元素。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
integers.stream().map(x -> x*x).collect(Collectors.toSet());
// output: [1,4,9,16,25,36]
- toMap:根据集合的值创建
Map。
创建了一个Map,其中集合值作为key,在集合中的出现次数作为值。
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<String,Integer> map = strings
.stream()
.collect(Collectors
.toMap(Function.identity(),String::length));
// output: {alpha=5, beta=4, gamma=5}
- toCollection:可以将元素累积到指定的集合中。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
integers
.stream()
.filter(x -> x >2)
.collect(Collectors.toCollection(LinkedList::new));
// output: [3,4,5,6,6]
- toUnmodifiableList:用于创建只读
List集合。任何试图对此不可修改List集合进行更改的尝试都将导致UnsupportedOperationException。
List<String> strings = Arrays.asList("alpha","beta","gamma");
List<String> collect2 = strings
.stream()
.collect(Collectors.toUnmodifiableList());
// output: ["alpha","beta","gamma"]
- toUnmodifiableSet:返回不可修改的Set集合。
List<String> strings = Arrays.asList("alpha","beta","gamma","alpha");
Set<String> readOnlySet = strings
.stream()
.sorted()
.collect(Collectors.toUnmodifiableSet());
// output: ["alpha","beta","gamma"]
②数学计算
- Counting:用于返回计算集合中存在的元素个数。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
Long collect = integers
.stream()
.filter(x -> x <4)
.collect(Collectors.counting());
// output: 3
- minBy:用于返回列表中存在的最小值。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
List<String> strings = Arrays.asList("alpha","beta","gamma");
integers
.stream()
.collect(Collectors.minBy(Comparator.naturalOrder()))
.get();
// output: 1
strings
.stream()
.collect(Collectors.minBy(Comparator.naturalOrder()))
.get();
// output: alpha
按照整数排序返回1,按照字符串排序返回alpha
可以使用reverseOrder()方法反转顺序。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
List<String> strings = Arrays.asList("alpha","beta","gamma");
integers
.stream()
.collect(Collectors.minBy(Comparator.reverseOrder()))
.get();
// output: 6
strings
.stream()
.collect(Collectors.minBy(Comparator.reverseOrder()))
.get();
// output: gamma
- maxBy:和最小值方法类似,使用
maxBy()方法来获得最大值。
List<String> strings = Arrays.asList("alpha","beta","gamma");
strings
.stream()
.collect(Collectors.maxBy(Comparator.naturalOrder()))
.get();
// output: gamma
- averagingLong:查找
Long类型集合的平均值。注意:返回的是Double类型而不是Long类型
List<Long> longValues = Arrays.asList(100L,200L,300L);
Double d1 = longValues
.stream()
.collect(Collectors.averagingLong(x -> x * 2));
// output: 400.0
-
averagingInt
-
averagingDouble
-
averagingLong:
-
summingInt :
查找集合中所有整数的和。它并不总是初始集合的和,就像我们在下面的例子中使用的我们使用的是字符串列表,首先我们把每个字符串转换成一个等于它的长度的整数,然后把所有的长度相加。
List<String> strings = Arrays.asList("alpha","beta","gamma");
Integer collect4 = strings
.stream()
.collect(Collectors.summingInt(String::length));
// output: 18
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
Integer sum = integers
.stream()
.collect(Collectors.summingInt(x -> x));
// output: 27
- summingDouble
- **summingLong **
- summarizingInt :它给出集合中出现的值的所有主要算术运算值,如所有值的平均值、最小值、最大值、所有值的计数和总和。
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,6);
IntSummaryStatistics stats = integers
.stream()
.collect(Collectors.summarizingInt(x -> x ));
//output: IntSummaryStatistics{count=7, sum=27, min=1, average=3.857143, max=6}
可以使用get方法提取不同的值:
stats.getAverage(); // 3.857143
stats.getMax(); // 6
stats.getMin(); // 1
stats.getCount(); // 7
stats.getSum(); // 27
③链接
- Joining:用指定的字符串链接集合内的元素。
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
return joining(delimiter, "", "");
}
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
return new CollectorImpl<>(
() -> new StringJoiner(delimiter, prefix, suffix),
StringJoiner::add, StringJoiner::merge,
StringJoiner::toString, CH_NOID);
}
示例:
List<String> strings = Arrays.asList("alpha","beta","gamma");
String collect3 = strings
.stream()
.distinct()
.collect(Collectors.joining(","));
// output: alpha,beta,gamma
String collect4 = strings
.stream()
.map(s -> s.toString())
.collect(Collectors.joining(",","[","]"));
// output: [alpha,beta,gamma]
④分组
- GroupingBy:是一种高级方法,用于从任何其他集合创建
Map。
示例:
将字符串长度作为key,并将该长度的字符串列表作为value。
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<Integer, List<String>> collect = strings
.stream()
.collect(Collectors.groupingBy(String::length));
// output: {4=[beta, beta], 5=[alpha, gamma]}
还可以指定Map中需要的列表类型(Libkedlist)。
List<String> strings = Arrays.asList("alpha","beta","gamma");
Map<Integer, LinkedList<String>> collect1 = strings
.stream()
.collect(Collectors.groupingBy(String::length,
Collectors.toCollection(LinkedList::new)));
// output: {4=[beta, beta], 5=[alpha, gamma]}
- partitioningBy:返回的map的key是booelan类型的,也就是说会把流分成两类
示例:
map 中有两个key,一个是大于3的true,一个是小于3的false
public static void main(String[] args)
{
Stream<Integer>
s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Boolean, Long>
map = s.collect(
Collectors.partitioningBy(
num -> (num > 3), Collectors.counting()));
}
}
7.去重详解
list根据元素的某个字段或者多个字段去重
①toMap
利用Collectors.toMap去重
List<Person> personList = new ArrayList<>();
personList.add(new Person("one", 10));
personList.add(new Person("two", 20));
personList.add(new Person("three", 30));
//这两的name值重复
personList.add(new Person("four", 40));
personList.add(new Person("four", 45));
System.out.println("利用Collectors.toMap去重:");
//利用Collectors.toMap去重
personList.stream()
.collect(Collectors.toMap(Person::getName, Function.identity(), (oldValue, newValue) -> oldValue))
.values()
.stream()
.forEach(System.out::println); //打印
Person
@Data
@AllArgsConstructor
class Person {
private String name;
private int age;
}
Collector
//参数1:keyMapper函数
//参数2:valueMapper函数
//参数3:去重的关键,BinaryOperator函数接口
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
这个BinaryOperator函数接收两个参数,如上面代码,一个oldValue,一个newValue,字面意思,第一个旧值,第二个是新值。当stream构造Map时,会先调用Map的get方法获取该key对应节点的旧值,如果该值为null,则不会调用BinaryOperator函数,如果不为null,则会把获取的旧值与新值作为参数传给函数执行,然后把函数的返回值作为新值put到Map中。
②toCollection & TreeSet
利用TreeSet原理去重(TreeSet内部使用的是TreeMap,使用指定Comparator比较元素,如果元素相同,则新元素代替旧元素)
List<Person> personList = new ArrayList<>();
personList.add(new Person("one", 10));
personList.add(new Person("two", 20));
personList.add(new Person("three", 30));
//这两的name值重复
personList.add(new Person("four", 40));
personList.add(new Person("four", 42));
System.out.println("利用Collectors.toCollection和TreeSet去重:");
//利用TreeSet原理去重(TreeSet内部使用的是TreeMap,使用指定Comparator比较元素,如果元素相同,则新元素代替旧元素)
personList.stream()
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))))
.stream()
.forEach(System.out::println); //打印
如果想要不想要返回TreeSet类型,那也可以使用Collectors.collectingAndThen转换成ArrayList,也可以用new ArrayList(set),原理一样
List<Person> distinctList = personList.stream()
.collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))), ArrayList::new));

浙公网安备 33010602011771号