1.对并发散列映射的批操作:
Java SE 8为并发散列映射提供了批操作,即使有其他线程在处理映射,这些操作也能安全地执行。批操作会遍历映射,处理遍历过程中找到的元素。无须冻结当前映射的快照。
有三种不同的批操作:搜索、归约、forEach。
每个操作都有四个版本:operationKeys(处理键)、operationValues(处理值)、operation(处理键和值)、operationEntries(处理Map.Entry对象)。
对于上述的各个操作,需要指定一个参数化阀值(operation threshold)。如果映射包含的元素多于这个阀值,就会并行完成批操作。例如搜索方法:
U searchKeys(long threshold, BiFunction<? super K, ? extends U> f)
U searchValues(long threshold, BiFunction<? super K, ? extends U> f)
U search(long threshold, BiFunction<? super K, ? super V, ? extends U> f)
U searchEntries(long threshold, BiFunction<MapEntry<K, V>, ? extends U> f)
假如我们需要找出第一个出现1000次的单词:
String result = map.search(threshold, (k, v) -> v > 1000 ? k : null);
forEach操作:
map.forEach(threshold, (k, v) -> System.out.println(k + "->" + v));
//另一种方式:转换器函数
map.forEach(threshold,
(k, v) -> k + "->" + v, //转换器
System.out::println);
reduce操作:
Long sum = map.reduceValues(threshold, Long::sum);
//另一种方式:转换器函数
Integer maxlength = map.reduceKeys(threshold,
String::length, //转换器
Integer::max);
//如果映射为空,或者所有条目都被过滤掉,reduce操作会返回null。如果只有一个元素,则返回其转换结果,不会应用累加器。
2.并发集视图:
并不存在一个ConcurrentHashSet类,如果需要一个大的线程安全的集,可以通过ConcurrentHashMap静态的newKeySet方法得到一个Set<K>,这实际上是ConcurrentHashMap<K, Boolean>的一个包装器。(所有映射值都为Boolean.TRUE,但是并不关心其值是什么)。
Set<String> words = ConcurrentHashMap.<String>newKeySet();
这个集是可变的,如果删除这个集的元素,这个键会从映射中删除。但是不能插入元素,因为没有相应的值可以增加。Java SE 8为ConcurrentHashMap增加了第二个keySet方法,包含一个默认值,可以在为集增加元素时使用:
Set<String> words = map.keySet(1L);
words.add("Java");
如果"Java"在words中不存在,现在他会有一个值1。
3.写数组的拷贝:
CopyOnWriteArrayList和CopyOnWriteArraySet是线程安全的的集合。其中所有的修改线程对底层数组进行复制。如果在集合上进行迭代的线程数超过修改线程数,这样的安排是很有用的。当构建一个迭代器的时候,它包含一个当前数组的引用。如果数组后来被修改了,迭代器仍然引用旧数组,但是,集合的数组已经被替换了。因而,旧的迭代器拥有一致的(可能过时的)视图,访问它无须同步开销。