Java 8 为什么java Map.merge没有传递给供应商?
在java中,我需要一个方法,允许我修改一个值(如果存在),或者插入一个值(如果不存在)。与“合并”类似,但:Java 8 为什么java Map.merge没有传递给供应商?,java-8,Java 8,在java中,我需要一个方法,允许我修改一个值(如果存在),或者插入一个值(如果不存在)。与“合并”类似,但: 我想传递一个价值供应商,而不是一个价值,以避免在不需要时创建它 如果该值存在,我不想重新插入或删除它,只需使用容器访问它的方法即可 我不得不写这个。我自己编写的问题是,并发映射的版本并不简单 public static <K, V> V putOrConsume(Map<K, V> map, K key, Supplier<V> ifAbsent,
public static <K, V> V putOrConsume(Map<K, V> map, K key, Supplier<V> ifAbsent, Consumer<V> ifPresent) {
V val = map.get(key);
if (val != null) {
ifPresent.accept(val);
} else {
map.put(key, ifAbsent.get());
}
return val;
}
public static V putOrConsume(地图地图、K键、供应商ifAbsent、消费者ifPresent){
V val=map.get(键);
如果(val!=null){
如果存在,则接受(val);
}否则{
map.put(key,ifAbsent.get());
}
返回val;
}
实现它的最佳“标准”方法是使用compute()
Map Map=newhashmap();
双函数转换=(k,v)->v==null?“新的”+k:“旧的”+v;
map.compute(“x”,convert);
map.compute(“x”,convert);
System.out.println(map.get(“x”))//打印旧的新的
现在,比方说,你有你的供应商和消费者,并愿意遵循干燥的原则。然后您可以使用一个简单的函数组合器:
Map<String, String> map = new HashMap<>();
Supplier<String> ifAbsent = () -> "new";
Consumer<String> ifPresent = System.out::println;
BiFunction<String, String, String> putOrConsume = (k, v) -> {
if (v == null) return ifAbsent.get();
ifPresent.accept(v);
return v;
};
map.compute("x", putOrConsume); //nothing
map.compute("x", putOrConsume); //prints "new"
Map Map=newhashmap();
供应商ifAbsent=()->“新”;
消费者ifPresent=System.out::println;
双功能计算机消费=(k,v)->{
if(v==null)返回ifAbsent.get();
如果存在,接受(v);
返回v;
};
map.compute(“x”,putOrConsume)//没有什么
map.compute(“x”,putOrConsume)//打印“新”
显然,您可以编写一个组合函数,它接受供应商和消费者并返回双函数,从而使上述代码更加通用
这种方法的缺点是,即使您只是简单地使用值,也会额外调用map.put(),也就是说,在键查找时,它会稍微慢一点。好消息是,map实现将简单地替换值,而不创建新节点。即,不会创建新对象或垃圾收集。大多数情况下,这种权衡是合理的
map.compute(…)
和map.putIfAbsent(…)
比相当专业化的建议的putOrConsume(…)强大得多。它是如此的不对称,我实际上会在代码中回顾您需要它的原因。您可以通过一个简单的助手方法,以及一个本地类的帮助来实现您想要的,以了解您的ifAbsent
供应商是否已被使用:
public static <K, V> V putOrConsume(
Map<K, V> map,
K key,
Supplier<V> ifAbsent,
Consumer<V> ifPresent) {
class AbsentSupplier implements Supplier<V> {
boolean used = false;
public V get() {
used = true;
return ifAbsent.get();
}
}
AbsentSupplier absentSupplier = new AbsentSupplier();
V computed = map.compute(
key,
(k, v) -> v == null ?
absentSupplier.get() :
consumeAndReturn(v, ifPresent));
return absentSupplier.used ? null : computed;
}
private static <V> V consumeAndReturn(V v, Consumer<V> consumer) {
consumer.accept(v);
return v;
}
publicstaticv putOrConsume(
地图,
K键,
供应商(如有),
消费者(如有){
类缺席供应商实现供应商{
布尔值=假;
公共V get(){
使用=正确;
返回ifAbsent.get();
}
}
缺席供应商缺席供应商=新缺席供应商();
V computed=map.compute(
钥匙
(k,v)->v==null?
缺席供应商。获取():
(v,如果存在);
返回缺席供应商。是否已使用?空:已计算;
}
专用静态V消费者和返回(V,消费者){
消费者接受(v);
返回v;
}
棘手的部分是查找您是否使用了ifAbsent
供应商来返回null
或现有的已消耗值
helper方法只是调整
ifPresent
使用者,使其行为类似于一元运算符,使用给定值并返回它。与其他答案不同,您还可以使用Map.compute
方法,并将函数
s与界面默认方法/静态方法相结合,使代码更具可读性。例如:
用法
//仅当值存在时使用
消费者行动=。。。;
compute(key,ValueMapping.ifPresent(action));
//如果没有价值,就创造价值
供应商=。。。;
compute(key,ValueMapping.ifPresent(action.orElse(supplier));
//如果缺少值,则从键映射值
函数映射=。。。;
compute(key,ValueMapping.ifPresent(action.orElse(mapping));
//orElse支持短路功能
compute(键,ValueMapping.ifPresent(操作)
.orElse(供应商)
.orElse(()->fail(“不应调用它”)+
“如果以前的orElse计算的值”);
T失败(字符串消息){
抛出新断言错误(消息);
}
值映射
接口值映射扩展了双功能{
默认值映射orElse(供应商其他){
返回orElse(k->other.get());
}
默认值映射orElse(函数其他){
返回(k,v)->{
R结果=此。应用(k,v);
返回结果!=null?结果:其他。应用(k);
};
}
静态值映射ifPresent(使用者操作){
返回(k,v)->{
如果(v!=null){
行动.接受(v);
}
返回v;
};
}
}
注
在以前的版本中,我在ValueMapping
中使用了Objects.isNull
。@Holger指出,这是一个过度使用的情况,应该用更简单的条件取代它空 大多数(相关的)Map
实现以执行单个查找的方式覆盖compute
的默认实现,如果键存在,换句话说,不需要“额外调用Map.put()”,即使更改值也是如此…
public static <K, V> V putOrConsume(
Map<K, V> map,
K key,
Supplier<V> ifAbsent,
Consumer<V> ifPresent) {
class AbsentSupplier implements Supplier<V> {
boolean used = false;
public V get() {
used = true;
return ifAbsent.get();
}
}
AbsentSupplier absentSupplier = new AbsentSupplier();
V computed = map.compute(
key,
(k, v) -> v == null ?
absentSupplier.get() :
consumeAndReturn(v, ifPresent));
return absentSupplier.used ? null : computed;
}
private static <V> V consumeAndReturn(V v, Consumer<V> consumer) {
consumer.accept(v);
return v;
}
//only consuming if value is present
Consumer<V> action = ...;
map.compute(key,ValueMapping.ifPresent(action));
//create value if value is absent
Supplier<V> supplier = ...;
map.compute(key,ValueMapping.ifPresent(action).orElse(supplier));
//map value from key if value is absent
Function<K,V> mapping = ...;
map.compute(key,ValueMapping.ifPresent(action).orElse(mapping));
//orElse supports short-circuit feature
map.compute(key,ValueMapping.ifPresent(action)
.orElse(supplier)
.orElse(() -> fail("it should not be called "+
"if the value computed by the previous orElse")));
<T> T fail(String message) {
throw new AssertionError(message);
}
interface ValueMapping<T, R> extends BiFunction<T, R, R> {
default ValueMapping<T, R> orElse(Supplier<R> other) {
return orElse(k -> other.get());
}
default ValueMapping<T, R> orElse(Function<T, R> other) {
return (k, v) -> {
R result = this.apply(k, v);
return result!=null ? result : other.apply(k);
};
}
static <T, R> ValueMapping<T, R> ifPresent(Consumer<R> action) {
return (k, v) -> {
if (v!=null) {
action.accept(v);
}
return v;
};
}
}