【问题标题】:How to orchestrate mutable object as immutable across objects如何将可变对象编排为跨对象不可变
【发布时间】:2018-10-26 21:01:06
【问题描述】:

我有一个类似这样的 java 类

class Runner {
  public Object run(Map<String, Object> input);
  public String name();
}

public class Test {

  public static void main(String args[]) {  
    Map<String, Object> map = Maps.newHashMap();
    List<Runner> runners;
    forEach(Runner runner: runners) {
      Object obj = runner.run(map);
      map.put(runner.name(), obj);
    }
  }

}

在上面的代码中,我调用了 Runner 类的 run 方法并将它产生的输出添加到 Map 对象中。对跑步者对象列表重复此操作。如何使作为输入传递给 run 方法的 map 对象不可变?我考虑过从地图创建一个不可变地图并将其作为输入参数传递。但我担心我可能创建的不可变映射的数量取决于执行的运行器对象的数量,这可能导致 OOM 错误。有没有关于如何去做的模式或解决方案?任何建议将不胜感激。

【问题讨论】:

标签: java immutability immutable-collections


【解决方案1】:

您可以通过可修改的地图来支持不可修改的地图。

它们具有相同的底层数据,但对 setter API 的访问在不可修改的包装器中受到限制。

以下代码 sn-p 将在更新可修改映射时打印1,即使您正在从不可修改映射中读取。您可能会注意到,它不会创建地图数据的两个副本,它们只是共享相同的值。

Map<String, String> modifiableMap = new HashMap<>();
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
modifiableMap.put("a", "1");
System.out.println(unmodifiableMap.get("a"));

但是,尝试unmodifiableMap.put("a", "1") 将导致UnsupportedOperationException 如预期的那样。


对于您的代码,您可以尝试以下方法:

public static void main(String args[]) {
    Map<String, Object> modifiableMap = Maps.newHashMap();
    Map<String, Object> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
    List<Runner> runners;
    forEach(Runner runner: runners) {
        Object obj = runner.run(unmodifiableMap);
        modifiableMap.put(runner.name(), obj);
    }
}

【讨论】:

    【解决方案2】:

    如果您只是通过 run 方法中的键获取映射值,那么您可以使用 Function 接口包装您的 Map

    class Runner {
      public Object run(Function<String, Object> getter) {}
      public String name() {}
    }
    
    public static void main(String args[]) {  
      Map<String, Object> map = new HashMap<>();
      List<Runner> runners;
      for(Runner runner: runners) {
        Object obj = runner.run(map::get);
        map.put(runner.name(), obj);
      }
    }
    

    【讨论】:

    • 很好,但我认为您仍然会在每次调用时创建一个新对象。你可以在循环之前创建一次,也许 javac 或 jit 足够聪明来做到这一点。
    • 我怀疑 lambda 表达式会被缓存,但至少这样可以避免创建包含重复条目的辅助映射以使其不可修改。
    猜你喜欢
    • 2021-03-20
    • 1970-01-01
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多