声明:本文使用JDK1.8
Java8提供了丰富的lambda的API。今天我们来讲讲关于其中的stream(流的操作)。对于List集合,有了stream的流操作,感觉如虎添翼。
生成一个List 1 2 3 4 5 6 7 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:
1 2 3 4 5 6 7 8 9 10 11 @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,现在你就可以这样操作:
1 List<Integer> idList = beans.stream().map(ListDTO::getId).collect(Collectors.toList());
如果你想要得到name的列表,也可以进行上面类似的操作。下面对List列表的元素先转换成大写,在用逗号把元素拼接起来。
1 2 3 4 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);
输出:
1 USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
flatMap 类似于map,但是flatMap是对Stream之间的转换。
1 2 3 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()是对列表的聚合,统计列表的数量。
1 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。
1 2 3 4 5 6 7 Map<Integer,String> beanMap = beans.stream() .collect(Collectors.toMap(x->x.getId(), m->m.getName())); 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,可以对列表进行分组,总和和排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 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<>(); result.entrySet().stream() .sorted(Map.Entry.<String, Long>comparingByValue() .reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue())); 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<Integer, Set<String>> setMap = beans.stream().collect( Collectors.groupingBy(SimpleDTO::getId, Collectors.mapping(SimpleDTO::getName, Collectors.toSet())) ); System.out.println(JSON.toJSON(setMap)); 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); 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(); }
总和 1 2 3 4 5 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()));
输出结果:
1 {papaya=1, banana=1, orange=2, Apple=3}
排序 1 2 3 4 5 6 Map<String, Long> finalMap = new LinkedHashMap<>(); result.entrySet().stream() .sorted(Map.Entry.<String, Long>comparingByValue() .reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue())); System.out.println(finalMap);
分组 现在有数据
1 2 3 4 5 6 7 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.
1 2 Map<Integer,List<ListDTO>> listMap = beans.stream().collect( Collectors.groupingBy(ListDTO::getId));
把对象中的name取出并塞到一个Set中
1 2 3 Map<Integer,Set<String>> setMap = beans.stream().collect( Collectors.groupingBy(ListDTO::getId,Collectors.mapping(ListDTO::getName,Collectors.toSet())) );
其他 现在需要对上面的集合,根据ID,对数据进行统计。
1 2 3 4 Map<Integer,Long> countMap = beans .stream().collect( Collectors.groupingBy(ListDTO::getId,Collectors.counting()) );
Stream 和 parallelStream Stream
是普通的对流操作,而 parallelStream
是对集合进行并发操作,看下面的对两者的简单比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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" ); }
结果:
1 2 stream 耗时:77ms parallelStream 耗时:19ms
从最终的耗时可以看出,对于 parallelStream
处理大量数据的时候,效率还是有很大的提升的。