Java Map to Set?有没有一种方法可以实现预先存在的java库,以允许Map包含Set作为值



我试图创建id号的键和Set的值,但我也需要编写通用方法,对那些固定在值中的Set进行迭代(需要Iterable,因此我试图使用Set)。在Map对象的某些Generic值上实现迭代器的有效方法是什么?

您所描述的Map<X, Set<Y>>通常被称为"基于集合的MultiMap"。

这种地图的一个流行实现是Google Guava

Aksing这样一个Map的values()将返回一个Collection,该Collection将包含Y的所有值(而不是所有Set<Y>实例)。

人们可以使用基本的Map"手动"实现这样的Collection,但事实证明,要真正实现所有用例并保留Java Collections Framework语义是非常困难的。因此,使用库的实现通常是可行的。

另一种选择是使用Stream API(Java8),使用groupBy等收集器来构建Map实例,并使用其他流操作来在您想要迭代值时"展平"映射的值(例如,请参见展平集合)。

下面,找到一个SetMultiMap代码示例

public static void main(String[] args) {
Multimap<Integer, String> multiMap = HashMultimap.create();
multiMap.put(0, "key0_value0");
// Twice the same value for the same key, the Set<String> will "ignore" this
multiMap.put(0, "key0_value0"); 
multiMap.put(1, "key1_value0"); 
multiMap.put(1, "key1_value1"); 

System.out.println("Values for key 0");
System.out.println("----------------");
System.out.println(multiMap.get(0));
System.out.println("rnValues for key 1");
System.out.println("----------------");
System.out.println(multiMap.get(1));
System.out.println("rnAll values");
System.out.println("------------");
System.out.println(multiMap.values());
// Entries are all Integer/String associations
Collection<Entry<Integer, String>> entries = multiMap.entries();
System.out.println("rnNumber of entries : " + entries.size());
// We can build a standard Java Map out of the Multimap
Map<Integer, Collection<String>> realJavaMap = multiMap.asMap();
// The map's values are actually guaranteed to be Sets
System.out.println("Multimap as Map, values implement Set : " + Set.class.isAssignableFrom(realJavaMap.values().iterator().next().getClass()));
// The java Map is a live view of the multimap
realJavaMap.get(0).add("key0_value1"); // Actions on realJavaMap will update multimap
System.out.println("rnValues for key 0");
System.out.println("----------------");
System.out.println(multiMap.get(0));

}

该程序输出:

Values for key 0
----------------
[key0_value0]
Values for key 1
----------------
[key1_value1, key1_value0]
All values
------------
[key0_value0, key1_value1, key1_value0]
Number of entries : 3
Multimap as Map, value class is set : true
Values for key 0
----------------
[key0_value1, key0_value0]

这似乎有效:

class MapOfSetsIterable<V> implements Iterable<V> {
    private final Map<?, Set<V>> map;
    public MapOfSetsIterable(Map<?, Set<V>> map) {
        this.map = map;
    }
    @Override
    public Iterator<V> iterator() {
        return new MapOfSetsIterator();
    }
    private class MapOfSetsIterator implements Iterator<V> {
        final Iterator<Set<V>> sets = map.values().iterator();
        Iterator<V> i = sets.hasNext() ? sets.next().iterator() : Collections.EMPTY_SET.iterator();
        V next = null;
        @Override
        public boolean hasNext() {
            while (next == null && (sets.hasNext() || i.hasNext())) {
                if (!i.hasNext() && sets.hasNext()) {
                    i = sets.next().iterator();
                }
                if (i.hasNext()) {
                    next = i.next();
                }
            }
            return next != null;
        }
        @Override
        public V next() {
            if (next == null) {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
            }
            V n = next;
            next = null;
            return n;
        }
    }
}
enum E {
    A, B
};
public void test() {
    Map<Integer, Set<E>> map = new HashMap<>();
    map.put(1, EnumSet.of(E.A));
    map.put(2, EnumSet.of(E.B));
    map.put(3, EnumSet.of(E.A, E.B));
    for (E e : new MapOfSetsIterable<>(map)) {
        System.out.println(e);
    }
}

最新更新