Подтвердить что ты не робот

Java collection/map применить метод эквивалент?

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

4b9b3361

Ответ 1

С Java 8 lambdas это один лайнер:

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

Для истории, здесь версия без lambdas:

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();
  }
});

Ответ 2

Я не знаю, как это сделать с библиотеками 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>) существует для коллекций.

Ответ 3

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

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

Ответ 4

Возможно, это слишком сложно, но есть ряд действительно хороших утилит для этих типов проблем в Коллекции Apache Commons библиотека.

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

Я очень рекомендую Джакартскую коллективную книгу от O'Reilly.

Ответ 5

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

  • вернуть новый Collection, не изменять на месте
  • return 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;
}

Ответ 6

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

Ответ 7

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

Ответ 8

Вы также можете посмотреть коллекцию Google