文章目录
- Stream特点
- Stream语法
- Stream创建
- Stream和paralleStream区别
- Stream对象方法使用
- 中间操作
- filter(过滤)
- distinct(去重)
- sorted(排序)
- map(映射)重点
- flatMap(映射)重点
- 结束操作
- forEach(遍历)
- reduce(归约)
- collect(搜集)
- 通过Stream对List,Map操作和互转
- Map转List
- List转Map
- 案例
- List集合Map类型排序
- Map值求和
- Map 排序
- Map 遍历倒序输出
- peek 和 map 的区别
- 使用 Collectors.toMap 将 List 转 Map
- List<Map<String,Object>> stream 常用操作
Stream特点
- 代码简洁: 函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。
- 多核友好:Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下parallel()方法。
- 无存储:stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
- 为函数式编程而生:对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行:stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性:stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
使用Stream需要三个操作步骤:创建Stream、中间操作以及结束操作
。
注意:因为stream操作是延迟的,这就意味着它们会等到需要结果的时候才执行,只有有终止操作,流的相关操作才会执行。
Stream语法
Stream创建
stream并不是某种数据结构,它只是数据源的一种视图。这里的数据源可以是一个数组,Java容器或I/O channel等。正因如此要得到一个stream通常不会手动创建,而是调用对应的工具方法。
1、 调用Collection
系列集合的串行流:stream()
方法或者并行流: parallelStream()
方法
List<String> list = Arrays.asList("tom", "James");
// 获取并行的Stream对象
Stream<String> stream = list.parallelStream();
// 获取串行的Stream对象
Stream<String> stream = list.stream();
2、调用Arrays.stream(T[] array)
方法获取数组流
String[] array = {"aa", "bb", "cc"};
Stream<String> stream = Arrays.stream(array);
3、调用Stream.of()
方法
Stream<String> stream = Stream.of("tom", "james");
4、其他方式
Stream.iterate()
Stream.generate()
Stream和paralleStream区别
Stream:串行单管流,按顺序打印结果。
paralleStream:并行多管流,并行打印结果,每次结果都不一致。
parallelStream执行效率要比传统的for循环和stream要快的多。
时候要用stream或者parallelStream呢?
- 是否需要并行?
- 任务之间是否是独立的?是否会引起任何竞态条件?
- 结果是否取决于任务的调用顺序?
Stream对象方法使用
Stream对象提供的方法分成两类:
- 中间操作:将原始的Stream转换成另外一个Stream,并且总是会惰式执行,如filter返回的是过滤后的Stream。
- 终端操作:产生的是一个结果或者其它的复合操作,如count或者forEach操作。
中间操作
方法 | 说明 |
sequential | 返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象 |
parallel | 返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象 |
unordered | 返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象 |
onClose | 返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象 |
close | 关闭Stream对象 |
filter | 元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素 |
map | 元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果 |
mapToInt | 元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象 |
flatMap | 元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回; |
distinct | 去重:返回一个去重后的Stream对象 |
sorted | 排序:返回排序后的Stream对象 |
peek | 使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象 |
limit | 获取有限个元素组成新的Stream对象返回 |
skip | 抛弃前指定个元素后使用剩下的元素组成新的Stream返回 |
takeWhile | 如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。 |
dropWhile | 与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。 |
区分中间操作和结束操作最简单的方法,就是看方法的返回值,返回值为stream的大都是中间操作,否则是结束操作。 |
filter(过滤)
用于对Stream中的元素进行过滤,作用是返回一个只包含满足predicate
条件元素的Stream。
函数原型:
Stream<T> filter(Predicate<? super T> predicate);
使用示例:
Stream<String> stream = Stream.of("I", "love", "you", "too");
// 保留长度等于3的字符串
stream = stream.filter(str -> str.length()==3);
由于filter()是个中间操作,如果只调用filter()不会有实际计算,因此也不会输出任何信息。
从List中过滤出一个元素:
User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
distinct(去重)
作用是返回一个去除重复元素之后的Stream。
函数原型:
Stream<T> distinct();
使用示例:
Stream<String> stream = Stream.of("I", "you", "too", "too");
// 去掉一个too字符串
stream = stream.distinct();
sorted(排序)
返回的是排序好后的Stream。
如果不指定一个自定义的Comparator则会使用默认排序。
一、实现Comparable接口,重写CompareTo()方法
// 默认排序
List<String> list = Arrays.asList("June", "Kmde", "Kang", "Zhan", "Gui");
Stream<String> stream = list.stream();
stream.sorted().forEach(System.out::println);
// 自定义排序
List<User> list = Arrays.asList(new User(23), new User(56));
// 按自然序排序
list.stream().sorted(Comparator.comparing(User::getAge));
// 按自然序逆序
list.stream().sorted(Comparator.comparing(User::getAge).reversed());
// 自定义复杂排序
List<User> list = Arrays.asList(new User(), new User());
list.stream().sorted((x, y)->{
if(null == x.getAge()) {
return 1;
}
if(null == y.getAge()) {
return -1;
}
return y.getAge().compareTo(x.getAge());
});
map(映射)重点
元素一对一转换。
它接收一个Funcation
参数,用其对Stream
中的所有元素进行处理,返回的Stream
对象中的元素为Function
对原元素处理后的结果 。
直观的说,就是对每个元素按照某种操作进行转换,转换前后Stream
中元素的个数不会改变。
函数原型:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
使用示例:
Stream<String> stream = Stream.of("I", "love", "you", "too");
// 将所有字符串转化成大写
stream = stream.map(str -> str.toUpperCase());
stream.forEach(System.out::println);
结果:
I
LOVE
YOU
TOO
从结果看出,流中的每个元素都应用了map()里的参数中的Function函数,并返回经过Function处理的元素。
flatMap(映射)重点
元素一对多转换。
它对原Stream
中的所有元素使用传入的Function
进行处理,每个元素经过处理后生成一个多个元素的Stream
对象,然后将返回的所有Stream
对象中的所有元素组合成一个统一的Stream
并返回。转换前后元素的个数和类型都可能会改变。
函数原型:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
使用示例:
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2), Arrays.asList(3, 4, 5));
// 原来的stream中有两个元素,分别是两个List<Integer>,执行flatMap()之后,将每个List都“摊平”成了一个个的数字,所以会新产生一个由5个数字组成的Stream
stream = stream.flatMap(list -> list.stream());
Stream<String> stream = Stream.of("tom", "James");
// 将每一个String元素拆分成单个字母的Stream
stream = stream.flatMap(n -> Stream.of(n.split("")));
map、flatMap区别:
- map一对一转化,将一个流中的所有元素按照逻辑执行后并返回。
- flatMap一对一或者一对多,将一个流中的所有元素都转换成另一个流,然后把所有的流连接起来成为一个流。
结束操作
方法 | 说明 |
iterator | 返回Stream中所有对象的迭代器; |
spliterator | 返回对所有对象进行的spliterator对象 |
forEach | 对所有元素进行迭代处理,无返回值 |
forEachOrdered | 按Stream的Encounter所决定的序列进行迭代处理,无返回值 |
toArray | 返回所有元素的数组 |
reduce | 使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。 |
collect | 根据传入参数做相关汇聚计算 |
min | 返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty |
max | 与Min相反 |
count | 所有元素个数 |
anyMatch | 只要其中有一个元素满足传入的Predicate时返回True,否则返回False |
allMatch | 所有元素均满足传入的Predicate时返回True,否则False |
noneMatch | 所有元素均不满足传入的Predicate时返回True,否则False |
findFirst | 返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。 |
findAny | 返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。 |
isParallel | 判断是否当前Stream对象是并行的 |
forEach(遍历)
作用是对容器中的每个元素执行action指定的动作,也就是对元素进行遍历。
函数原型:
void forEach(Consumer<? super E> action)
使用示例:
// 使用Stream.forEach()迭代
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.forEach(System.out::println);
运行结果:
由于forEach()是结束方法,上述代码会立即执行,输出所有字符串。
reduce(归约)
规约操作(reduction operation)又被称作折叠操作(fold),是通过某个连接动作将所有元素汇总成一个汇总结果的过程。元素求和、求最大值或最小值、求出元素总个数、将所有元素转换成一个列表或集合,都属于规约操作。
Stream类库有两个通用的规约操作reduce()
和collect()
,也有一些为简化书写而设计的专用规约操作,比如sum()、max()、min()、count()
等。
最大或最小值这类规约操作很好理解(至少方法语义上是这样),我们着重介绍reduce()
和collect()
,这是比较有魔法的地方。
使用示例:
@Test
public void test() {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
// x:上一次执行的返回值,默认0;y:当前遍历的值
Integer sum = list.stream().reduce(0, (x, y) -> {
System.out.println("上次的返回值:" + x);
System.out.println("这一次遍历的值:" + y);
return x + y;
});
System.out.println("总和:" + sum);
System.out.println("---黄金分割线---");
List<String> list2 = Arrays.asList("June", "Kmde", "Kang", "Zhan", "Gui");
Optional<String> result = list2.stream().reduce((x, y) -> x + "_" + y);
System.out.println(result.get());
}
运行结果:
collect(搜集)
接收的参数是将流中的元素累积到汇总结果的各种方式(称为收集器)。
可以转化成List、Set、Map …
// 把流中所有元素收集到List中
List<String> names = stream1.map(Student::getName).collect(Collectors.toList());
// 把流中所有元素收集到Set中,删除重复项
Set<String> names = stream1.map(Student::getName).collect(Collectors.toSet());
// 把流中所有元素收集到给定的供应源创建的集合中
Set<String> names = stream1.map(Student::getName).collect(Collectors.toCollection(HashSet::new));
// 把流中所有元素收集到Map中
Map<Integer, String> names = stream1.distinct().collect(Collectors.toMap(Student::getId, Student::getName));
//将集合中所有的名字连接在一起,并逗号分割
String allNameStr1 = stuList.stream().map(Stu::getName).collect(Collectors.joining(","));
通过Stream对List,Map操作和互转
Map转List
List<User> list1 = map.entrySet().stream()
.sorted(Comparator.comparing(e -> e.getKey()))
.map(e -> new User(e.getKey()))
.collect(Collectors.toList());
List<User> list2 = map.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getValue))
.map(e -> new User(e.getKey()))
.collect(Collectors.toList());
List<User> list3 = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> new User(e.getKey()))
.collect(Collectors.toList());
List<User> list = map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.map(e -> new User(e.getKey()))
.collect(Collectors.toList());
以上方式不同之处在于排序的处理。
List转Map
List<User> list = Arrays.asList(new User("10", "James"), new User("20", "James"));
Map<String, String> collect = list.stream().collect(Collectors.toMap(User::getAge, User::getName));
collect.forEach((key, value)->{
System.out.println(key);
System.out.println(value);
});
案例
List集合Map类型排序
// Map排序
List<Map<String, String>> lists = new ArrayList<>();
Map<String, String > map1 = new HashMap<>();
map1.put("age", "15");
map1.put("name", "James");
Map<String, String > map2 = new HashMap<>();
map2.put("age", "20");
map2.put("name", "Tom");
lists.add(map1);
lists.add(map2);
lists.stream().sorted(Comparator.comparing(Map::entrySet, (x, y)->{
List<Map.Entry<String, String>> xList = x.stream().filter(xFilter -> xFilter.getKey().equalsIgnoreCase("age")).collect(Collectors.toList());
List<Map.Entry<String, String>> yList = y.stream().filter(yFilter -> yFilter.getKey().equalsIgnoreCase("age")).collect(Collectors.toList());
return xList.get(0).getValue().compareTo(yList.get(0).getValue());
}));
Optional<Integer> todayTargetCount = ProvinceCodeMap.qrcodepurcharseWebsiteProvinceSource2020TargetMap.entrySet().stream().map(map -> map.getValue())
Map值求和
Map<Integer, Integer> map = new HashMap<>();
map.put(101, 448800);
map.put(1775, 241200);
map.put(2732, 325200);
Optional<Integer> total = map.entrySet().stream()
.map(mapTmp -> mapTmp.getValue()).collect(Collectors.toList())
.stream().reduce((x, y) -> x + y);
System.out.println(total.get());
Map 排序
实际需求,针对 list 根据数据时间分组,分组后的 map 无序,但需要按照时间正序排列。
// 先根据时间分组
Map<Object, List<HashMap<String, Object>>> listMap = cardRecord.stream().collect(Collectors.groupingBy(item -> item.getOrDefault("data", "")));
// 分组后根据key正序排列,()LinkedHashMap有序)
listMap = listMap.entrySet().stream().sorted(new Comparator<Map.Entry<Object, List<HashMap<String, Object>>>>() {
@Override
public int compare(Map.Entry<Object, List<HashMap<String, Object>>> o1, Map.Entry<Object, List<HashMap<String, Object>>> o2) {
try {
Date d1 = DateUtil.parse(o1.getKey().toString(), "yyyy-MM-dd");
Date d2 = DateUtil.parse(o2.getKey().toString(), "yyyy-MM-dd");
return d2.compareTo(d1);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}).collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
));
Map 遍历倒序输出
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map .put("a", "1");
map .put("b", "2");
map .put("c", "3");
ListIterator<Map.Entry<String, String>> li = new ArrayList<Map.Entry<String, String>>(map.entrySet()).listIterator(map.size());
while(li.hasPrevious()) {
Map.Entry<String, String> entry = li.previous();
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
peek 和 map 的区别
peek用来修改数据
map用来转换数据类型
使用 Collectors.toMap 将 List 转 Map
userList.stream().collect(Collectors.toMap(User::getId, User::getName));
当然,如果希望得到 Map的value为对象本身时,可以这样写:
userList.stream().collect(Collectors.toMap(User::getId, t -> t));
或者:
userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
Collectors.toMap有三个重载方法:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
参数解释:
- keyMapper:Key 的映射函数
- valueMapper:Value的映射函数
- mergeFunction:当 Key 冲突时,调用的合并方法
- mapSupplier:Map 构造器,在需要返回特定的 Map 时使用
【强制】在使用java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要使用含有参数类型为BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同key值时会抛出 IllegalStateException 异常。
说明:参数 mergeFunction 的作用是当出现 key 重复时,自定义对value 的处理策略。
List<User> userList = new ArrayList<>();
userList.add(new User().setId("A").setName("张三"));
userList.add(new User().setId("A").setName("李四"));//相同的key
userList.add(new User().setId("C").setName("王五"));
userList.stream().collect(Collectors.toMap(User::getId, User::getName));
// 异常:
java.lang.IllegalStateException: Duplicate key 张三
at java.util.stream.Collectors.lambda$throwingMerger$114(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1245)
at java.util.stream.Collectors.lambda$toMap$172(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
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 Test.toMap(Test.java:17)
...
解决方案:
userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2));
// 输出结果:
A-> 张三李四
C-> 王五
转化为ConcurrentMap:
userList.stream().collect(
Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, ConcurrentHashMap::new)
);
List<Map<String,Object>> stream 常用操作
1、测试集合
List<Map<String,Object>> listMap = new ArrayList<>();
Map<String,Object> temp = new HashMap<>();
temp.put("name","张三");
temp.put("age",20);
temp.put("height",new BigDecimal("170.32"));
listMap.add(temp);
temp = new HashMap<>();
temp.put("name","李四");
temp.put("age",20);
temp.put("height",new BigDecimal("185.32"));
listMap.add(temp);
temp = new HashMap<>();
temp.put("name","王五");
temp.put("age",21);
temp.put("height",new BigDecimal("192.32"));
listMap.add(temp);
BigDecimal 求和:
BigDecimal sum = listMap.stream().map(e -> (BigDecimal)e.get("height")).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal 最大值、最小值:
Map<String, Object> mapMax = listMap.stream().max((v1, v2) -> ((BigDecimal) v1.get("height")).compareTo((BigDecimal) v2.get("height"))).orElse(null);
Map<String, Object> mapMin = listMap.stream().min((v1, v2) -> ((BigDecimal) v1.get("height")).compareTo((BigDecimal) v2.get("height"))).orElse(null);
List<Map<String,Object>> 分组、排序、筛选
Map<String, List<Map<String, Object>>> mapGroup = listMap.stream().collect(Collectors.groupingBy(e -> e.get("name").toString()));
List<Map<String,Object>> mapSort = listMap.stream().sorted(Comparator.comparing(map->(BigDecimal) map.get("height"))).collect(Collectors.toList()); // 正序
Collections.reverse(mapSort); // 倒序
// 筛选 全部
List<Map<String,Object>> mapFindList = listMap.stream().filter(e -> "20".equals(e.get("age").toString())).collect(Collectors.toList());
// 查找一个
Map<String,Object> mapFindOne = listMap.stream().filter(p -> "20".equals(p.get("age").toString())).findAny().orElse(null);
其他操作:
// 求和
int ageAll = listMap.stream().mapToInt(e -> (int) e.get("age")).sum();
// 最大值
int ageMax = listMap.stream().mapToInt(e -> (int) e.get("age")).summaryStatistics().getMax();
// 最小值
int ageMin = listMap.stream().mapToInt(e -> (int) e.get("age")).summaryStatistics().getMin();
// 均值
double ageAvg = listMap.stream().mapToInt(e -> (int) e.get("age")).summaryStatistics().getAverage();
// 获取总数
long age = listMap.stream().mapToInt(e -> (int) e.get("age")).summaryStatistics().getCount();