Domanda

Vorrei applicare una funzione a una raccolta Java, in questo caso particolare una mappa.C'è un bel modo per fare questo?Ho una mappa e vuoi basta eseguire il trim() su tutti i valori della mappa, la mappa riflettere gli aggiornamenti.

È stato utile?

Soluzione

Con Java 8 di lambda, questo è un uno di linea:

map.replaceAll((k, v) -> v.trim());

Per il bene della storia, ecco una versione senza lambda:

public void trimValues(Map<?, String> map) {
  for (Map.Entry<?, String> e : map.entrySet()) {
    String val = e.getValue();
    if (val != null)
      e.setValue(val.trim());
  }
}

O, più in generale:

interface Function<T> {
  T operate(T val);
}

public static <T> void replaceValues(Map<?, T> map, Function<T> f)
{
  for (Map.Entry<?, T> e : map.entrySet())
    e.setValue(f.operate(e.getValue()));
}

Util.replaceValues(myMap, new Function<String>() {
  public String operate(String val)
  {
    return (val == null) ? null : val.trim();
  }
});

Altri suggerimenti

Non so un modo per farlo che con il JDK librerie diversa da quella risposta accettata, tuttavia Google Collezioni ti permette di fare la cosa seguente, con la classi com.google.collect.Maps e com.google.common.base.Function:

Map<?,String> trimmedMap = Maps.transformValues(untrimmedMap, new Function<String, String>() {
  public String apply(String from) {
    if (from != null)
      return from.trim();
    return null;
  }
}

La differenza più grande di questo metodo con quella proposta è che fornisce una vista alla mappa originale, il che significa che, mentre è sempre in sincronia con la vostra mappa originale, il metodo apply potrebbe essere invocato molte volte se siete manipolazione detta mappa pesantemente.

Un metodo Collections2.transform(Collection<F>,Function<F,T>) simile esiste per le collezioni.

Se è possibile modificare la vostra collezione sul posto o no dipende dalla classe degli oggetti della collezione.

Se tali oggetti sono immutabili (quali siano le stringhe), allora non si può semplicemente prendere gli elementi della collezione e modificarli - invece avrete bisogno di iterare la raccolta, chiama la relativa funzione, e poi mettere la risultante valore di ritorno.

potrebbe essere eccessivo per qualcosa di simile, ma ci sono una serie di ottimi programmi di utilità per questi tipi di problemi nel libreria Apache Commons Collezioni .

Map<String, String> map = new HashMap<String, String>(); 
map.put("key1", "a  ");
map.put("key2", " b ");
map.put("key3", "  c");

TransformedMap.decorateTransform(map, 
  TransformerUtils.nopTransformer(), 
  TransformerUtils.invokerTransformer("trim"));

Consiglio vivamente il Jakarta Commons Cookbook da O'Reilly.

Ho finito per usare una mutazione di @erickson risposta, mutato di:

  • il ritorno di un nuovo Collection, non modificare in luogo
  • ritorno Collections con elementi di tipo uguale al valore di ritorno della Function
  • supporta la mappatura sui valori di una mappa o gli elementi di una lista

Codice:

public static interface Function<L, R> {
    L operate(R val);
}

public static <K, L, R> Map<K, L> map(Map<K, R> map, Function<L, R> f) {
    Map<K, L> retMap = new HashMap<K, L>();

    for (Map.Entry<K, R> e : map.entrySet()) retMap.put(e.getKey(), f.operate(e.getValue()));

    return retMap;
}

public static <L, R> List<L> map(List<R> list, Function<L, R> f) {
    List<L> retList = new ArrayList<L>();

    for (R e : list) retList.add(f.operate(e));

    return retList;
}

Dovrete iterare su tutte le voci e tagliare ogni valore Stringa. Dal momento che String è immutabile dovrete ri-mettere nella mappa. Un approccio migliore potrebbe essere quella di tagliare i valori come sono posizionati nella mappa.

Sono venuto su con una classe "Mapper"

public static abstract class Mapper<FromClass, ToClass> {

    private Collection<FromClass> source;

    // Mapping methods
    public abstract ToClass map(FromClass source);

    // Constructors
    public Mapper(Collection<FromClass> source) {
        this.source = source;
    }   
    public Mapper(FromClass ... source) {
        this.source = Arrays.asList(source);
    }   

    // Apply map on every item
    public Collection<ToClass> apply() {
        ArrayList<ToClass> result = new ArrayList<ToClass>();
        for (FromClass item : this.source) {
            result.add(this.map(item));
        }
        return result;
    }
}

che uso così:

Collection<Loader> loaders = new Mapper<File, Loader>(files) {
    @Override public Loader map(File source) {
        return new Loader(source);
    }           
}.apply();

Si potrebbe anche dare un'occhiata a Google Collezioni

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top