69

请帮助我使用以下代码,即使更改值后我也会得到相同的输出

import java.util.*;

class Test {
    public static void main(String[] args) {
        ArrayList<Integer> a = new ArrayList<Integer>();

       // added 0-9 to ArrayList        
          for(int i=0;i<9;i++)
            a.add(new Integer(i));

        // initialize the Iterator
        Iterator<Integer> i = a.iterator();

        // changed the value of first element in List
        if(i.hasNext()) {
            Integer x = i.next();
            x = Integer.valueOf(9);
        }

        // initialized the iterator again and print all the elements
        i = a.iterator();
        while(i.hasNext())
            System.out.print(i.next());
    }
}    

//Output : 012345678

值 9 未更新。

4

7 回答 7

91

该列表正在维护对存储在列表中的原始值的对象引用。因此,当您执行此行时:

Integer x = i.next();

两者x和列表都存储对同一对象的引用。但是,当您执行时:

x = Integer.valueOf(9);

列表中没有任何变化,但x现在存储了对不同对象的引用。名单没有改变。您需要使用一些列表操作方法,例如

list.set(index, Integer.valueof(9))

注意:正如其他人所暗示的那样,这与 的不变性无关。Integer这只是基本的 Java 对象引用行为。


这是一个完整的示例,以帮助解释这一点。请注意,这使用了ListIterator支持在迭代中删除/设置项目的类:

import java.util.*;

public class ListExample {

  public static void main(String[] args) {

    List<Foo> fooList = new ArrayList<Foo>();
    for (int i = 0; i < 9; i++)
      fooList.add(new Foo(i, i));

    // Standard iterator sufficient for altering elements
    Iterator<Foo> iterator = fooList.iterator();

    if (iterator.hasNext()) {
      Foo foo = iterator.next();
      foo.x = 99;
      foo.y = 42;
    }

    printList(fooList);    

    // List iterator needed for replacing elements
    ListIterator<Foo> listIterator = fooList.listIterator();

    if (listIterator.hasNext()) {
      // Need to call next, before set.
      listIterator.next();
      // Replace item returned from next()
      listIterator.set(new Foo(99,99));
    }

    printList(fooList);
  }

  private static void printList(List<?> list) {
    Iterator<?> iterator = list.iterator();
    while (iterator.hasNext()) {
      System.out.print(iterator.next());
    }
    System.out.println();
  }

  private static class Foo {
    int x;
    int y;

    Foo(int x, int y) {
      this.x = x;
      this.y = y;
    }

    @Override
    public String toString() {
      return String.format("[%d, %d]", x, y);
    }
  }
}

这将打印:

[99, 42][1, 1][2, 2][3, 3][4, 4][5, 5][6, 6][7, 7][8, 8]
[99, 99][1, 1][2, 2][3, 3][4, 4][5, 5][6, 6][7, 7][8, 8]
于 2012-10-07T20:14:03.717 回答
16

使用 set 方法将旧值替换为新值。

list.set( 2, "New" );
于 2016-10-15T20:43:13.317 回答
12

你说你正在改变第一个元素的值;

x = Integer.valueOf(9);

您正在更改x为指向一个全新的 Integer,但不再使用它。您不会以任何方式更改集合。

由于您正在使用 ArrayList,因此如果您想要一个允许您更改元素的迭代器,则可以使用 ListIterator,这是需要更改的代码片段;

//初始化迭代器
ListIterator <Integer> i = a. 列表迭代器()

//改变List中第一个元素的值
if(i.hasNext()) {
    i.next();
    i.set(Integer.valueOf(9));     // 改变迭代器当前所在的元素
}

// 新建迭代器,并打印所有元素
Iterator iter = a.iterator();
while(iter.hasNext())
    System.out.print(iter.next());

>> 912345678

遗憾的是,不能将其扩展到其他集合,如 Set<T>。实现细节(例如,作为哈希表实现的 HashSet 并更改对象可能会更改哈希值并因此更改迭代顺序)使 Set<T> 成为“仅添加新/仅删除”类型的数据结构,并更改内容在迭代它时根本不安全。

于 2012-10-07T20:11:51.270 回答
1

我认为问题在于您认为该声明...

x = Integer.valueOf(9);

... 导致 '9' 的值被“存储”到(!) x 所引用的对象中。

但那是错误的。

相反,该语句会导致类似的事情,就好像你会打电话一样

x = new Integer(9); 

如果您查看 java 源代码,您将详细了解发生了什么。

以下是“Integer”类中“valueOf(int i)”方法的代码:

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

此外,每当第一次使用 IntegerCache 类时,都会调用以下脚本:

static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }

您会看到在 valueOf 方法中使用“new Integer(i)”创建了一个新的 Integer Object ... ... 或者返回了对存储在 IntegerCache 中的 Integer Object 的引用。

在这两种情况下,x 都将引用一个新对象。

这就是为什么当您调用...时列表中对 Object 的引用会丢失的原因。

x = Integer.valueOf(9);

而不是这样做,结合 ListIterator 使用......

i.set(Integer.valueOf(9));

...在您获得要更改的元素之后...

i.next();
于 2014-09-12T17:29:35.070 回答
1

我同意 Duncan ...我已经尝试使用可变对象但仍然遇到相同的问题...我有一个简单的解决方案...使用 ListIterator 而不是 Iterator 并使用 ListIterator 的 set 方法

ListIterator<Integer> i = a.listIterator();
//changed the value of first element in List
Integer x =null;
        if(i.hasNext()) {
            x = i.next();
            x = Integer.valueOf(9);
        }
    //set method sets the recent iterated element in ArrayList
    i.set(x);
        //initialized the iterator again and print all the elements
        i = a.listIterator();
        while(i.hasNext())
        System.out.print(i.next());

但这限制了我只能将它用于可以使用 ListIterator 的 ArrayList ......我对任何其他 Collection 都会有同样的问题

于 2012-10-07T20:57:44.310 回答
1

您正在尝试更改列表中的值,但您所做的只是更改 x 的引用。执行以下操作只会更改 x,而不是集合中的任何内容:

x = Integer.valueOf(9);

此外,Integer它是不可变的,这意味着您无法更改 Integer 对象内的值(无论如何这都需要不同的方法)。这意味着您需要替换整个对象。没有办法用一个Iterator(不添加你自己的拳击层)来做到这一点。请改为执行以下操作:

a.set(0, 9);
于 2012-10-07T20:12:30.723 回答
0

将其更改为for(int i=0;i<=9;i++)

于 2014-12-03T04:40:24.173 回答