首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >番石榴: Set<K> + Function<K,V> = Map<K,V>?

番石榴: Set<K> + Function<K,V> = Map<K,V>?
EN

Stack Overflow用户
提问于 2010-10-06 03:08:39
回答 6查看 7.1K关注 0票数 33

是否有一种惯用的方式来获取Set<K>Function<K,V>,并获得Map<K,V>实时视图?(也就是说,MapSetFunction组合支持,如果向Set添加了元素,那么相应的条目也存在于Map中)。

(有关实时视图的更多讨论,请参见Collections2.filter )

如果不需要实时视图怎么办?还有什么比这更好的吗?

代码语言:javascript
复制
public static <K,V> Map<K,V> newMapFrom(Set<K> keys, Function<? super K,V> f) {
    Map<K,V> map = Maps.newHashMap();
    for (K k : keys) {
        map.put(k, f.apply(k));
    }
    return map;
}
EN

回答 6

Stack Overflow用户

发布于 2010-10-06 08:00:18

从集合和函数创建映射

这里有两个类,每个类都应该完成这项工作。第一个只显示集合的映射视图,而第二个则可以通过一个特殊的接口将值写回集合。

调用语法:

代码语言:javascript
复制
Map<K,V> immutable = new SetBackedMap<K,V>(Set<K> keys, Function<K,V> func);
Map<K,V> mutable = new MutableSetBackedMap<K,V>(Set<K> keys, Function<K,V> func);

把这段代码放哪儿?

附带注意:如果番石榴是我的库,我会通过地图类访问它们:

代码语言:javascript
复制
Map<K,V> immutable = Maps.immutableComputingMap(Set<K> keys, Function<K,V> func);
Map<K,V> mutable = Maps.mutableComputingMap(Set<K> keys, Function<K,V> func);

不变版本:

我将此作为一个单向视图来实现:

  • 对集合的更改反映在映射中,反之亦然(而且您也不能更改映射,put(key, value)方法没有实现)。
  • entrySet()迭代器在内部使用set迭代器,因此它还将继承内部迭代器对ConcurrentModificationException的处理。
  • put(k,v)entrySet().iterator().remove()都会抛出UnsupportedOperationException
  • 值缓存在WeakHashMap中,没有特殊的并发处理,即没有任何级别的同步。这在大多数情况下都可以,但是如果您的函数很昂贵,您可能需要添加一些锁定。

代码:

代码语言:javascript
复制
public class SetBackedMap<K, V> extends AbstractMap<K, V>{

    private class MapEntry implements Entry<K, V>{
        private final K key;
        public MapEntry(final K key){
            this.key = key;
        }
        @Override
        public K getKey(){
            return this.key;
        }
        @Override
        public V getValue(){
            V value = SetBackedMap.this.cache.get(this.key);
            if(value == null){
                value = SetBackedMap.this.funk.apply(this.key);
                SetBackedMap.this.cache.put(this.key, value);
            }
            return value;
        }
        @Override
        public V setValue(final V value){
            throw new UnsupportedOperationException();
        }
    }

    private class EntrySet extends AbstractSet<Entry<K, V>>{

        public class EntryIterator implements Iterator<Entry<K, V>>{
            private final Iterator<K> inner;
            public EntryIterator(){
                this.inner = EntrySet.this.keys.iterator();
            }
            @Override
            public boolean hasNext(){
                return this.inner.hasNext();
            }
            @Override
            public Map.Entry<K, V> next(){
                final K key = this.inner.next();
                return new MapEntry(key);
            }
            @Override
            public void remove(){
                throw new UnsupportedOperationException();
            }
        }

        private final Set<K> keys;

        public EntrySet(final Set<K> keys){
            this.keys = keys;
        }

        @Override
        public Iterator<Map.Entry<K, V>> iterator(){
            return new EntryIterator();
        }

        @Override
        public int size(){
            return this.keys.size();
        }

    }

    private final WeakHashMap<K, V> cache;
    private final Set<Entry<K, V>> entries;
    private final Function<? super K, ? extends V> funk;

    public SetBackedMap(
        final Set<K> keys, Function<? super K, ? extends V> funk){
        this.funk = funk;
        this.cache = new WeakHashMap<K, V>();
        this.entries = new EntrySet(keys);
    }

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

}

测试:

代码语言:javascript
复制
final Map<Integer, String> map =
    new SetBackedMap<Integer, String>(
        new TreeSet<Integer>(Arrays.asList(
            1, 2, 4, 8, 16, 32, 64, 128, 256)),
        new Function<Integer, String>(){

            @Override
            public String apply(final Integer from){
                return Integer.toBinaryString(from.intValue());
            }
        });
for(final Map.Entry<Integer, String> entry : map.entrySet()){
    System.out.println(
        "Key: " + entry.getKey()
        + ", value: " + entry.getValue());
}

输出:

代码语言:javascript
复制
Key: 1, value: 1
Key: 2, value: 10
Key: 4, value: 100
Key: 8, value: 1000
Key: 16, value: 10000
Key: 32, value: 100000
Key: 64, value: 1000000
Key: 128, value: 10000000
Key: 256, value: 100000000

可变版本:

虽然我认为这是一个好主意,使这个单向,这是一个版本的Emil,提供了一个双向视图(这是变体埃米尔的变体我的解决方案:-)。它需要一个扩展的映射接口,我将称之为ComputingMap,以明确这是一个映射,在那里调用put(key, value)是没有意义的。

地图接口:

代码语言:javascript
复制
public interface ComputingMap<K, V> extends Map<K, V>{
    boolean removeKey(final K key);
    boolean addKey(final K key);
}

地图实现:

代码语言:javascript
复制
public class MutableSetBackedMap<K, V> extends AbstractMap<K, V> implements
    ComputingMap<K, V>{

    public class MapEntry implements Entry<K, V>{

        private final K key;

        public MapEntry(final K key){
            this.key = key;
        }

        @Override
        public K getKey(){
            return this.key;
        }

        @Override
        public V getValue(){
            V value = MutableSetBackedMap.this.cache.get(this.key);
            if(value == null){
                value = MutableSetBackedMap.this.funk.apply(this.key);
                MutableSetBackedMap.this.cache.put(this.key, value);
            }
            return value;
        }

        @Override
        public V setValue(final V value){
            throw new UnsupportedOperationException();
        }

    }

    public class EntrySet extends AbstractSet<Entry<K, V>>{

        public class EntryIterator implements Iterator<Entry<K, V>>{

            private final Iterator<K> inner;

            public EntryIterator(){
                this.inner = MutableSetBackedMap.this.keys.iterator();
            }

            @Override
            public boolean hasNext(){
                return this.inner.hasNext();
            }

            @Override
            public Map.Entry<K, V> next(){
                final K key = this.inner.next();
                return new MapEntry(key);
            }

            @Override
            public void remove(){
                throw new UnsupportedOperationException();
            }

        }

        public EntrySet(){
        }

        @Override
        public Iterator<Map.Entry<K, V>> iterator(){
            return new EntryIterator();
        }

        @Override
        public int size(){
            return MutableSetBackedMap.this.keys.size();
        }

    }

    private final WeakHashMap<K, V> cache;
    private final Set<Entry<K, V>> entries;
    private final Function<? super K, ? extends V> funk;
    private final Set<K> keys;

    public MutableSetBackedMap(final Set<K> keys,
        final Function<? super K, ? extends V> funk){
        this.keys = keys;
        this.funk = funk;
        this.cache = new WeakHashMap<K, V>();
        this.entries = new EntrySet();
    }

    @Override
    public boolean addKey(final K key){
        return this.keys.add(key);
    }

    @Override
    public boolean removeKey(final K key){
        return this.keys.remove(key);
    }

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

}

测试:

代码语言:javascript
复制
public static void main(final String[] args){
    final ComputingMap<Integer, String> map =
        new MutableSetBackedMap<Integer, String>(
            new TreeSet<Integer>(Arrays.asList(
                1, 2, 4, 8, 16, 32, 64, 128, 256)),
            new Function<Integer, String>(){

                @Override
                public String apply(final Integer from){
                    return Integer.toBinaryString(from.intValue());
                }
            });
    System.out.println(map);
    map.addKey(3);
    map.addKey(217);
    map.removeKey(8);
    System.out.println(map);
}

输出:

代码语言:javascript
复制
{1=1, 2=10, 4=100, 8=1000, 16=10000, 32=100000, 64=1000000, 128=10000000, 256=100000000}
{1=1, 2=10, 3=11, 4=100, 16=10000, 32=100000, 64=1000000, 128=10000000, 217=11011001, 256=100000000}
票数 29
EN

Stack Overflow用户

发布于 2011-07-26 18:23:08

注意。肖恩·帕特里克·弗洛伊德()的回答虽然非常有用,但有一个缺陷。一个简单的例子,但我花了一段时间进行调试,所以不要陷入相同的陷阱: MapEntry类需要等于和哈希代码实现。这里是我的(来自javadoc的简单副本)。

代码语言:javascript
复制
@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Entry)) {
        return false;
    }
    Entry<?, ?> e2 = (Entry<?, ?>) obj;
    return (getKey() == null ? e2.getKey() == null : getKey().equals(e2.getKey()))
        && (getValue() == null ? e2.getValue() == null : getValue().equals(e2.getValue()));
}

@Override
public int hashCode() {
    return (getKey() == null ? 0 : getKey().hashCode()) ^
        (getValue() == null ? 0 : getValue().hashCode());
}

这个回复作为对相关答案的评论会更好,但是AFAIU我没有权利发表评论(或者没有找到如何!)。

票数 22
EN

Stack Overflow用户

发布于 2010-10-06 03:52:17

番石榴14现在有Maps.asMap作为集合的视图,Maps.toMap用于不可变的副本。

您可以在这里看到有关问题的大部分讨论:https://github.com/google/guava/issues/56

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3869258

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档