2009-07-23 44 views
2

我在基于enum的条件制作对象列表时遇到了一些麻烦。似乎在我完成清单后,清单中的每个项目都等同于最后一个项目。这些Java枚举为什么会更改值?

它是指向同一个对象的不同引用的经典案例,但我不知道如何避免它:

我已经削减下来的东西,就像我可以在保持可读性:

public class Foo { 
    Digit[] array = new Digit[2]; 
    ArrayList<Foo> foozlets; 

    Foo() { 
    array[0] = Digit.ZERO; 
    foozlets = new ArrayList<Foo>(); 
    } 

    Foo(Foo old, Digit num) { 
    this.array = old.array; \\This line is a problem, what should it say? 
    array[1] = num; 
    } 

    public static void main(String[] args) { 
    Foo f = new Foo(); 
    System.out.println("Initial Foo:"); 
    System.out.println(f); 
    f.listFoozlets(); 
    } 

    void listFoozlets() { 
    for (Digit k : Digit.values()) { 
     if (k == Digit.TWO || k == Digit.FIVE) { 
     foozlets.add(new Foo(this, k)); 
     System.out.println("** Foozlet being added **"); 
     Foo foo = new Foo(this, k); 
     System.out.println(foo); 
     } 
    } 
    System.out.println("** List of Foozlets **"); 
    for (Foo foo : foozlets) { 
     System.out.println(foo); 
    } 
    } 

    public String toString() { 
    return array[0].toString() + " " + array[1].toString(); 
    } 
} 

enum Digit { ZERO, ONE, TWO, THREE, FOUR, FIVE } 

这里是输出:

Initial Foo: 
ZERO NULL 
** Foozlet being added ** 
ZERO TWO 
** Foozlet being added ** 
ZERO FIVE 
** List of Foozlets ** 
ZERO FIVE 
ZERO FIVE 

如果有人能解释为什么Foo上的列表中变化的第一个实例,以及如何我可以做一个不变的名单,我会很感激。

编辑:好吧,我看到现在的问题。在一个真正的,更大的程序中,我有一个更大的数组,当我为列表创建一个新的Foo时,我想保留旧信息。我已经更改了代码,以反映我还想保留其他信息。我该如何做到这一点?

+1

关于你的编辑,你需要得到更具体的关于你的情况,作为明显的答案是克隆数组(正如Jon和我的答案所暗示的那样)。如果数组非常大,那么旧数据和新数据之间的关系是什么?你可能需要完全不同的数据结构才能完成这个工作,但由于我们没有深入了解所存储的数据以及新旧Foos的关系,所以我不能给出任何好的建议。 – Yishai 2009-07-23 13:53:48

回答

6

该位是罪魁祸首:

Foo(Foo old, Digit num) { 
    this.array = old.array; 
    array[0] = num; 
} 

你复制一个参考老富的数组,然后改变该数组中值。

为什么你甚至有一个大小为1的数组而不仅仅是一个Digit?如果你真的想要这个数组,你可能想克隆它,而不是仅仅复制引用,但是我们不能真正知道它的意图是什么。

这里有一个更短的例子:

enum Digit { ZERO, ONE, TWO, THREE, FOUR, FIVE } 

public class Foo { 
    Digit[] array = new Digit[1]; 

    Foo() { 
    array[0] = Digit.ZERO; 
    } 

    Foo(Foo old, Digit num) { 
    this.array = old.array; 
    array[0] = num; 
    } 

    public String toString() { 
    return array[0].toString(); 
    } 

    public static void main(String[] args) { 
    Foo f = new Foo(); 
    System.out.println(f); 
    Foo other = new Foo(f, Digit.ONE); 
    System.out.println(f); 
    } 
} 
+0

更短但同样的错误,仍然使用相同的阵列 – 2009-07-23 13:46:12

+0

非常感谢,克隆似乎在我这里工作。 – 2009-07-23 13:51:09

+0

@Carlos:这是一个重点 - 展示一个显示同样问题的简短例子。 – 2009-07-23 13:55:02

2

在你的第二个构造函数:

Foo(Foo old, Digit num) { 
    this.array = old.array; 
    array[0] = num; 
} 

您要重新使用列表从old。您想创建该列表的副本而不是使用相同的列表。你可以通过改变赋值为:

Foo(Foo old, Digit num) { 
    this.array = new ArrayList<Foo>(old.array); 
    array[0] = num; 
} 
1

在这种情况下的对象引用是数组。 foo的构造函数是您所遇到的问题,特别是:

Foo(Foo old, Digit num) { 
    this.array = old.array; // reference the old array 
    array[0] = num; // set the first element of the array (for this AND the old array) to num 
} 

您需要将阵列复制到新的数组中foo的构造函数。

2

的问题似乎是在这一行:

this.array = old.array; 

您共享数组引用,所以每次富共享相同的阵列,所以他们都在阵列相同的值[0]。

要解决这个问题,请尝试:

this.array = old.array.clone(); 
+0

这是正确的溶剂,现在你克隆数组的内容并且可以覆盖其中一个元素 – Salandur 2009-07-23 14:45:37

0

而不是

Foo(Foo old, Digit num) { 
    this.array = old.array; 
    array[0] = num; 

}

根本就

Foo(Foo old, Digit num) { 
    array[0] = num; 

}

如果你真的需要阵列... (最好是使用一个简单的数字,如另一个答复中所建议的)

+0

这适用于我的示例,但我需要一些来自旧数组的信息。我已经改变了这个例子来反映这一点。 – 2009-07-23 14:05:26