Java1.8中引入了流的概念,具体实现是java.util.stream.Stream接口,流的性能是高效的,在多核CPU中,可以充分的的利用CPU资源,代码写起来也比常规的写法更简洁,在实际开发中,用得也比较多
我总结了一下Stream流的基本用法和常用方法,让我们来感受一下吧
在使用流之前,需要准备三件事情:
中间操作
操作 | 返回类型 | 说明 | 使用场景 |
---|---|---|---|
filter | Stream<T> | 返回一个由与此流匹配的元素组成的流 | 过滤元素 |
map | Stream<R> | 返回由应用给定结果组成的流 | 类型转换 |
limit | Stream<T> | 返回一个被截断指定长度的流 | 数据截取 |
sorted | Stream<T> | 返回由该流的元素组成的流,根据自然顺序排序 如果此流的元素不是Comparable,可能会引起ClassCastException异常 | 数据排序 |
distinct | Stream<T> | 返回由不同元素组成的流 | 数据去重 |
终端操作
操作 | 返回类型 | 说明 | 使用场景 |
---|---|---|---|
forEach | void | 消费流中的每个元素并对其应用Lambda | 遍历元素 |
count | long | 返回流中元素的个数 | 统计数量 |
collect | <R> R | 把流规约成一个集合,可以是List、Map、Integer | 数据归约 |
anyMatch | boolean | 流中元素任意一个满足判断条件 | 数据判断 |
allMatch | boolean | 流中元素全部满足判断条件 | 数据判断 |
在实际开发中,我们使用的更多的是DTO,所以新建一个类,再造些数据
@Data
@AllArgsConstructor
static class DTO {
private String name;
private Integer age;
@Override
public String toString() {
return "(name=" + this.name + ", age=" + this.age + ")";
}
}
List<DTO> list = Arrays.asList(new DTO("苹果", 20), new DTO("香蕉", 35), new DTO("橘子", 15), new DTO("芒果", 50));
Api:
// 返回 predicate(过滤条件) 为True的元素
Stream<T> filter(Predicate<? super T> predicate);
示例
// 过滤价格大于20的水果
list.stream().filter(o -> o.getPrice() > 20).forEach(System.out::println);
输出如下:
(name=香蕉, price=35)
(name=芒果, price=50
Api:
<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);
类型转换的Api很多,这里只用map举例,其他Api用法大同小异,当我们要把类中的price转换成String或者其他类型的时候,可以这样
// 类型转换map(转换过程,需要有return) 将price转换成Double类型,并只返回Double类型的age
List<Double> collect = list.stream().map(o -> Double.valueOf(o.getPrice())).collect(Collectors.toList());
System.out.println(collect);
输出如下
[20.0, 35.0, 15.0, 50.0]
Api:
Stream<T> limit(long maxSize);
maxSize表示返回元素的最大数量
list.stream().limit(2).forEach(System.out::println);
输出如下
(name=苹果, price=20)
(name=香蕉, price=35)
Api:
// 原生排序 如果T没有实现Comparable,会引起ClassCastException异常
Stream<T> sorted();
// 可以自定义排序规则 扩展性高
Stream<T> sorted(Comparator<? super T> comparator);
先来看看原生排序,会发生异常:
java.lang.ClassCastException: xxxxxxx cannot be cast to java.lang.Comparable
// 排序
List<DTO> collect1 = list.stream().sorted().collect(Collectors.toList());
System.out.println(collect1);
输出如下
Exception in thread "main" java.lang.ClassCastException: com.steam.T_ForeachSteam$DTO cannot be cast to java.lang.Comparable
at java.util.Comparators$NaturalOrderComparator.compare(Comparators.java:47)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:220)
at java.util.Arrays.sort(Arrays.java:1512)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:348)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.steam.T_ForeachSteam.main(T_ForeachSteam.java:35)
自定义排序
list.stream().sorted(Comparator.comparing(DTO::getPrice)).forEach(System.out::println);
输出如下:
(name=橘子, price=15)
(name=苹果, price=20)
(name=香蕉, price=35)
(name=芒果, price=50)
倒序
list.stream().sorted(Comparator.comparing(DTO::getPrice).reversed()).forEach(System.out::println);
输出如下:
(name=芒果, price=50)
(name=香蕉, price=35)
(name=苹果, price=20)
(name=橘子, price=15)
Api:
// 底层是一个HashSet的实现
Stream<T> distinct()
List<Integer> distinctList = Arrays.asList(10, 11, 10);
distinctList.stream().distinct().forEach(o -> System.out.print(o + " "));
输出如下
10 11
Api:
long count();
List<String> anyMatchList = Arrays.asList("1","2","3","4","5");
long count = anyMatchList.stream().count();
System.out.println(count);
输出如下
5
Api:
boolean allMatch(Predicate<? super T> predicate);
List<String> addList = new ArrayList<>();
List<String> anyMatchList = Arrays.asList("1","2","3","4","5");
// 返回值可以理解为是否遍历了所有元素
boolean b = anyMatchList.stream().allMatch(o -> {
addList.add(o);
// 为false时返回
return addList.size() < 2;
});
System.out.println(addList.toString() + b);
输出如下
[1, 2]false
Api:
boolean allMatch(Predicate<? super T> predicate);
List<String> addList1 = new ArrayList<>();
// 返回值可以理解为是否有一个元素满足返回条件
boolean any = anyMatchList.stream().anyMatch(o -> {
addList1.add(o);
// 为true时返回
return addList1.size() < 2;
});
System.out.println(addList1.toString() + any);
输出如下
[1]true
Api:
<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);
使用
List<DTO> list = Arrays.asList(new DTO("苹果", 20), new DTO("香蕉", 35),
new DTO("菠萝", 20), new DTO("水蜜桃", 35),
new DTO("橘子", 15), new DTO("芒果", 50));
// 根据price分组
Map<Integer, List<DTO>> collect = list.stream().collect(Collectors.groupingBy(DTO::getPrice));
System.out.println(JSON.toJSONString(collect));
// 转换成set集合
System.out.println(Stream.of("a", "b", "c","a").collect(Collectors.toSet()));
// 转换成map
Map<String, String> collect1 = Stream.of("a", "b", "c", "a").collect(Collectors.toMap(x -> x, x -> x + x,(oldVal, newVal) -> newVal));
collect1.forEach((k,v) -> System.out.println(k + ":" + v));
输出如下
{50:[{"name":"芒果","price":50}],35:[{"name":"香蕉","price":35},{"name":"水蜜桃","price":35}],20:[{"name":"苹果","price":20},{"name":"菠萝","price":20}],15:[{"name":"橘子","price":15}]}
[a, b, c]
a:aa
b:bb
c:cc
Java Stream Api繁多,需要读者手动写代码感受一下。
笔者水平有限,文中有不正之处还请各位不吝指出,Stream涉及知识面广,笔者一时也无法全面叙出,文章会不定时更新和巩固,欢迎给我留言,我会在第一时间回复