Эквивалент метода коллекции/карты Java?
-
09-09-2019 - |
Вопрос
Я хотел бы применить функцию к коллекции Java, в данном случае к карте.Есть ли хороший способ сделать это?У меня есть карта, и я хотел бы просто запустить обрезку() для всех значений на карте, чтобы карта отражала обновления.
Решение
С лямбда-выражениями 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();
}
});
Другие советы
Я не знаю, как сделать это с библиотеками JDK, кроме вашего принятого ответа, однако Коллекции Google позволяют вам делать следующее с классами 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>)
метод существует для коллекций.
Возможность изменения коллекции на месте или нет зависит от класса объектов в коллекции.
Если эти объекты являются неизменяемыми (каковыми являются строки), вы не можете просто взять элементы из коллекции и изменить их — вместо этого вам нужно будет перебрать коллекцию, вызвать соответствующую функцию, а затем вернуть полученное значение обратно.
Возможно, это излишне, но для решения подобных задач есть ряд действительно хороших утилит. Коллекции 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"));
Я очень рекомендую Поваренная книга Джакарты Коммонс от О'Рейли.
В итоге я использовал мутацию ответа @erickson, мутировавшую на:
- вернуть новый
Collection
, не изменять на месте - возвращаться
Collection
s с элементами типа, равным возвращаемому типу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;
}
Вам придется перебрать все записи и обрезать каждое строковое значение.Поскольку String является неизменяемой, вам придется повторно поместить ее на карту.Лучшим подходом может быть обрезка значений по мере их размещения на карте.
Я придумал класс «Картограф»
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();
Вы также можете взглянуть на Коллекции Google.