Créer une Carte à partir d'un Ensemble et d'une Fonction
Ici sont deux classes qui doivent faire le travail. La première montre une vue de la carte de l'ensemble, alors que la seconde permet d'écrire les valeurs de l'ensemble grâce à une interface spéciale.
La Syntaxe D'Appel:
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);
Où mettre ce code?
Remarque: Si la goyave ont été ma bibliothèque, j'avais les rendre accessibles à travers les Cartes de classe:
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);
Immuable version:
J'ai mis en œuvre ce qu'une seule façon de voir:
- Modifications de cette série sont reflétés dans
la carte, mais pas vice-versa (et vous ne pouvez pas changer la carte de toute façon, l'
put(key, value)
méthode n'est pas mis en œuvre).
- L'
entrySet()
itérateur utilise le
ensemble itérateur interne, de sorte qu'il sera
aussi hériter de l'itérateur interne de l'
la manipulation de
ConcurrentModificationException
.
- Les deux
put(k,v)
et
entrySet().iterator().remove()
sera
jeter
UnsupportedOperationException
.
- Les valeurs sont mises en cache dans un
WeakHashMap
,
avec le n ° spécial de la simultanéité de la manipulation, c'est à dire il n'y a pas de synchronisation à
de tout niveau. Cela va faire dans la plupart des cas, mais si votre fonction est cher, vous pouvez ajouter certains de verrouillage.
Code:
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;
}
}
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());
}
Sortie:
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
Mutable Version:
Alors que je pense que c'est une bonne idée de faire de cette façon, voici une version pour Emil, qui fournit les deux sens de la vue (c'est une variante de Emil de la variation de ma solution :-)). Il nécessite une extension de la carte d'interface que j'appellerai ComputingMap
à préciser que c'est une carte où il n'est pas logique de faire appel put(key, value)
.
Interface de carte:
public interface ComputingMap<K, V> extends Map<K, V>{
boolean removeKey(final K key);
boolean addKey(final K key);
}
Carte de mise en œuvre:
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;
}
}
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);
}
Sortie:
{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}