我想将一个函数应用于 Java 集合,在这种特殊情况下是一个映射。有没有很好的方法来做到这一点?我有一张地图,想对地图中的所有值运行 trim() 并让地图反映更新。
8 回答
使用 Java 8 的 lambda,这是一个单行:
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();
}
});
除了您接受的响应之外,我不知道使用 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>)
方法。
是否可以就地修改集合取决于集合中对象的类。
如果这些对象是不可变的(字符串是),那么您不能只从集合中获取项目并修改它们 - 相反,您需要遍历集合,调用相关函数,然后将结果值放回去。
对于这样的事情可能有点矫枉过正,但是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"));
我强烈推荐 O'Reilly 的Jakarta Commons Cookbook。
我最终使用了@erickson 答案的突变,突变为:
- 返回一个新的
Collection
,而不是就地修改 - return
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 值。由于 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 Collections