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());
});