Java:两个地图并集的只读视图?

Java:两个地图并集的只读视图?,java,collections,Java,Collections,在公共库(Guava、commons collections等)中是否有java.util.Map的实现,该库充当两个支持映射的并集的只读视图?也就是说,它的行为类似于下面的union,但没有显式创建新的映射实例,也没有反映基础映射的未来更新: 映射a、b;//从某处 Map union=newhashmap(); 普塔尔工会(b); 普塔尔联盟(a); 我的搜索没有找到任何结果。当您自己实现它时,您可以扩展AbstractMap,它唯一必须实现的方法是entrySet()。通过扩展Abstr

在公共库(Guava、commons collections等)中是否有
java.util.Map
的实现,该库充当两个支持映射的并集的只读视图?也就是说,它的行为类似于下面的
union
,但没有显式创建新的映射实例,也没有反映基础映射的未来更新:

映射a、b;//从某处
Map union=newhashmap();
普塔尔工会(b);
普塔尔联盟(a);

我的搜索没有找到任何结果。

当您自己实现它时,您可以扩展
AbstractMap
,它唯一必须实现的方法是
entrySet()
。通过扩展
AbstractSet
来实现条目集需要实现
iterator()
size()
。这是实现映射所需的最低要求

由于一个键可能出现在两个地图中,我们不能简单地总结地图的大小。因此,它必须基于与
iterator()实现相同的迭代逻辑:

  • 从第一个映射中获取所有条目
  • 从第二个映射中获取其键未出现在第一个映射中的条目
请注意,结果大小可能超过
int
值范围。没有真正的解决办法。指定当有更多元素时,将返回
Integer.MAX\u VALUE
,但当许多开发人员不知道此策略时,我不会感到惊讶

当实现迭代器或类似工具分发
Map.Entry
实例时,我们必须注意不要使用原始Map的条目,因为这将允许通过条目的
setValue
方法修改原始Map

可以使用流API简洁地表示迭代逻辑:

公共类MergedMap扩展了AbstractMap{
最终地图第一,第二;
公共合并地图(地图第一,地图第二){
this.first=Objects.requirennoull(first);
this.second=Objects.requirennoull(秒);
}
//强制性方法
最终集合entrySet=new AbstractSet(){
@凌驾
公共迭代器迭代器(){
返回流().iterator();
}
@凌驾
公共整数大小(){
长大小=流().count();
返回大小new AbstractMap.SimpleImmutableEntry(e.getKey(),e.getValue());
}
@凌驾
公共流并行流(){
返回流().parallel();
}
@凌驾
公共拆分器拆分器(){
返回流().spliterator();
}
};
第二流{
返回second.entrySet().stream().filter(e->!first.containsKey(e.getKey());
}
@凌驾
公共集入口集(){
返回入口集;
}
//优化
@凌驾
公共布尔containsKey(对象键){
返回第一个.containsKey(键)| |第二个.containsKey(键);
}
@凌驾
公共布尔包含值(对象值){
首先返回。containsValue(值)||
secondStream().anyMatch(谓词.isEqual(值));
}
@凌驾
public V get(对象键){
V=第一个。获取(键);
返回v!=null | | first.containsKey(键)?v:second.get(键);
}
@凌驾
public V getOrDefault(对象键,V defaultValue){
V=第一个。获取(键);
返回v!=null | | first.containsKey(键)?v:
第二,getOrDefault(key,defaultValue);
}
@凌驾

public void forEach(BiConsumer我非常喜欢Holger的答案,但这里有一个更简单的方法,易于理解和调试。它将所有只读方法委托给按需计算的union:

public class UnionMap<K,V> extends AbstractMap<K,V> {
    private Map<K,V> a, b;

    public UnionMap(Map<K, V> a, Map<K, V> b) {
        this.a = Objects.requireNonNull(a);
        this.b = Objects.requireNonNull(b);
    }

    private Map<K,V> union() {
        Map<K, V> union = new HashMap<>(b);
        union.putAll(a);
        return union;
    }

    @Override
    public Set<Map.Entry<K, V>> entrySet() {
        return union().entrySet();
    };

    @Override
    public boolean containsKey(Object key) {
        return union().containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return union().containsValue(value);
    }

    @Override
    public V get(Object key) {
        return union().get(key);
    }

    @Override
    public V getOrDefault(Object key, V defaultValue) {
        return union().getOrDefault(key, defaultValue);
    }

    @Override
    public void forEach(BiConsumer<? super K, ? super V> action) {
        union().forEach(action);
    }
}
公共类UnionMap扩展了AbstractMap{
私人地图a、b;
公共地图(地图a、地图b){
this.a=Objects.requirennull(a);
this.b=Objects.requirennull(b);
}
私有地图联盟(){
Map union=newhashmap(b);
普塔尔联盟(a);
回归联盟;
}
@凌驾
公共集入口集(){
返回union().entrySet();
};
@凌驾
公共布尔containsKey(对象键){
返回union().containsKey(键);
}
@凌驾
公共布尔包含值(对象值){
返回union().containsValue(值);
}
@凌驾
public V get(对象键){
返回union().get(键);
}
@凌驾
public V getOrDefault(对象键,V defaultValue){
返回union().getOrDefault(键,defaultValue);
}
@凌驾

public void forEach(BiConsumerI猜测不合并源地图就不可能创建此类视图,否则,如果map
a
与map
b
具有相同的键,但值不同,则可能会发生冲突。简短回答“否”。但编写代码却很简单。@Aron此特定行为意味着它没有标准实现。但您可以通过以下方式为它实现
java.utils.Map
接口yourself@Bohemian“琐碎”是主观的。我不知道如何实现有效的
size()
迭代器
,用于不需要存储已遇到元素的联合。@Bohemian我再次考虑了它,它实际上是可行的。不过,不一定是“微不足道的”。Map a的条目应该覆盖Map b。你的stream()是否实现了你的entrySet()如果映射共享密钥,则删除重复条目?顺便说一句,我不会将impl基于流-这不是必需的,也不是最简单的。@波希米亚式的。所有需要过滤第二个映射条目的操作都使用方法
secondStream()
,为过滤逻辑设置一个单独的位置。这是不使用额外存储的最简单方法。它只是一个两行程序。我知道,您建议的最简单方法是只使用