Эквивалент метода применения коллекции/карты Java?

Я хотел бы применить функцию к коллекции Java, в данном случае к карте. Есть ли хороший способ сделать это? У меня есть карта, и я хотел бы просто запустить функцию trim() для всех значений на карте, чтобы карта отражала обновления.


person joshjdevl    schedule 13.04.2009    source источник


Ответы (8)


С лямбда-выражениями Java 8 это один вкладыш:

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

Для истории вот версия без лямбд:

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

Или, в более общем смысле:

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();
  }
});
person erickson    schedule 13.04.2009
comment
Эй, эриксон, где я могу узнать об этом синтаксисе: Map‹?, String› map ?? Я только изучаю java и не сталкивался с чем-то подобным ... любое направление будет оценено! - person james emanon; 07.12.2014
comment
@jamesemanon Это означает, что карта, которую вы передаете этому методу, может иметь ключ любого типа, но значение должно быть String. Основы можно узнать из учебника по Java. Ответы Анжелики Лангер на общие вопросы содержат гораздо более подробную информацию обо всех аспектах Java Generics. - person erickson; 07.12.2014
comment
Спасибо, очень ценю это! - person james emanon; 08.12.2014

Я не знаю, как это сделать с библиотеками JDK, кроме вашего принятого ответа, однако Google Collections позволяет вам делать следующее с классами com.google.collect.Maps и 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;
  }
}

Самое большое отличие этого метода от предлагаемого заключается в том, что он обеспечивает представление вашей исходной карты, а это означает, что, хотя он всегда синхронизирован с вашей исходной картой, метод apply может вызываться много раз, если вы манипулируете указанной картой. сильно.

Аналогичный метод Collections2.transform(Collection<F>,Function<F,T>) существует для коллекций.

person Jean Hominal    schedule 15.09.2009

Можете ли вы изменить свою коллекцию на месте или нет, зависит от класса объектов в коллекции.

Если эти объекты являются неизменяемыми (какими являются строки), то вы не можете просто взять элементы из коллекции и изменить их — вместо этого вам нужно будет перебрать коллекцию, вызвать соответствующую функцию, а затем вернуть полученное значение обратно.

person Alnitak    schedule 13.04.2009

Это может быть излишним для чего-то подобного, но в Apache есть ряд действительно хороших утилит для подобных проблем. Библиотека Commons Collections.

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"));

Я настоятельно рекомендую Jakarta Commons Cookbook от O' Рейли.

person Gene Gotimer    schedule 13.04.2009
comment
Просто хотел упомянуть неизменяемую версию в библиотеке коллекций Apache Commons: CollectionUtils.collect(Collection inputCollection, Transformer transform) - person Jakub; 19.11.2013

В итоге я использовал мутацию ответа @erickson, измененную на:

  • вернуть новый Collection, не изменять на месте
  • вернуть Collections с элементами типа, равными возвращаемому типу Function
  • поддержка сопоставления значений карты или элементов списка

Код:

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;
}
person nmr    schedule 17.03.2012

Вам придется перебрать все записи и обрезать каждое строковое значение. Поскольку String неизменяем, вам придется повторно поместить его на карту. Лучшим подходом может быть обрезка значений по мере их размещения на карте.

person Steve Kuo    schedule 13.04.2009

Я придумал класс "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;
    }
}

Что я использую так:

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

Вы также можете взглянуть на коллекции Google.

person Fortyrunner    schedule 13.04.2009
comment
Немного расплывчато... хотя бы дайте ссылку и описание/пример того, как использовать Google Collections для решения проблемы? - person ; 26.10.2011