2011-01-04 3 views

risposta

9

Ecco una versione insensibile caso di un ForwardingMap:

public class CaseInsensitiveForwardingMap<V> extends ForwardingMap<String, V> 
    implements Serializable{ 

    private static final long serialVersionUID = -7741335486707072323L; 

    // default constructor 
    public CaseInsensitiveForwardingMap(){ 
     this(new HashMap<String, V>()); 
    } 

    // constructor with a supplied map  
    public CaseInsensitiveForwardingMap(final Map<String, V> inner){ 
     this.inner = inner; 
    } 

    private final Map<String, V> inner; 
    @Override 
    protected Map<String, V> delegate(){ 
     return inner; 
    } 

    // convert keys to lower case Strings, preserve null keys 
    private static String lower(final Object key){ 
     return key == null ? null : key.toString().toLowerCase(); 
    } 

    @Override 
    public V get(final Object key){ return inner.get(lower(key)); } 
    @Override 
    public void putAll(final Map<? extends String, ? extends V> map){ 
     if(map == null || map.isEmpty()){ inner.putAll(map); } 
     else{ 
      for(final Entry<? extends String, ? extends V> entry : 
       map.entrySet()){ 
        inner.put(lower(entry.getKey()), entry.getValue()); 
      } 
     } 
    } 
    @Override 
    public V remove(final Object object){ return inner.remove(lower(object)); } 
    @Override 
    public boolean containsKey(final Object key){ 
     return inner.containsKey(lower(key)); 
    } 
    @Override 
    public V put(final String key, final V value){ 
     return inner.put(lower(key), value); 
    } 
} 

Utilizzando questa mappa, è possibile creare il MultiMap utilizzando i metodi del fornitore in MultiMaps.

Esempio:

Map<String, Collection<String>> map = 
    new CaseInsensitiveForwardingMap<Collection<String>>(); 
Multimap<String, String> caseInsensitiveMultiMap = 
    Multimaps.newMultimap(map, new Supplier<Collection<String>>(){ 

     @Override 
     public Collection<String> get(){ return Sets.newHashSet(); } 

    }); 

Caveat: keySet() torneranno valori minuscole solo, indipendentemente da quanto sono stati inseriti i tasti.

+3

Si noti che è meglio utilizzare String.toUpperCase anziché String.toLowerCase per questo, poiché alcuni caratteri vengono associati a più caratteri pur essendo in maiuscolo, ma non mentre sono in minuscolo. Ad esempio, il carattere '\ u00df' è mappato al maiuscolo' \ u0053 \ u0053', mentre '\ u0053 \ u0053' è mappato al' \ u0073 \ u0073' minuscolo. Ma ovviamente '\ u00df' non è considerato lo stesso di \ u0073 \ u0073' mentre semanticamente lo è. –

+0

@ Frór wow, non lo sapevo! Grazie! –

1

No, ma presumibilmente si stanno utilizzando le chiavi stringa? In tal caso, perché non normalizzare solo l'accesso a un multimap regolare? Per il caso dell'80%, ciò farà mettere tutte le chiamate e ottenere la chiave in minuscolo.

Per una descrizione completa dei problemi con multimaps case-insensitive, vedere this google group discussion

5

Non potresti usare un Map<String,List<Payload>> e dare un Comparator<String>, che ha fatto un case-insensitive confronto?

Sembra che né le raccolte di Google né i framework di Apache Collection dispongano di una multimap che accetta un comparatore per la valutazione dell'eguaglianza delle chiavi.

+3

Utilizzare [MultiMaps.newListMultiMap()] (http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/ google/common/collect/Multimaps.html # newListMultimap% 28java.util.Map% 2C% 20com.google.common.base.Supplier% 29) e fornire una 'TreeMap' con un comparatore personalizzato –

+0

le chiamate standard java equalsIgnoreCase aLowerCase internamente – sds

2

È possibile definire un comparatore di stringhe senza distinzione tra maiuscole e minuscole utilizzando un Collator. Quindi creare un TreeMultimap con le chiavi ordinate da quel comparatore.