Guava:设置< K> +函数< K,V> = Map< K,V>? [英] Guava: Set<K> + Function<K,V> = Map<K,V>?
问题描述
是否有一个惯用的方法来取 Set< K>
和 Function< K,V>
并获得地图< K,V>
实时取景? (即 Map
由 Set
和 Function
组合,并且如果例如将一个元素添加到 Set
中,则相应的条目也存在于 Map
中)。
Is there an idiomatic way to take a Set<K>
and a Function<K,V>
, and get a Map<K,V>
live view? (i.e. the Map
is backed by the Set
and Function
combo, and if e.g. an element is added to the Set
, then the corresponding entry also exists in the Map
).
(请参阅 Collections2.filter
了解更多有关直播的讨论视图)
(see e.g. Collections2.filter
for more discussion on live views)
如果不需要实时视图会怎么样?有比这更好的东西:
What if a live view is not needed? Is there something better than this:
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;
}
推荐答案
和一个函数
这里有两个类,每个都应该做这个工作。第一个只显示集合的地图视图,第二个可以通过一个特殊的接口将值写回集合。
Creating a Map from a Set and a Function
Here are two classes that should each do the job. The first just shows a map view of the set, while the second can write values back to the set through a special interface.
调用语法:
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);
何处放置此代码?
注意:如果番石榴是我的图书馆,我可以通过地图类别:
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);
不可更改的版本:
我已将此实现为单向视图:
Immutable version:
I have implemented this as a one-way view:
- 对集合的更改反映在
的映射,但不是反过来(你不能改变地图,put(key,value)
方法没有实现)。 li>
-
entrySet()
迭代器在内部使用
集合迭代器,因此
也会继承内部迭代器
处理
ConcurrentModificationException
。 -
put(k,v) code>和
entrySet()。iterator()。remove()
将
throw
UnsupportedOperationException
。 - 值缓存在
WeakHashMap
,
中,即在
任何级别上没有同步。这在大多数情况下会做,但如果你的功能很贵,你可能想要添加一些锁。
- Changes to the set are reflected in
the map, but not vice-versa (and you can't change the map anyway, the
put(key, value)
method isn't implemented). - The
entrySet()
iterator uses the set iterator internally, so it will also inherit the internal iterator's handling ofConcurrentModificationException
. - Both
put(k,v)
andentrySet().iterator().remove()
will throwUnsupportedOperationException
. - Values are cached in a
WeakHashMap
, with no special concurrency handling, i.e. there is no synchronization at any level. This will do for most cases, but if your function is expensive, you might want to add some locking.
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;
}
}
strong>
Test:
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());
}
输出:
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
< h2> Mutable版本:
虽然我认为这是一个单向的好主意,这里有一个Emil的版本提供了一个双向视图的Emil的我的解决方案的变化:-))。它需要一个扩展的映射接口,我将调用 ComputingMap
清楚地表明这是一个映射,它没有意义调用 put键,值)
。
Mutable Version:
While I think it's a good idea to make this one-way, here's a version for Emil that provides a two-way view (it's a variation of Emil's variation of my solution :-)). It requires an extended map interface that I'll call ComputingMap
to make clear that this is a map where it doesn't make sense to call put(key, value)
.
地图界面:
public interface ComputingMap<K, V> extends Map<K, V>{
boolean removeKey(final K key);
boolean addKey(final K key);
}
地图实施:
$ b
Map implementation:
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;
}
}
strong>
Test:
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);
}
输出:
{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}
这篇关于Guava:设置< K> +函数< K,V> = Map< K,V>?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!