Java 8 ConcurrentHashMap 合并与 computeIfAbsent
Java 8 ConcurrentHashMap merge vs computeIfAbsent
我正在做 Cay S. Horstmann 的 "Java SE 8 for the Really Impatient" 一书中的练习。有 2 个练习要求同一算法的不同实现,一个使用 merge
,另一个 computeIfAbsent
。我已经使用 merge
实现了该程序,但无法弄清楚如何使用 computeIfAbsent
来做同样的事情。在我看来 computeIfPresent
会更合适,因为 merge
只有在密钥存在时才有效, computeIfPresent
.
也是如此
问题陈述:
Write an application in which multiple threads read all words from a
collection of files. Use a ConcurrentHashMap<String, Set<File>>
to
track in which files each word occurs. Use the merge
method to update
the map.
我的代码使用 merge
:
public static Map<String, Set<File>> reverseIndexUsingMerge(final Path path)
throws IOException {
final ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<>();
final BiConsumer<? super String, ? super Set<File>> action = (key,
value) -> map.merge(key, value, (existingValue, newValue) -> {
LOGGER.info("Received key: {}, existing value: {}, new value: {}.",
key, existingValue, newValue);
newValue.addAll(existingValue);
return newValue;
});
commonPool().invokeAll(
find(path, 1,
(p, fileAttributes) -> fileAttributes.isRegularFile())
.map(p -> new ReverseIndex(p, action))
.collect(toList()));
return unmodifiableMap(map);
}
private static class ReverseIndex implements Callable<Void> {
private final Path p;
private final BiConsumer<? super String, ? super Set<File>> action;
private static final Pattern AROUND_WHITESPACE = compile("\s");
private ReverseIndex(final Path p,
final BiConsumer<? super String, ? super Set<File>> action) {
this.p = p;
this.action = action;
}
@Override
public Void call() throws Exception {
reverseIndex().forEach(action);
return null;
}
private Map<String, Set<File>> reverseIndex() {
/* File stream needs to be closed. */
try (Stream<String> lines = lines(p, UTF_8)) {
return lines.flatMap(AROUND_WHITESPACE::splitAsStream)
.collect(
groupingBy(String::toString,
mapping(word -> p.toFile(), toSet())));
} catch (IOException e) {
LOGGER.error("Something went wrong. Get the hell outta here.",
e);
throw new UncheckedIOException(e);
}
}
}
专注于必须完成的事情,如果价值不存在。您需要做的是为缺失的条目创建一个新的 Set
值。当并发Set
。您可以利用 ConcurrentHashMap
通过映射到固定值来创建事实上的 ConcurrentHashSet
(该形式不存在),如果您让值信号存在是 Boolean.TRUE
:
ConcurrentHashMap<String, Set<File>> map=new ConcurrentHashMap<>();
final BiConsumer<? super String, ? super Set<File>> action =
(key, value) -> map.computeIfAbsent(key, x->ConcurrentHashMap.newKeySet())
.addAll(value);
我使用 computeIfAbsent
并按扩展名“.txt”过滤文件。
结果显示在控制台中。
对于IntelliJ IDEA IDE:如果结果不完整,则检查并增加"Override console cycle buffer size"(File/Settings/Editor/General/Console).
导入列表:
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
我解决这个任务:
public static void printMap(Path path){
try (Stream<Path> stream = Files.walk(path)){
ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<>();
stream.parallel()
.filter(p -> !(Files.isDirectory(p)) & p.getFileName()
.toString()
.toLowerCase()
.endsWith(".txt"))
.collect(Collectors.toList())
.forEach((p) -> {
try {
Files.lines(p, StandardCharsets.UTF_8)
.flatMap(s -> Arrays.asList(s.split("\PL+")).stream())
.filter(w -> w.length() > 0)
.map(String::toLowerCase)
.parallel()
.forEach(
key -> {
Set<File> tempSet = new HashSet<>();
tempSet.add(new File(p.toString()));
map.computeIfAbsent(key, x -> ConcurrentHashMap.newKeySet())
.addAll(tempSet);
});
} catch (IOException e){
} catch (UncheckedIOException e){}
});
map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(System.out::println);
} catch (IOException e){}
}
通话printMap()
:
public static void main(String[] args){
Path path = Paths.get(*somePathName*);
printMap(path);
}
如果需要使用merge
,那么只需替换
map.computeIfAbsent(key, x -> ConcurrentHashMap.newKeySet()).addAll(tempSet);
到
map.merge(key, tempSet, (oldSet, newSet) -> {oldSet.addAll(newSet); return oldSet;});
我正在做 Cay S. Horstmann 的 "Java SE 8 for the Really Impatient" 一书中的练习。有 2 个练习要求同一算法的不同实现,一个使用 merge
,另一个 computeIfAbsent
。我已经使用 merge
实现了该程序,但无法弄清楚如何使用 computeIfAbsent
来做同样的事情。在我看来 computeIfPresent
会更合适,因为 merge
只有在密钥存在时才有效, computeIfPresent
.
问题陈述:
Write an application in which multiple threads read all words from a collection of files. Use a
ConcurrentHashMap<String, Set<File>>
to track in which files each word occurs. Use themerge
method to update the map.
我的代码使用 merge
:
public static Map<String, Set<File>> reverseIndexUsingMerge(final Path path)
throws IOException {
final ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<>();
final BiConsumer<? super String, ? super Set<File>> action = (key,
value) -> map.merge(key, value, (existingValue, newValue) -> {
LOGGER.info("Received key: {}, existing value: {}, new value: {}.",
key, existingValue, newValue);
newValue.addAll(existingValue);
return newValue;
});
commonPool().invokeAll(
find(path, 1,
(p, fileAttributes) -> fileAttributes.isRegularFile())
.map(p -> new ReverseIndex(p, action))
.collect(toList()));
return unmodifiableMap(map);
}
private static class ReverseIndex implements Callable<Void> {
private final Path p;
private final BiConsumer<? super String, ? super Set<File>> action;
private static final Pattern AROUND_WHITESPACE = compile("\s");
private ReverseIndex(final Path p,
final BiConsumer<? super String, ? super Set<File>> action) {
this.p = p;
this.action = action;
}
@Override
public Void call() throws Exception {
reverseIndex().forEach(action);
return null;
}
private Map<String, Set<File>> reverseIndex() {
/* File stream needs to be closed. */
try (Stream<String> lines = lines(p, UTF_8)) {
return lines.flatMap(AROUND_WHITESPACE::splitAsStream)
.collect(
groupingBy(String::toString,
mapping(word -> p.toFile(), toSet())));
} catch (IOException e) {
LOGGER.error("Something went wrong. Get the hell outta here.",
e);
throw new UncheckedIOException(e);
}
}
}
专注于必须完成的事情,如果价值不存在。您需要做的是为缺失的条目创建一个新的 Set
值。当并发Set
。您可以利用 ConcurrentHashMap
通过映射到固定值来创建事实上的 ConcurrentHashSet
(该形式不存在),如果您让值信号存在是 Boolean.TRUE
:
ConcurrentHashMap<String, Set<File>> map=new ConcurrentHashMap<>();
final BiConsumer<? super String, ? super Set<File>> action =
(key, value) -> map.computeIfAbsent(key, x->ConcurrentHashMap.newKeySet())
.addAll(value);
我使用 computeIfAbsent
并按扩展名“.txt”过滤文件。
结果显示在控制台中。
对于IntelliJ IDEA IDE:如果结果不完整,则检查并增加"Override console cycle buffer size"(File/Settings/Editor/General/Console).
导入列表:
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
我解决这个任务:
public static void printMap(Path path){
try (Stream<Path> stream = Files.walk(path)){
ConcurrentHashMap<String, Set<File>> map = new ConcurrentHashMap<>();
stream.parallel()
.filter(p -> !(Files.isDirectory(p)) & p.getFileName()
.toString()
.toLowerCase()
.endsWith(".txt"))
.collect(Collectors.toList())
.forEach((p) -> {
try {
Files.lines(p, StandardCharsets.UTF_8)
.flatMap(s -> Arrays.asList(s.split("\PL+")).stream())
.filter(w -> w.length() > 0)
.map(String::toLowerCase)
.parallel()
.forEach(
key -> {
Set<File> tempSet = new HashSet<>();
tempSet.add(new File(p.toString()));
map.computeIfAbsent(key, x -> ConcurrentHashMap.newKeySet())
.addAll(tempSet);
});
} catch (IOException e){
} catch (UncheckedIOException e){}
});
map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(System.out::println);
} catch (IOException e){}
}
通话printMap()
:
public static void main(String[] args){
Path path = Paths.get(*somePathName*);
printMap(path);
}
如果需要使用merge
,那么只需替换
map.computeIfAbsent(key, x -> ConcurrentHashMap.newKeySet()).addAll(tempSet);
到
map.merge(key, tempSet, (oldSet, newSet) -> {oldSet.addAll(newSet); return oldSet;});