12-案例实现
案例实现
根据如下步骤来实现本案例。
1.首先,实现将在本例中使用的辅助类。先创建 Person 类来存储人的基本数据,然后创建 PersonGenerator 类来生成一个包含随机人员的列表。这两个类的源码参见6.2节。
2.重写 Person 类的 toString() 方法,返回姓名:
@Override
public String toString() {
return firstName + " " + lastName;
}
3.创建一个名为 Counter 的类。该类包含两个属性:一个名为 value 的 String 属性和一个名为 counter 的 int 属性。然后生成这两个属性的 get() 和 set() 方法。由于这个类的代码非常简单,此处不再展示。
4.现在,创建一个带有 main() 方法的 Main 类。然后用 PersonGenerator 类来创建一个包含随机 Person 的 List :
public class Main {
public static void main(String args[]) {
List<Person> persons = PersonGenerator.generatePersonList
(100);
5.要实现的第一个收集操作是生成一个 Map ,其key是人名,其值是一个跟key同名的人的列表—用 Stream 的 collect() 方法和 Collectors.groupingByConcurrent 收集器来实现。然后使用 forEach() 方法来处理Map中所有人名,并把拥有相同人名的人数打印到控制台:这里把方法引用作为参数传递给了 groupingByConcurrent() 方法。如果只是和本例一样调用一个已有的方法,那就能通过lambda表达式来使用这种方法引用机制:
Map<String, List<Person>> personsByName = persons
.parallelStream().collect(Collectors
.groupingByConcurrent(Person::getFirstName));
personsByName.keySet().forEach(key -> {
List<Person> listOfPersons = personsByName.get(key);
System.out.printf("%s: There are %d persons with that name\n",
key, listOfPersons.size());
6.要实现的第二个收集操作是把所有人的名字串联起来,这里要使用 Person 的 toString() 方法、 Stream 的 collect() 方法以及 Collectors 的 joining() 方法,通过它们把流的所有元素用指定的字符序列串联起来:
String message = persons.parallelStream().map
(p -> p.toString()).collect(Collectors.joining(","));
System.out.printf("%s\n", message);
7.下一个要实现的收集操作是把人拆分为两组。第一组人的工资高于50000元,其余人都在第二组。这个操作的结果是一个 Map 对象, key 是 Boolean 值, value 是装有 Person 的列表。要实现这个操作,需要用到 Stream 类的 collect() 方法和 Collectors 的 partitioningBy() 方法,它接收一个 Boolean 型的参数表达式,通过 true 和 false 来区分元素。最后使用 forEach() 把生成的列表的大小打印出来:
Map<Boolean, List<Person>> personsBySalary = persons
.parallelStream().collect(Collectors
.partitioningBy(p -> p.getSalary() > 50000));
personsBySalary.keySet().forEach(key -> {
List<Person> listOfPersons = personsBySalary.get(key);
System.out.printf("%s: %d \n", key, listOfPersons.size());
});
8.现在来实现一个生成 Map 的收集操作。 Map 的 key 是人名, value 是串联起来的同名人的姓。在实现过程中会用到 Stream 类的 collect() 方法和 Collectors 类的 toConcurrentMap() 方法。我们要给该方法传递一个获取 key 的lambda表达式,一个获取 value 的lambda表达式,以及一个处理 Map 中已经存在指定 key 时的lambda表达式。最后使用 forEach() 方法打印所有的 key 和 value :
ConcurrentMap<String, String> nameMap = persons
.parallelStream().collect(Collectors
.toConcurrentMap(p -> p.getFirstName(),
p -> p.getLastName(),
(s1, s2) -> s1 + ", " + s2));
nameMap.forEach((key, value) -> {
System.out.printf("%s: %s \n", key, value);
});
9.到目前为止,在所有例子里实现的 collect() 方法都是接收一个 Collector 接口的实现。不过,还有另外一种版本的 collect() 方法。下面用这个版本的 collect() 方法来实现一个收集操作,生成一个工资高于50000的人员列表。要给 collect() 方法传递一个表达式来创建列表(用 List::new 方法),传递一个lambda表达式来处理列表和元素,以及一个用来处理两个列表的表达式(用 List::addAll 方法):
List<Person> highSalaryPeople = persons
.parallelStream().collect(
ArrayList::new, (list, person) -> {
if (person.getSalary() > 50000) {
list.add(person);
}
},
ArrayList::addAll
);
System.out.printf("High Salary People: %d\n",
highSalaryPeople.size());
10.最后,实现一个生成 ConcurrentHashMap 的例子, map 将包含 人员 列表中人的名字,以及每个名字出现的次数。以人名作为 key ,以 Counter 对象作为值。 collect 方法的第一个参数用于创建一个新的 ConcurrentHashMap 对象。第二个参数是 BiConsumer 接口的一个实现,它用一个 ConcurrentHashMap 对象和一个 Person 对象作为参数。我们首先使用 map 的 computeIfPresent() 方法,在人名存在时递增 Counter 。然后使用 map 的 computeIfAbsent() 方法,在人名不存在时将其插入。 collect() 方法的第三个参数也是一个 BiConsumer 接口的实现,它接收两个 ConcurrentHashMap 对象,用 merge() 方法来处理第二个 map 中的所有元素,把不存在于第一个 map 中的元素插入第一个 map 中,或存在时增加第一个 map 的 counter :
System.out.printf("Collect, second example\n");
ConcurrentHashMap<String, Counter> peopleNames = persons
.parallelStream().collect(
ConcurrentHashMap::new, (map, person) -> {
map.computeIfPresent(person.getFirstName(), (name,
counter) -> {
counter.increment();
return counter;
});
map.computeIfAbsent(person.getFirstName(), name -> {
Counter c=new Counter();
c.setValue(name);
return c;
});
},
(map1, map2) -> {
map2.forEach (10, (key, value) -> {
map1.merge(key, value, (v1,v2) -> {
v1.setCounter(v1.getCounter()+v2.getCounter());
return v1;
});
});
});
peopleNames.forEach((name, counter) -> {
System.out.printf("%s: %d\n", name, counter.getCounter());
});