【问题标题】:when does a collection when assigned to another will have a different reference in java [duplicate]一个集合何时分配给另一个集合在java中会有不同的引用[重复]
【发布时间】:2014-02-14 16:03:41
【问题描述】:

我有一个 Details 类型的 ArrayList 集合 1。我将它分配给另一个集合 2 以对其执行操作,以便它不会影响集合 1。但我看到集合 1 受到影响,因为引用了对象。 但我想拥有两个不同的集合,具有不同的内存引用 - 请帮助我。

细节类 -

public class Details implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private boolean enabled;

    private int number;

    public Details() {

    }

    public Details(int number, boolean enabled) {
        this.enabled = enabled;
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

}

主类-

public static void main(String[] args) {

    List<Details> detailsList = new ArrayList<Details>();
    detailsList.add(new Details(10, false));
    detailsList.add(new Details(20, false));

    List<Details> newDetailsList = new ArrayList<Details>(detailsList);
    for(Details d : newDetailsList) {
        d.setEnabled(true);
        d.setNumber(50);
    }

    for(Details d : detailsList) {
        System.out.println("---" + d.getEnabled());
        System.out.println("---" + d.getNumber());
    }

}

输出 -

---true
---50
---true
---50

我从detailsList 创建了一个新集合newDetailsList 并对其进行了修改...我看到更改已应用于 detailsList 集合。我怎样才能避免这种情况

【问题讨论】:

    标签: java collections


    【解决方案1】:

    集合持有引用,集合的副本持有相同的引用

    集合只保存对对象的引用。集合的副本只是一个新引用,它包含对相同对象的相同引用。

    除非您对集合中的所有对象进行deep clone,否则这将永远无法工作。

    克隆在 Java 中被破坏

    这不仅仅是implements Clonable 对您要复制的对象的简单处理,对此和almost impossible to get correct, to the point of not even bothering except in very simple cases 有很多警告,尤其是在您没有源的对象时。

    因此,您的Details 对象的所有实例变量也必须被深度复制,它的所有实例成员,以及他们的,以及他们的,以及他们的。你必须避免循环引用。就像我说的,不像听起来那么简单。

    有时这是不可能的,尤其是mutable 对象。但是有一个解决方案。

    惯用的解决方案

    使您的 Details 对象不可变,并从原始实例创建新实例,并在构建新实例时设置所需的新值。

    public class Details 
    {
        private final Boolean enabled;
    
        private final Integer number;
    
        public Details(@Nonnull final Integer number, @Nonnull Boolean enabled) 
        {
            this.enabled = enabled;
            this.number = number;
        }
    
        public int getNumber() { return number;}
    
        public boolean getEnabled() { return enabled; }
    }
    

    一个很好的便利工厂方法是:

    public static Details newDetailsFromOldDetails(@Nonnull final Details details, @Nullable Integer number, @Nullable Boolean enabled)
    {
        final Integer n = number == null ? details.getNumber() : number;
        final Boolean e = flag == null ? details.getBoolean() : enabled;
        return new Details(n, e)
    }
    

    【讨论】:

    • 嗨,Jarrord,我可以通过我提供的示例中的深拷贝(克隆)来实现它。但是对于我在我的应用程序中使用的对象来说,克隆是根本不可能的。非常感谢
    • @Hari 这是我的观点,深度克隆是一团糟,让你的对象immutable 有很多好处,几乎没有缺点。
    【解决方案2】:

    您复制列表,但不复制列表中包含的项目。因此,如果您修改复制集合中的项目,更改仍将显示在原始集合中的项目中。您可能需要deep copy,这将需要创建一种机制,通过该机制可以复制Details 对象,然后将原始列表的副本 项添加到新列表中。相比之下,使用ArrayList 的复制构造函数只是复制引用(即浅拷贝)。

    【讨论】:

      【解决方案3】:

      克隆每个详细信息类型的对象,如下所示 -

      List<Details> newDetailsList = new ArrayList<Details>();
          for(Details t : detailsList) {
              newDetailsList.add(t.clone());
          }
      
      
      public Details clone() {
          return new Details(this.number, this.enabled);
      }
      

      但这对于我在我的应用程序中真正使用的对象是不可能的。

      【讨论】:

      • .clone()is not a public method!这不是解决这个问题的方法。 因此,此方法执行此对象的“浅拷贝”,而不是“深拷贝”操作。 Object 类本身并没有实现接口 Cloneable,因此在类为 Object 的对象上调用 clone 方法会导致在运行时抛出异常。
      猜你喜欢
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多