Java 要设置地图吗?有没有办法实现预先存在的 java 库以允许包含集合作为值的映射?
Java Map to Set? Is there a way to implement pre-existing java libraries to allow a Map that contains Sets as values?
我正在尝试创建 ID 号的键和 Set 的值,但我还需要编写通用方法来迭代(需要 Iterable,因此我尝试使用 set 的原因)那些卡在值中的 Set .在属于 Map 对象的一些通用值上实现 Iterator 的有效方法是什么?
您所描述的 Map<X, Set<Y>>
通常被称为 "Set based MultiMap"。
这种 Map 的流行实现是 Google Guava
使用这样的地图,它的 values()
将 return 一个包含 Y
的所有值(而不是所有 Set<Y>
实例)的集合。
可以使用基本映射实现 "by hand" 这样的集合,但事实证明,要针对所有用例正确实施并保留 Java 集合框架语义,这证明是非常困难的。所以使用库的实现通常是可行的方法。
另一种选择是使用 Stream API (Java8),使用 groupBy
等收集器来构建 Map 实例,以及对 [=37= 的其他流操作] 当您想遍历值时地图的值(参见 Flattening a collection 例如)。
在下面找到一个 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("\r\nValues for key 1");
System.out.println("----------------");
System.out.println(multiMap.get(1));
System.out.println("\r\nAll 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("\r\nNumber 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("\r\nValues 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);
}
}
我正在尝试创建 ID 号的键和 Set 的值,但我还需要编写通用方法来迭代(需要 Iterable,因此我尝试使用 set 的原因)那些卡在值中的 Set .在属于 Map 对象的一些通用值上实现 Iterator 的有效方法是什么?
您所描述的 Map<X, Set<Y>>
通常被称为 "Set based MultiMap"。
这种 Map 的流行实现是 Google Guava
使用这样的地图,它的 values()
将 return 一个包含 Y
的所有值(而不是所有 Set<Y>
实例)的集合。
可以使用基本映射实现 "by hand" 这样的集合,但事实证明,要针对所有用例正确实施并保留 Java 集合框架语义,这证明是非常困难的。所以使用库的实现通常是可行的方法。
另一种选择是使用 Stream API (Java8),使用 groupBy
等收集器来构建 Map 实例,以及对 [=37= 的其他流操作] 当您想遍历值时地图的值(参见 Flattening a collection 例如)。
在下面找到一个 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("\r\nValues for key 1");
System.out.println("----------------");
System.out.println(multiMap.get(1));
System.out.println("\r\nAll 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("\r\nNumber 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("\r\nValues 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);
}
}