![](/img/trans.png)
[英]Convert Set<Map.Entry<K, V>> to HashMap<K, V>
[英]Guava: Set<K> + Function<K,V> = Map<K,V>?
是否有慣用的方法來獲取Set<K>
和Function<K,V>
,並獲得Map<K,V>
實時視圖? (即, Map
由Set
和Function
組合支持,如果例如將一個元素添加到Set
,則相應的條目也存在於Map
)。
(有關實時視圖的更多討論,請參閱例如Collections2.filter
)
如果不需要實時視圖怎么辦? 有沒有比這更好的東西:
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;
}
這里有兩個應該分別完成工作的課程。 第一個只顯示集合的地圖視圖,而第二個可以通過特殊接口將值寫回集合。
調用語法:
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);
旁注:如果guava是我的庫,我可以通過Maps類訪問它們:
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
,沒有特殊的並發處理,即在任何級別都沒有同步。 這適用於大多數情況,但如果您的功能很昂貴,您可能需要添加一些鎖定。 碼:
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;
}
}
測試:
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
雖然我覺得單向制作是一個好主意,但這里是Emil的一個版本,它提供了一個雙向視圖(它是Emil對我的解決方案變體的變體:-))。 它需要一個擴展的地圖界面,我將其稱為ComputingMap
,以明確這是一個調用put(key, value)
沒有意義的地圖。
地圖界面:
public interface ComputingMap<K, V> extends Map<K, V>{
boolean removeKey(final K key);
boolean addKey(final K key);
}
地圖實施:
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;
}
}
測試:
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}
警告。 肖恩·帕特里克·弗洛伊德的回答雖然非常有用,卻有一個缺陷。 一個簡單的,但我花了一段時間來調試所以不要陷入同一個陷阱:MapEntry類需要equals和hashcode實現。 這是我的(來自javadoc的簡單副本)。
@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我無權發表評論(或者沒有找到如何!)。
Guava 14現在具有Maps.asMap
用於查看不可變副本的Set和Maps.toMap
。
您可以在此處查看有關問題的大部分討論: https : //github.com/google/guava/issues/56
對於非實時視圖,代碼存在於LambdaJ中,帶有Lambda.map(Set, Converter)
。
Set<K> setKs = new Set<K>();
Converter<K, V> converterKv = new Converter<K,V>{
@Override
public V convert(K from){
return null; //Not useful here but you can do whatever you want
}
}
Map<K, V> mapKvs = Lambda.map(setKs, converterKv);
我嘗試了自己的實現: http : //ideone.com/Kkpcn如評論中所述,我必須擴展另一個類,所以我只是實現了Map
,這就是為什么有這么多代碼的原因。
有一個完全無用的(或沒有?)功能,允許您動態更改轉換器。
我不知道這是不是你的意思是現場觀點。這里的任何方式都是我的嘗試。
public class GuavaTst {
public static void main(String[] args) {
final Function<String, String> functionToLower = new Function<String, String>() {
public String apply (String input) {
return input.toLowerCase();
}
};
final Set<String> set=new HashSet<String>();
set.add("Hello");
set.add("BYE");
set.add("gOOd");
Map<String, String> testMap = newLiveMap(set,functionToLower);
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
set.add("WoRld");
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
testMap.put("OMG","");
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
}
static <K,V> Map<K,V> newLiveMap(final Set<K> backEnd,final Function<K,V> fun)
{
return new HashMap<K,V>(){
@Override
public void clear() {
backEnd.clear();
}
@Override
public boolean containsKey(Object key) {
return backEnd.contains(key);
}
@Override
public boolean isEmpty() {
return backEnd.isEmpty();
}
@Override
public V put(K key, V value) {
backEnd.add(key);
return null;
}
@Override
public boolean containsValue(Object value) {
for(K s:backEnd)
if(fun.apply(s).equals(value))
return true;
return false;
}
@Override
public V remove(Object key) {
backEnd.remove(key);
return null;
}
@Override
public int size() {
return backEnd.size();
}
@Override
public V get(Object key) {
return fun.apply((K)key);
}
@Override
public String toString() {
StringBuilder b=new StringBuilder();
Iterator<K> itr=backEnd.iterator();
b.append("{");
if(itr.hasNext())
{
K key=itr.next();
b.append(key);
b.append(":");
b.append(this.get(key));
while(itr.hasNext())
{
key=itr.next();
b.append(", ");
b.append(key);
b.append(":");
b.append(this.get(key));
}
}
b.append("}");
return b.toString();
}
};
}
}
實現沒有完成,被覆蓋的功能沒有經過測試,但我希望它傳達了這個想法。
更新:
我對seanizer的 答案做了一些小改動,以便地圖中所做的更改也會反映在集合中。
public class SetBackedMap<K, V> extends AbstractMap<K, V> implements SetFunctionMap<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 = 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();
}
}
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 = 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 boolean add(Entry<K, V> e) {
return keys.add(e.getKey());
}
@Override
public Iterator<Map.Entry<K, V>> iterator(){
return new EntryIterator();
}
@Override
public int size(){
return this.keys.size();
}
@Override
public boolean remove(Object o) {
return keys.remove(o);
}
}
private final WeakHashMap<K, V> cache;
private final Set<Entry<K, V>> entries;
private final Function<K, V> funk;
public SetBackedMap(final Set<K> keys, final Function<K, 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;
}
public boolean putKey(K key){
return entries.add(new MapEntry(key));
}
@Override
public boolean removeKey(K key) {
cache.remove(key);
return entries.remove(key);
}
}
接口SetFunctionMap:
public interface SetFunctionMap<K,V> extends Map<K, V>{
public boolean putKey(K key);
public boolean removeKey(K key);
}
測試代碼:
public class SetBackedMapTst {
public static void main(String[] args) {
Set<Integer> set=new TreeSet<Integer>(Arrays.asList(
1, 2, 4, 8, 16));
final SetFunctionMap<Integer, String> map =
new SetBackedMap<Integer, String>(set,
new Function<Integer, String>(){
@Override
public String apply(final Integer from){
return Integer.toBinaryString(from.intValue());
}
});
set.add(222);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
map.putKey(112);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
map.removeKey(112);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
}
}
輸出:
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 222=11011110}//change to set reflected in map
Set: [1, 2, 4, 8, 16, 222]
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 112=1110000, 222=11011110}
Set: [1, 2, 4, 8, 16, 112, 222]//change to map reflected in set
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 222=11011110}
Set: [1, 2, 4, 8, 16, 222]//change to map reflected in set
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.