声明:本文使用JDK1.8
Java8提供了丰富的lambda的API。今天我们来讲讲关于其中的stream(流的操作)。对于List集合,有了stream的流操作,感觉如虎添翼。
生成一个List
String str = "1,2,3,4,10,11,9,66,222,12";
List<Integer> list = Stream.of(str.split(","))
.map(Integer::valueOf)
.filter(x-> !Objects.equals(x,3))
.sorted(Comparator.reverseOrder())
.limit(4)
.collect(Collectors.toList());
上面的代码Stream.of 为我们生成了一个List ,但是我们需要的Integer,所以我们还需要使用 map 来转换类型。然后对于生成的列表,我们不想要其中的3,于是我们使用 filter 来过滤掉列表中的3。对于现在列表我们又想是倒序后的列表的前四条数据,于是我们在使用 sorted 来对列表来进行倒序后,再使用 limit 取前四条。顺便说一句,对于比较,推荐使用Objects.equals(a,b).
下面来简单的介绍下stream。
map
map:对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素
map方法是对列表里面的对象的转换,比如上面的map的功能是吧String转换成Integer。除了这样的转换,你也可以对对象进行你需要的操作。比如现在有一个对象ListDTO:
@Data
public class ListDTO{
public Integer id;
public String name;
public ListDTO() {
}
public ListDTO(Integer id, String name) {
this.id = id;
this.name = name;
}
}
现在这么一个List ,你现在只想得到这个列表中ListDTO对象里面的id,现在你就可以这样操作:
List<Integer> idList = beans.stream().map(ListDTO::getId).collect(Collectors.toList());
如果你想要得到name的列表,也可以进行上面类似的操作。下面对List列表的元素先转换成大写,在用逗号把元素拼接起来。
// 将字符串换成大写并用逗号拼接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
输出:
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
flatMap
类似于map,但是flatMap是对Stream之间的转换。
String[][] data = new String[][]{{"a", "b"}, {"c", "d"}, {"e", "f"}};
Stream<String[]> temp = Arrays.stream(data);
Stream<String[]> stream = temp.filter(x -> "a".equals(x.toString()));
count
count()是对列表的聚合,统计列表的数量。
Long count = list.stream().count();
filter
对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素
distinct
distinct是对stream里面的元素进行去重,有点类似于SQL里面的distinct去重
limit
对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素
stream 生成 map
上面我们介绍了stream里面的toList(),下面我们来介绍下如何使用stream直接生成map,而不是像以前一样,需要循环后再一个一个的put进map。虽然我们也可以第三方的jar生成Map,比如Google的某些jar,使用里面的Maps.uniqueIndex()也可以直接把List转换成Map。
toMap
利用Collectors里面的toMap对List列表转换Map。
// 这种toMap,当出现Key相同时,会出现 Duplicate key 问题
Map<Integer,String> beanMap = beans.stream()
.collect(Collectors.toMap(x->x.getId(), m->m.getName()));
//解决key冲突问题,使用新的value替换原来的value
Map<Integer, String> map = beans.stream()
.collect(Collectors.toMap(SimpleDTO::getId, SimpleDTO::getName, (v1, v2) -> v2));
前者的toMap使用的是 throwingMerger,在出现key相同时,抛出异常;后者的toMap使用的是mergeFunction,如示例中,如果出现key相同的元素,新的key能覆盖旧的。
groupingBy
利用Collectors里面的groupingBy,可以对列表进行分组,总和和排序。
public static void main(String[] args) {
List<String> items = Arrays.asList("Apple", "Apple", "orange",
"Apple", "orange", "banana", "papaya");
Map<String, Long> result = items
.stream()
.collect(Collectors
.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(result);
//排序
Map<String, Long> finalMap = new LinkedHashMap<>();
//sort a map and add to finalMap
result.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue()
.reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));
// 根据value 倒叙
Map<String, SimpleDTO> sampleMap = BeanData.getDataList()
.stream().collect(Collectors.toMap(SimpleDTO::getName, m->m));
Map<String, SimpleDTO> newMap = sampleMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue((o1, o2) -> {
if (o1.getAge() > o2.getAge()) {
return -1;
}
if (o1.getAge() < o2.getAge()) {
return 1;
}
return 0;
})).collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(o1, o2) -> o1,
LinkedHashMap::new));
System.out.println(newMap);
// map 根据key正序排列
Map<String, SimpleDTO> keyMap = sampleMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(o1, o2) -> o1,
LinkedHashMap::new));
System.out.println(keyMap);
System.out.println(finalMap);
//数据统计
List<SimpleDTO> beans = BeanData.getBeanDataList();
Map<Integer, Long> countMap = beans
.stream().collect(
Collectors.groupingBy(SimpleDTO::getId, Collectors.counting()));
System.out.println(countMap);
Map<Integer, List<SimpleDTO>> listMap = beans.stream().collect(
Collectors.groupingBy(SimpleDTO::getId));
System.out.println(JSON.toJSON(listMap));
//Map 的value值自行聚合
Map<Integer, Set<String>> setMap = beans.stream().collect(
Collectors.groupingBy(SimpleDTO::getId, Collectors.mapping(SimpleDTO::getName, Collectors.toSet()))
);
System.out.println(JSON.toJSON(setMap));
//Map的value重新聚合一个新的对象
Map<Integer, List<UserDTO>> collectMap = beans.stream()
.collect(Collectors.groupingBy(SimpleDTO::getId,
Collectors.collectingAndThen(Collectors.toList(), input -> input.stream()
.map(m -> {
UserDTO user = new UserDTO();
user.setId(m.getId());
user.setUsername(m.getName());
return user;
}).collect(Collectors.toList())
)));
System.out.println(JSON.toJSON(collectMap));
SimpleDTO dto1 = new SimpleDTO(1, "Jack", "1");
SimpleDTO dto2 = new SimpleDTO(2, "James", "2");
SimpleDTO dto3 = new SimpleDTO(3, "Hangzhou", "3");
SimpleDTO dto4 = new SimpleDTO(3, "Hangzhou", "4");
List<SimpleDTO> list = new ArrayList<>();
list.add(dto1);
list.add(dto2);
list.add(dto3);
list.add(dto4);
//自行拼接KEY
Map<String, List<SimpleDTO>> mapKey = list.stream()
.collect(Collectors.groupingBy(e -> fetchGroupKey(e)));
System.out.println(mapKey);
}
private static String fetchGroupKey(SimpleDTO dto) {
return dto.getId() + "_" + dto.getName();
}
总和
List<String> items = Arrays.asList("Apple", "Apple", "orange",
"Apple", "orange", "banana", "papaya");
Map<String,Long> result = items.stream()
.collect(Collectors
.groupingBy(Function.identity(),Collectors.counting()));
输出结果:
{papaya=1, banana=1, orange=2, Apple=3}
排序
Map<String, Long> finalMap = new LinkedHashMap<>();
//Sort a map and add to finalMap
result.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue()
.reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));
System.out.println(finalMap);
分组
现在有数据
public static List<ListDTO> getBeanDataList(){
List<ListDTO> listDTOS = Arrays.asList(
new ListDTO(1,"孙博"), new ListDTO(1,"二代"),new ListDTO(1,"孙博"),
new ListDTO(2,"戴硕"),new ListDTO(2,"戴硕"),new ListDTO(2,"赛克"),
new ListDTO(3,"二代"),new ListDTO(3,"路痴"),new ListDTO(3,"路痴"),
new ListDTO(4,"赛克"),new ListDTO(4,"二代"),new ListDTO(4,"路痴")
);
下面更加其中的ID作为Map的key,对上面的List进行Map.
Map<Integer,List<ListDTO>> listMap = beans.stream().collect(
Collectors.groupingBy(ListDTO::getId));
把对象中的name取出并塞到一个Set中
Map<Integer,Set<String>> setMap = beans.stream().collect(
Collectors.groupingBy(ListDTO::getId,Collectors.mapping(ListDTO::getName,Collectors.toSet()))
);
其他
现在需要对上面的集合,根据ID,对数据进行统计。
Map<Integer,Long> countMap = beans
.stream().collect(
Collectors.groupingBy(ListDTO::getId,Collectors.counting())
);
Stream 和 parallelStream
Stream
是普通的对流操作,而 parallelStream
是对集合进行并发操作,看下面的对两者的简单比较:
public static void main(String[] args) {
List<SimpleDTO> list = new ArrayList<>();
for (int i = 1; i < 100000; i++) {
SimpleDTO dto = new SimpleDTO();
dto.setId(i);
dto.setName("测试员" + i + "号");
dto.setContent("Stream 测试普通和并行的效率");
list.add(dto);
}
List<SimpleDTO> testList = list;
Long stattTime = System.currentTimeMillis();
List<String> list1 = list.stream()
.filter(x -> x.getId() > 1000)
.map(SimpleDTO::getName)
.collect(Collectors.toList());
Long endTime = System.currentTimeMillis();
System.out.println("stream 耗时:" + (endTime - stattTime) + "ms");
List<String> list2 = testList.parallelStream()
.filter(x -> x.getId() > 1000)
.map(SimpleDTO::getName)
.collect(Collectors.toList());
Long endTime1 = System.currentTimeMillis();
System.out.println("parallelStream 耗时:" + (endTime1 - endTime) + "ms");
}
结果:
stream 耗时:77ms
parallelStream 耗时:19ms
从最终的耗时可以看出,对于 parallelStream
处理大量数据的时候,效率还是有很大的提升的。