24-案例实现
案例实现
根据如下步骤来实现本案例。
1.创建一个 Operation
类,并实现3个字段:一个 String
类型的 user
字段、一个 String
类型的 operation
字段和一个 Date
类型的 time
字段。同时给这些字段分别实现 getter
和 setter
方法。由于该类结构比较简单,因此在此不予展示。
2.创建一个 HashFiller
类并实现 Runnable
接口:
public class HashFiller implements Runnable {
3.声明一个 ConcurrentHashMap
类型的 userHash
私有字段。该类的 key
为 String
类型,而其 value
则为 ConcurrentLinkedDeque<Operation>
类型。同时,实现该类的一个构造方法来初始化字段:
private ConcurrentHashMap<String, ConcurrentLinkedDeque<Operation>>
userHash;
public HashFiller(ConcurrentHashMap<String, ConcurrentLinkedDeque
<Operation>> userHash) {
this.userHash = userHash;
}
4.实现 run()
方法。在方法中给 ConcurrentHashMap
填充100个随机的 Operation
对象。首先生成随机数据,然后用 addOperationToHash()
方法来插入对象到散列中:
@Override
public void run() {
Random randomGenerator=new Random();
for (int i=0; i<100; i++) {
Operation operation=new Operation();
String user = "USER" + randomGenerator.nextInt(100);
operation.setUser(user);
String action = "OP" + randomGenerator.nextInt(10);
operation.setOperation(action);
operation.setTime(new Date());
addOperationToHash(userHash, operation);
}
}
5.实现 addOperationToHash()
方法。它以 hash
和 operation
作为参数。map的key是与operation对应的user。用 computeIfAbsent()
方法可以关联key和对应的 ConcurrentLinkedDeque
对象。如果key已存在,则该方法将返回对应的value;否则,将会执行方法上的参数,该参数是一个lambda表达式,它生成与key相对应的value。在本案例中,它将生成一个新的 ConcurrentLinkedDeque
对象。最后,插入operation到队列中:
private void addOperationToHash(ConcurrentHashMap<String,
ConcurrentLinkedDeque<Operation>>
userHash, Operation operation) {
ConcurrentLinkedDeque<Operation> opList = userHash
.computeIfAbsent(operation.getUser(),
user -> new ConcurrentLinkedDeque<>());
opList.add(operation);
}
6.实现 Main
类和它的 main()
方法。首先,声明 ConcurrentHashMap
对象和 HashFiller
对象:
ConcurrentHashMap<String, ConcurrentLinkedDeque<Operation>>
userHash = new ConcurrentHashMap<>();
HashFiller hashFiller = new HashFiller(userHash);
7.执行10个带 HashFiller
类的线程,并用 join()
方法等待它们执行完成:
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(hashFiller);
threads[i].start();
}
for (int i = 0; i < 10; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
8.现在,把关于 ConcurrentHashMap
的信息抽取出来。首先,从 size()
方法中获取元素的个数。然后,用 forEach()
方法遍历散列中的所有元素。第一个参数是并发阈值,而且它还是元素的最小值,它要求代码操作用并发的方式来执行。因为已经给定了值为10的并发阈值以及有100个元素的散列,所以该操作将会以并发的方式来执行。lambda表达式接收了两个参数:key和value。打印key和作为value的 ConcurrentLinkedDeque
的长度:
System.out.printf("Size: %d\n", userHash.size());
userHash.forEach(10, (user, list) -> {
System.out.printf("%s: %s: %d\n", Thread.currentThread()
.getName(), user, list.size());
});
9.调用 forEachEntry()
方法。虽然它跟前一个方法很相似,但是其lambda表达式接收的参数是一个 Entry
对象,取代了之前接收的两个参数。因此,可以用entry对象来获取其key和value:
userHash.forEachEntry(10, entry -> {
System.out.printf("%s: %s: %d\n", Thread.currentThread()
.getName(), entry.getKey(),
entry.getValue().size());
});
10.调用 search()
方法来查找第一个满足指定查询条件的元素。在本案例中,查询条件是搜索一个 code
末尾字符为1的operation。与 forEach()
方法类似,该方法可以指定一个并发阈值:
Operation op = userHash.search(10, (user, list) -> {
for (Operation operation : list) {
if (operation.getOperation().endsWith("1")) {
return operation;
}
}
return null;
});
System.out.printf("The operation we have found is: %s, %s, %s,\n",
op.getUser(), op.getOperation(), op.getTime());
11.再次使用 search()
方法。但这次,要查询多于10个operation的user:
ConcurrentLinkedDeque<Operation> operations = userHash.search(10,
(user, list) -> {
if (list.size() > 10) {
return list;
}
return null;
});
System.out.printf("The user we have found is: %s: %d operations\n",
operations.getFirst().getUser(),
operations.size());
12.用 reduce()
方法来计算保存在散列里面的operation总值:
int totalSize = userHash.reduce(10, (user, list) -> {
return list.size();
}, (n1, n2) -> {
return n1 + n2;
});
System.out.printf("The total size is: %d\n", totalSize);
}
}