我需要一个可以包含空值并可以返回Optional包装值的映射。
HashMap
可以有空值,但不会使调用者明显知道该值可以为空。Map<K, Optional<V>>
,但对于数据类型使用Optional
是不理想的。 (有关仅将Optional
用于返回的Use of Optional in a map的相关SO帖子)HashMap
类,因为它的entrySet()
方法仍然面临着相同的空值问题,因此我编写了包装HashMap
并包含{{1}的类}和返回get()
包装值的自定义Entry.getValue()
。 (仍在弄清楚如何为此类编写Optional
版本)Collectors.toMap
不能轻松地“替换”该类(不能编程为接口),并且在我的业务逻辑中流传着一个定制程度不多的映射,对此我有点不满意。 Map
期望:public class CustomMap<K, V> {
private final Map<K, V> map;
public CustomMap() {
map = new HashMap<>();
}
public Optional<V> get(@NonNull final K k) { // Lombok.NonNull
return Optional.ofNullable(map.get(k));
}
public void put(@NonNull final K k, @Nullable final V v) {
map.put(k, v);
}
public Set<Entry<K, V>> entrySet() {
return map.entrySet().stream()
.map(e -> new Entry(e.getKey(), e.getValue()))
.collect(toImmutableSet());
}
public Set<K> keySet() {
return map.keySet();
}
public static class Entry<K, V> {
private final K k;
private final V v;
public Entry(K k, V v) {
this.k = k;
this.v = v;
}
public K getKey() {
return k;
}
public Optional<V> getValue() {
return Optional.ofNullable(v);
}
}
}
应该返回customMap.get(K)
包装的对象,并且Optional
不需要接受customMap.put(K, V)
作为输入。 Optional<V>
应该能够充当CustomMap
。
应该有一种更简洁,可扩展的方法来实现这一目标,我觉得我缺少明显的东西。有什么建议吗?
答案 0 :(得分:1)
如果我对您的理解正确,那么您想使Optionals变得显而易见,并且您不愿意推出一些与Map实现不同的自定义map实现;这与您认为Optional数据类型不理想的说法相冲突。
假设您只是不想在地图中存储Optional实例,则可能的解决方案将是Map实现,该实现将(内部)Map<K,V>
封装为空值作为Map<K, Optional<V>>
,例如
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class OptionalMap<K, V> implements Map<K, Optional<V>> {
private final Map<K, V> map;
public OptionalMap(Map<K, V> map) {
this.map = map;
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
if (!(value instanceof Optional)) {
return false;
}
return map.containsValue(((Optional<?>)value).get());
}
@Override
public Set<Entry<K, Optional<V>>> entrySet() {
return
map.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<K, Optional<V>>(e.getKey(), Optional.ofNullable(e.getValue())))
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
}
@Override
public Optional<V> get(Object key) {
return Optional.ofNullable(map.get(key));
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public Optional<V> put(K key, Optional<V> value) {
final Optional<V> previous = Optional.ofNullable(map.get(key));
map.put(key, value.orElse(null));
return previous;
}
@Override
public void putAll(Map<? extends K, ? extends Optional<V>> other) {
Map<K, V> unwrappedMap =
other.entrySet().stream()
.<Entry<K, V>>map(entry -> new AbstractMap.SimpleEntry<K, V>(entry.getKey(), entry.getValue().orElse(null)))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
map.putAll(unwrappedMap);
}
@Override
public Optional<V> remove(Object key) {
return Optional.ofNullable(map.remove(key));
}
@Override
public int size() {
return map.size();
}
@Override
public Collection<Optional<V>> values() {
return
map.values().stream()
.map(e -> Optional.ofNullable(e))
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}
}