【问题标题】:Java collection/map apply method equivalent?Java集合/映射应用方法等效?
【发布时间】:2010-10-19 04:03:46
【问题描述】:

我想将一个函数应用于 Java 集合,在本例中是一个映射。有没有很好的方法来做到这一点?我有一张地图,想对地图中的所有值运行 trim() 并让地图反映更新。

【问题讨论】:

    标签: java collections apply


    【解决方案1】:

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

    【讨论】:

    • Hey erickson - 我在哪里可以了解这种语法:Map, String> map ??我只是在学习 Java,还没有遇到过这样的事情.. 任何方向都将不胜感激!
    • @jamesemanon 表示你传递给这个方法的map可以有任意类型的key,但是value必须是String。您可以从Java Tutorial 获得基础知识。 Angelika Langer's Generic FAQs 有更多关于 Java 泛型各个方面的详细信息。
    • 谢谢,非常感谢!
    【解决方案2】:

    除了您接受的响应之外,我不知道使用 JDK 库执行此操作的方法,但是 Google Collections 允许您使用 com.google.collect.Mapscom.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&lt;F&gt;,Function&lt;F,T&gt;) 方法。

    【讨论】:

      【解决方案3】:

      是否可以就地修改集合取决于集合中对象的类。

      如果这些对象是不可变的(字符串是),那么您不能只从集合中获取项目并修改它们 - 相反,您需要遍历集合,调用相关函数,然后将结果价值回来。

      【讨论】:

        【解决方案4】:

        对于这样的事情可能有点矫枉过正,但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

        【讨论】:

        • 只想提一下Apache Commons Collections库中的不可变版本:CollectionUtils.collect(Collection inputCollection, Transformer transformer)
        【解决方案5】:

        我最终使用了@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;
        }
        

        【讨论】:

          【解决方案6】:

          您必须遍历所有条目并修剪每个字符串值。由于 String 是不可变的,因此您必须将其重新放入地图中。更好的方法可能是在将值放置在地图中时对其进行修剪。

          【讨论】:

            【解决方案7】:

            我想出了一个“映射器”类

            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 收藏集

              【讨论】:

              • 有点含糊……至少提供一个链接和说明/示例,说明如何使用 Google 收藏集解决问题?
              猜你喜欢
              • 2013-03-31
              • 2014-11-22
              • 2014-01-10
              • 2018-03-30
              • 1970-01-01
              • 2016-12-09
              • 2020-11-17
              • 2012-06-10
              • 1970-01-01
              相关资源
              最近更新 更多