2009-04-13 5 views
24

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

ответ

36

С лямбды 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(); 
    } 
}); 
+1

+1 для шаблон лямбда-функции – Alnitak

+0

Эй эриксон - где я могу узнать об этом синтаксисе: Карта map ?? Я просто изучаю java и не сталкивался с чем-либо подобным ... любое направление было бы оценено! –

+2

@jamesemanon Это означает, что карта, которую вы передаете этому методу, может иметь любой тип ключа, но значение должно быть «String». Вы можете получить основы из [Java Tutorial] (https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html). [Общие сведения о пользователе Angelika Langer] (http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) содержит гораздо более подробную информацию обо всех аспектах Java Generics. – erickson

2

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

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

1

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

2

Возможно, это слишком много, но есть ряд действительно хороших утилит для этих проблем в библиотеке 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'Reilly.

+1

Просто хотел упомянуть неизменяемую версию в библиотеке Apache Commons Collections: CollectionUtils.collect (коллекция inputCollection, трансформатор трансформатора) – Jakub

-1

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

+2

Тип неопределенного ... по крайней мере, предоставляет ссылку и описание/пример того, как использовать Коллекции Google для решать проблему? –

11

Я не знаю способ сделать это с 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 может быть вызвано много раз, если вы манипулируете сайтом d в значительной степени.

Подобный Collections2.transform(Collection<F>,Function<F,T>) способ существует для коллекций.

1

Я пришел с классом "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(); 
2

Я закончил с использованием мутацию ответа @ Эриксона, мутировавший в:

  • вернуть новый Collection, не изменять на месте
  • вернуться Collection с с элементами типа, равными возвращаемым типа отображения вспомогательного 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; 
}