Java8 Stream的介绍

声明:本文使用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
12
13
14
15
16
17
18
19
20
21
22
public class ListDTO{
public Integer id;
public String name;
public ListDTO() {
}
public ListDTO(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
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
Map<Integer,String> beanMap = beans.stream()
.collect(Collectors.toMap(x->x.getId(), m->m.getName()));

groupingBy

利用Collectors里面的groupingBy,可以对列表进行分组,总和和排序。

总和

1
2
3
4
5
6
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<>();
//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);

分组

现在有数据

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 处理大量数据的时候,效率还是有很大的提升的。

博主 wechat
钟意作者
客官,赏一杯coffee嘛~~~~
0%