JDK Stream流使用介绍
Posted tuacy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK Stream流使用介绍相关的知识,希望对你有一定的参考价值。
Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作。通常我们需要多行代码才能完成的操作,借助于Stream流式处理可以很简单的实现。
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。同时Stream提供串行和并行两种模式进行汇聚操作。比如你的Stream里面有很多数据,Stream可以开多个线程每个线程处理一部分。最后把结果汇总起来。
在开始之前我们先用一个图来整体的概况下Stream。如下所示:
一 Stream流创建
想使用Stream流,首先咱得先创建一个Stream流对象。创建Steam需要数据源.这些数据源可以是集合、可以是数组、可以使文件、甚至是你可以去自定义等等。
1.1 集合作为Stream数据源
集合Collection作为Stream的数据源,应该也是我们用的最多的一种数据源了。Collection里面也提供了一些方法帮助我们把集合Collection转换成Stream。
1.1.1 stream()方法
调用Collection.stream()函数创建一个Stream对象。相当于把集合Collection里面的数据都导入到了Stream里面去了。
@Test
public void collectionStream()
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
// 使用List创建一个流对象
Stream<Integer> stream = list.stream();
// TODO: 对流对象做处理
1.1.2 parallelStream()方法
调用Collection.parallelStream()创建Stream对象。
@Test
public void collectionParallelStream()
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
// 使用List创建一个流对象
Stream<Integer> stream = list.parallelStream();
// TODO: 对流对象做处理
parallelStream()与stream()区别是parallelStream()使用多线程并发处理最后子啊汇总结果,而stream()是单线程。所以相对来说parallelStream()效率要稍微高点。
1.2 数组作为Stream数据源
数组也可以作为Stream的数据源。我们可以通过Arrays.stream()方法把一个数组转化成流对象。Arrays.stream()方法很丰富,有很多个。大家可以根据实际情况使用。
@Test
public void arrayStream()
int[] intArray = new int[10];
for (int index = 0; index < intArray.length; index++)
intArray[index] = index;
// 使用数组创建一个流对象
IntStream stream = Arrays.stream(intArray);
// TODO: 对流对象做处理
1.3 BufferedReader作为Stream数据源
我们也可以把BufferedReader里面lines方法把BufferedReader里面每一行的数据作为数据源生成一个Stream对象。
@Test
public void bufferedReaderStream()
File file = new File("/home/tuacy/github/google-guava-study/src/main/resources/application.yml");
try
// 把文件里面的内容一行一行的读出来
BufferedReader in = new BufferedReader(new FileReader(file));
// 生成一个Stream对象
Stream<String> stream = in.lines();
// TODO: 对流对象做处理
catch (IOException e)
e.printStackTrace();
1.4 File作为Stream数据源
Files里面多个生成Stream对象的方法,都是对Path(文件)的操作。有的是指定Path目录下所有的子文件(所有的子文件相当于是一个列表了)作为Stream数据源,有的把指定Path文件里面的每一行数据作为Stream的数据源。
1.4.1 Files.list()
列出指定Path下面的所有文件。把这些文件作为Stream数据源。
@Test
public void fileListStream()
Path path = Paths.get("D:\\\\job\\\\git\\\\google-guava-study\\\\src\\\\main\\\\resources");
try
// 找到指定path下的所有的文件
Stream<Path> stream = Files.list(path);
// TODO: 对流对象做处理
catch (IOException e)
e.printStackTrace();
1.4.2 Files.walk()
Files.walk()方法用于遍历子文件(包括文件夹)。参数maxDepth用于指定遍历的深度。把子文件(子文件夹)作为Stream数据源。
@Test
public void fileWalkStream()
Path path = Paths.get("D:\\\\job\\\\git\\\\google-guava-study\\\\src\\\\main\\\\resources");
try
// 第二个参数用于指定遍历几层
Stream<Path> stream = Files.walk(path, 2);
// TODO: 对流对象做处理
catch (IOException e)
e.printStackTrace();
1.4.3 Files.find()
Files.find方法用于遍历查找(过滤)子文件。参数里面会指定查询(过滤)条件。把过滤出来的子文件作为Stream的数据源。
@Test
public void fileFindStream()
Path path = Paths.get("D:\\\\job\\\\git\\\\google-guava-study\\\\src\\\\main\\\\resources");
try
// 找到指定path下的所有不是目录的文件
Stream<Path> stream = Files.find(path, 2, (path1, basicFileAttributes) ->
// 过滤掉目录文件
return !basicFileAttributes.isDirectory();
);
// TODO: 对流对象做处理
catch (IOException e)
e.printStackTrace();
1.4.4 Files.lines()
Files.lines方法是把指定Path文件里面的每一行内容作为Stream的数据源。
@Test
public void fileLineStream()
Path path = Paths.get("D:\\\\job\\\\git\\\\google-guava-study\\\\src\\\\main\\\\resources\\\\application.yml");
try
// 生成一个Stream对象
Stream<String> stream = Files.lines(path);
// TODO: 对流对象做处理
catch (IOException e)
e.printStackTrace();
1.5 自己构建Stream
我们也可以自己去创建Stream自己提供数据源。Stream类里面提供of()、iterate()、generate()、builder()等一些方法来创建Stream,Stream的数据源我们自己提供。
1.5.1 Stream.of()
Stream.of()函数参数就是数据源。
Stream<Integer> ofSteam = Stream.of(1,2,3,4,5,6);
1.5.2 Stream.iterate()
Stream.iterate()可以用来生成无限流,函数需要两个参数:第一个参数是初始值、第二个参数用于确定怎么根据前一个元素的值生成下一个元素。
// Stream.iterate() 流式迭代器 Stream.iterate()函数的第二个参数告诉你怎么去生成下一个元素
Stream<BigInteger> integers = Stream.iterate(
BigInteger.ONE,
new UnaryOperator<BigInteger>()
@Override
public BigInteger apply(BigInteger bigInteger)
return bigInteger.add(BigInteger.ONE);
);
// 简单输出
integers.limit(10).forEach(new Consumer<BigInteger>()
@Override
public void accept(BigInteger bigInteger)
System.out.println(bigInteger.intValue());
);
1.5.3 Stream.generate()
Stream.generate()也是用于生成一个无限流。参数用于获取每个元素。
// Stream.generate() 生成无限流
Stream<Double> generateA = Stream.generate(new Supplier<Double>()
@Override
public Double get()
return java.lang.Math.random() * 100;
);
// 简单输出前10个值
generateA.limit(10).forEach(new Consumer<Double>()
@Override
public void accept(Double bigInteger)
System.out.println(bigInteger.intValue());
);
1.5.4 Stream.build()
Stream.build()通过建造者模式生成一个Stream建造器。然后把需要加入Stream里面的数据源一个一个通过建造器添加进去。
// Stream.builder()构造一个Stream对象
Stream.Builder<Integer> build = Stream.<Integer>builder().add(1)
.add(2)
.add(3);
build.accept(4);
build.accept(5);
build.build().forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
// TODO: 对流对象做处理
1.6 其他Stream创建方式
Stream其他创建方式我们就不一一举例了。有如下方式。
- Random.ints()。
- BitSet.stream()。
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()。
- …
二 Stream流操作(操作符)
Stream流操作就是对Stream流的各种处理。Stream里面已经给提供了很多中间操作(我们一般称之为操作符)。
Stream提供的流操作符。
Stream流操作符 | 解释 |
---|---|
filter | 过滤 |
map | 对流里面每个元素做转换 |
mapToInt | 把流里面的每个元素转换成int |
mapToLong | 流里面每个元素转换成long |
mapToDouble | 流里面每个元素转换成double |
flatMap | 流里面每个元素转换成Steam对象,最后平铺成一个Stream对象 |
flatMapToInt | 流里面每个元素转换成IntStream对象,最后平铺成一个IntStream对象 |
flatMapToLong | 流里面每个元素转换成LongStream对象,最后平铺成一个LongStream对象 |
flatMapToDouble | 流里面每个元素转换成DoubleStream对象,最后平铺成一个DoubleStream对象 |
distinct | 去重 |
sorted | 排序 |
peek | 查看流里面的每个元素 |
limit | 返回前n个数 |
skip | 跳过前n个元素 |
Stream提供了这么多的操作符,而且这些操作符是可以组合起来使用。关于每个操作符的使用我们用一个简单的实例代码来说明。
2.1 filter
filter用于对流里面的数据做过滤操作。
// 过滤
@Test
public void filter()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 过滤出偶数
Stream<Integer> filterStream = stream.filter(new Predicate<Integer>()
@Override
public boolean test(Integer integer)
return integer % 2 == 0;
);
// 简单输出
filterStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
2.2 map
map用于对流里面的元素做转换。
// 转换
@Test
public void map()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 整数转换为String
Stream<String> mapStream = stream.map(new Function<Integer, String>()
@Override
public String apply(Integer integer)
return String.valueOf(integer);
);
// 简单输出
mapStream.forEach(new Consumer<String>()
@Override
public void accept(String integer)
System.out.println(integer);
);
2.3 mapToInt、mapToLong、mapToDouble
mapToInt、mapToLong、mapToDouble用于将流里面的元素转换成对应的类型。
// 转换
@Test
public void mapToInt()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 整数转换为String
IntStream mapStream = stream.mapToInt(new ToIntFunction<Integer>()
@Override
public int applyAsInt(Integer value)
return value == null ? 0 : value;
);
// 简单输出总和
System.out.println(mapStream.sum());
2.4 flatMap、flatMapToInt、flatMapToLong、flatMapToDouble
flatMap、flatMapToInt、flatMapToLong、flatMapToDouble也是对每个元素的转换,不过他们和map的不同点在于,他们是吧每个元素转换成一个Stream流,最终在平铺成一个Stream流。
// 转换
@Test
public void flatMap()
Stream<String> stream = Stream.of("java:1", "android:2", "ios:3");
// 整数转换为String
Stream<String> rerStream = stream.flatMap(
new Function<String, Stream<String>>()
@Override
public Stream<String> apply(String s)
// 分割
Iterable<String> iterableList = Splitter.on(':').trimResults() // 移除前面和后面的空白
.omitEmptyStrings()
.split(s);
return Lists.newArrayList(iterableList).parallelStream();
);
// 简单输出
rerStream.forEach(new Consumer<String>()
@Override
public void accept(String integer)
System.out.println(integer);
);
// 转换
@Test
public void reduce()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 所有的元素相加,在加上20
Integer reduceValue = stream.reduce(20, new BinaryOperator<Integer>()
@Override
public Integer apply(Integer integer, Integer integer2)
System.out.println(integer);
System.out.println(integer2);
return integer + integer2;
);
System.out.println(reduceValue);
2.5 distinct
distinct操作符用于去重。
// 去重
@Test
public void distinct()
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
Stream<Integer> rerStream = stream.distinct();
// 简单输出
rerStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
2.6 sorted
sorted操作符用于对流里面的元素排序。
// 排序
@Test
public void sorted()
Stream<Integer> stream = Stream.of(1,2,3,2,5,4,8,6);
Stream<Integer> rerStream = stream.sorted(new Comparator<Integer>()
@Override
public int compare(Integer o1, Integer o2)
if (o1.equals(o2))
return 0;
else
return o1 > o2 ? 1 : -1;
);
// 简单输出
rerStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
2.7 peek
peek操作符用于查看流里面的每个元素。在多个操作符同时使用的时候的中间加入peek操作符可以参考每个操作符之后的结果。
// 查看
@Test
public void peek()
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// 查看
Stream<Integer> reStream = stream.peek(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
// 简单输出
reStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
2.8 limit
limit操作符用于取流前面多少个元素。
// limit
@Test
public void limit()
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// limit
Stream<Integer> reStream = stream.limit(3);
// 简单输出
reStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
2.9 skip
skip用于跳过里面的多少个元素。
// skip
@Test
public void skip()
Stream<Integer> stream = Stream.of(1,2,3,1,2,3,1,2,3);
// skip
Stream<Integer> reStream = stream.skip(3);
// 简单输出
reStream.forEach(new Consumer<Integer>()
@Override
public void accept(Integer integer)
System.out.println(integer);
);
三. Stream流终端操作
Stream流终端操作是流式处理的最后一步,之前已经对Stream做了一系列的处理之后。该拿出结果了。我们可以在终端操作中实现对流的遍历、查找、归约、收集等等一系列的操作。
Stream流终端操作提供的函数有
终端操作符 | 解释 |
---|---|
forEach | 遍历 |
forEachOrdered | 如果流里面的元素是有顺序的则按顺序遍历 |
toArray | 转换成数组 |
reduce | 归约 - 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值 |
collect | 收集 - 对处理结果的封装 |
min | 最小值 |
max | 最大值 |
count | 元素的个数 |
anyMatch | 任何一个匹配到了就返回true |
allMatch | 所有都匹配上了就返回true |
noneMatch | 没有一个匹配上就返回true |
findFirst | 返回满足条件的第一个元素 |
findAny | 返回某个元素 |
关于Stream终端操作部分,我们就着重讲下collect()函数的使用。因为其他的终端操作符都很好理解。collect()稍稍复杂一点。
3.1 collect()
collect()的使用主要在于对参数的理解,所有我们这里要专门讲下collect()函数的参数Collector这个类,以及怎么去构建Collector对象。只有在了解了这些之后,咱们才可以熟练的把他们用在各种场景中。
3.1.1 Collector
Collector类目前没别的用处,就是专门用来作为Stream的collect()方法的参数的。把Stream里面的数据转换成我们最终想要的结果上。
Collector各个方法,以及每个泛型的介绍,都在下面详细的解释了。如果有疑问可以留言。
/**
* Collector是专门用来作为Stream的collect方法的参数的
*
* 泛型含义
* T:是流中要收集的对象的泛型
* A:是累加器的类型,累加器是在收集过程中用于累积部分结果的对象。
* R:是收集操作得到的对象(通常但不一定是集合)的类型。
*/
public interface Collector<T, A, R>
/**
* 生成结果容器,容器类型为A
* (多线程的情况下可能会调用多次,开多个线程同时去处理一个流,每个线程调用一次)
*/
Supplier<A> supplier();
/**
* A对应supplier()函数创建的结果容器
* T对应Stream流里面一个一个的元素
* 用于消费元素,也就是归纳元素,一般在这个里面把流里面的元素T(也可以转换下)放到supplier()创建的结果T里面去
*/
BiConsumer<A, T> accumulator();
/**
* 用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
* 多线程的情况下,多个线程并行执行。每个线程产生一个结果
*/
BinaryOperator<A> combiner();
/**
* 用于将之前整合完的结果A转换成为R
*
* combiner()完成之后了A, 这里还可以在转一道。生成你自己想要的结果
*/
Function<A, R> finisher();
/**
* characteristics表示当前Collector的特征值,
* 这是个不可变Set
* 它定义了收集器的行为--尤其是关于流是否可以多线程并行执行,以及可以使用哪些优化的提示
*/
Set<Characteristics> characteristics();
/**
* 它定义了收集器的行为--尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示
*/
enum Characteristics
/**
* accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。如果收集器没有标为UNORDERED,
* 那它仅在用于无序数据源时才可以并行归约
* 多线程并行
*/
CONCURRENT,
/**
* 归约结果不受流中项目的遍历和累积顺序的影响(无序)
*/
UNORDERED,
/**
* 无需转换结果
*/
IDENTITY_FINISH
/**
* 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果
*/
public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
BiConsumer<R, T> accumulator,
BinaryOperator<R> combiner,
Characteristics... characteristics);
/**
* 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为R
*/
public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A, R> finisher,
Characteristics... characteristics);
有了上面的介绍,接下来我们自己来new一个Collector对象,把我们Steam流里面的数据转换成List。(当然了Collectors类里面有提供这个方法,这里我们自己写一个也是为了方便大家的理解)
// 自己来组装Collector,返回一个List
@Test
public void collectNew()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> intList = stream.collect(
new Collector<Integer, List<Integer>, List<Integer>>()
// 生成结果容器,容器类型为,我们这里为List<Integer>
@Override
public Supplier<List<Integer>> supplier()
return new Supplier<List<Integer>>()
@Override
public List<Integer> get()
return new ArrayList<>();
;
// 把流里面的结果都放到结果容器里面去
@Override
public BiConsumer<List<Integer>, Integer> accumulator()
return new BiConsumer<List<Integer>, Integer>()
@Override
public void accept(List<Integer> integers, Integer integer)
integers.add(integer);
;
// 两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
@Override
public BinaryOperator<List<Integer>> combiner()
return new BinaryOperator<List<Integer>>()
@Override
public List<Integer> apply(List<Integer> left, List<Integer> right)
left.addAll(right);
return left;
;
// 可以对最终的结果做一个转换操作
@Override
public Function<List<Integer>, List<Integer>> finisher()
return new Function<List<Integer>, List<Integer>>()
@Override
public List<Integer> apply(List<Integer> integers)
return integers;
;
// 特征值
@Override
public Set<Characteristics> characteristics()
return EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH);
);
for (Integer item : intList)
System.out.println(item);
3.1.2 Collectors
Collectors是Collector的工具类,用来创建Collector对象。它内部以及给我们提供了各种各样的创建Collector对象的方法,已经能满足我们大部分的需求了,我们可以直接拿来使用,非常方便。
Collectors各个方法介绍
public final class Collectors
/**
* 将流中的元素全部放置到一个集合中返回
*
* collectionFactory参数用于创建Collection对象(比如List,LinkeList等等)
*/
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);
/**
* 将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList
*/
public static <T>
Collector<T, ?, List<T>> toList();
/**
* 将流中的元素放置到一个无序集set中去。默认为HashSet
*/
public static <T>
Collector<T, ?, Set<T>> toSet();
/**
* joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀
*/
public static Collector<CharSequence, ?, String> joining();
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix);
/**
* 这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳
*/
public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream);
/**
* 该方法是在归纳动作结束之后,对归纳的结果进行再处理
*/
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher);
/**
* 该方法用于计数
*/
public static <T> Collector<T, ?, Long> counting();
/**
* 生成一个用于获取最小/最大值的Optional结果的Collector
*/
public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator);
public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator);
/**
* 生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和
*/
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper);
public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper);
/**
* 生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型
*/
public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper);
public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper);
/**
* 规约 对流中的元素做统计归纳作用
* 和Stream类里面的reducing操作符一样
*/
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op);
public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op);
public static <T, U> Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op);
/**
* 这个方法是用于生成一个拥有分组功能的Collector
*/
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier);
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream);
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream);
/**
* 和groupingBy方法一样,只是返回的Collector是并行的
*/
public static <T, K>
Collector<T, ?, ConcurrentMap<K, List<T>>>
groupingByConcurrent(Function<? super T, ? extends K> classifier);
public static <T, K, A, D>
Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream);
public static <T, K, A, D, M extends ConcurrentMap<K, D>>
Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream);
/**
* 该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List。
*/
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate);
public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream);
/**
* 根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,
* 可以指定出现重复键时的处理方案和保存结果的map
*/
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);
/**
* 并发版本的toMap
*/
public static <T, K, U>
Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper);
public static <T, K, U>
Collector<T, ?, ConcurrentMap<K,U>>
toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
public static <T, K, U, M extends ConcurrentMap<K, U>>
Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier);
/**
* 这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics
*/
public static <T>
Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);
public static <T>
Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper);
public static <T>
Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper);
为了加深大家的理解。接下来我们对Collectors里面的每个函数都写一个简单的实例。
3.1.2.1 toCollection
将流中的元素全部放置到一个集合中返回,这里使用Collection,泛指多种集合。
@Test
public void toCollection()
List<String> list = Arrays.asList("java", "ios", "c");
LinkedList<String> retList = list.stream().collect(Collectors.toCollection(
new Supplier<LinkedList<String>>()
@Override
public LinkedList<String> get()
return new LinkedList<>();
));
3.1.2.2 toList
将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList。
@Test
public void collectList()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> intList = stream.collect(Collectors.toList());
3.1.2.3 toSet
将流中的元素放置到一个无序集set中去。默认为HashSet。
@Test
public void toSet()
List<String> list = Arrays.asList("java", "ios", "c");
Set<String> retList = list.stream().collect(Collectors.toSet());
3.1.2.4 joining
joining的目的是将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀。
@Test
public void joining()
List<String> list = Arrays.asList("java", "ios", "c");
String ret = list.stream().collect(Collectors.joining(":", "@@", "@@"));
System.out.println(ret);//@@java:ios:c@@
3.1.2.5 mapping
这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。
@Test
public void mapping()
List<String> list = Arrays.asList("java", "ios", "c");
// 先把流里面的每个元素的前后加上[],之后在用:拼接起来
String ret = list.stream().collect(Collectors.mapping(
new Function<String, String>()
@Override
public String apply(String s)
return "[" + s + "]";
,
Collectors.joining(":")));
System.out.println(ret);//[java]:[ios]:[c]
3.1.2.6 collectingAndThen
该方法是在归纳动作结束之后,对归纳的结果进行再处理。
@Test
public void collectingAndThen()
List<String> list = Arrays.asList("java", "ios", "c");
// Collectors.toList()之后在把List<String>通过Joiner转换String
String ret = list.stream().collect(Collectors.collectingAndThen(
Collectors.toList(),
new Function<List<String>, String>()
@Override
public String apply(List<String> strings)
return Joiner.on("; ")
.join(strings);
));
System.out.println(ret);//java; ios; c
3.1.2.7 counting
计算流里面的元素个数。
@Test
public void counting()
List<String> list = Arrays.asList("java", "ios", "c");
// 元素个数
Long ret = list.stream().collect(Collectors.counting());
System.out.println(ret);//3
3.1.2.8 minBy/maxBy
生成一个用于获取最小/最大值的Optional结果的Collector。
@Test
public void minBy()
List<String> list = Arrays.asList("java", "ios", "c");
// 这里简单的用字符串比较的大小
Optional<String> ret = list.stream().collect(Collectors.minBy(
new Comparator<String>()
@Override
public int compare(String o1, String o2)
return o1.compareTo(o2);
));
3.2.2.9 summingInt/summingLong/summingDouble
生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和。
@Test
public void summingInt()
List<Integer> list = Arrays.asList(1, 2, 3);
// 求和
Integer ret = list.stream().collect(Collectors.summingInt(
new ToIntFunction<Integer>()
@Override
public int applyAsInt(Integer value)
return value;
));
3.2.2.10 averagingInt/averagingLong/averagingDouble
生成一个用于求元素平均值的Collector,首选通过参数将元素转换为指定的类型。
@Test
public void averagingInt()
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Double ret = list.stream().collect(Collectors.averagingInt(
new ToIntFunction<Integer>()
@Override
public int applyAsInt(Integer value)
return value;
));
3.1.2.11 reducing
reducing方法有三个重载方法,其实是和Stream里的三个reduce方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用(规约)。
@Test
public void reducing()
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Integer ret = list.stream().collect(Collectors.reducing(
10,
new Function<Integer, Integer>()
@Override
public Integer apply(Integer integer)
return integer * integer;
,
new BinaryOperator<Integer>()
@Override
public Integer apply(Integer integer, Integer integer2)
return integer + integer2;
));
System.out.println(ret); // 10 + 1*1 + 2*2 + 3*3 = 24
3.1.2.12 groupingBy
这个方法是用于生成一个拥有分组功能的Collector,它也有三个重载方法。这里要稍微解释下参数最长的函数每个参数的含义。
- Function<? super T, ? extends K> classifier:确定怎么从流里面每个元素获取key。
- Supplier mapFactory:用于创建最终要生成的对象一般都是Map。
- Collector<? super T, A, D> downstream:每一组数据用什么来存放,一般是List,所以这个参数一般是Collectors.toList()。
@Test
public void collectGroupingBy()
List<Student> list = Arrays.asList(new Student("吴六", 26), new Student("张三", 26), new Student("李四", 27));
Map<Integer, List<Student>> ret = list.stream().collect(Collectors.groupingBy(
new Function<Student, Integer>()
@Override
public Integer apply(Student student)
return student.getAge();
,
new Supplier<Map<Integer, List<Student>>>()
@Override
public Map<Integer, List<Student>> get()
return new HashMap<>();
,
Collectors.toList()));
for (Map.Entry<Integer, List<Student>> entry : ret.entrySet())
Integer mapKey = entry.getKey();
List<Student> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
private static class Student
private String name;
private int age;
public Student(String name, int age)
this.name = name;
this.age = age;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
groupingByConcurrent和groupingBy是一样的,只不过groupingByConcurrent生成的是ConcurrentMap,而groupingBy生成的是Map。
3.1.2.13 partitioningBy
该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List。
@Test
public void partitioningBy()
List<Integer> list = Arrays.asList(1, 2, 3);
// 求平均值
Map<Boolean, List<Integer>> ret = list.stream()
.collect(Collectors.partitioningBy(
new Predicate<Integer>()
@Override
public boolean test(Integer integer)
return integer % 2 == 0;
,
Collectors.toList()));
for (Map.Entry<Boolean, List<Integer>> entry : ret.entrySet())
Boolean mapKey = entry.getKey();
List<Integer> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
3.1.2.14 toMap
toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map。
@Test
public void toMap()
List<Student> list = Arrays.asList(new Student("吴六", 26), new Student("张三", 26), new Student("李四", 27));
Map<Integer, List<Student>> ret = list.stream()
.collect(Collectors.toMap(
// key
new Function<Student, Integer>()
@Override
public Integer apply(Student student)
return student.getAge();
,
// value
new Function<Student, List<Student>>()
@Override
public List<Student> apply(Student student)
return Lists.newArrayList(student);
,
// 有系统key的value怎么处理
new BinaryOperator<List<Student>>()
@Override
public List<Student> apply(List<Student> students, List<Student> students2)
students.addAll(students2);
return students;
));
for (Map.Entry<Integer, List<Student>> entry : ret.entrySet())
Integer mapKey = entry.getKey();
List<Student> mapValue = entry.getValue();
System.out.println(mapKey + ":" + mapValue);
private static class Student
private String name;
private int age;
public Student(String name, int age)
this.name = name;
this.age = age;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
3.1.2.15 summarizingInt/summarizingLong/summarizingDouble
这三个方法适用于汇总的,返回值分别是IntSummaryStatistics,LongSummaryStatistics,DoubleSummaryStatistics。
@Test
public void summarizingInt()
List<Integer> list = Arrays.asList(1, 2, 3);
IntSummaryStatistics ret = list.stream()
.collect(Collectors.summarizingInt(
new ToIntFunction<Integer>()
@Override
public int applyAsInt(Integer value)
return value;
));
System.out.println(ret.getAverage());
关于JDK8里面Stream部分内容我们就讲这么些,真心希望能帮助上大家。大家如果有什么疑问可以留言。最后,上面所有的实例代码在 https://github.com/tuacy/google-guava-study里面都可以找到。
以上是关于JDK Stream流使用介绍的主要内容,如果未能解决你的问题,请参考以下文章
还看不懂同事的代码?超强的 Stream 流操作姿势还不学习一下