Java:通过引用传递/ ListIterator.add()

时间:2009-10-15 16:29:55

标签: java data-structures variables iterator pass-by-reference

Java不通过引用传递变量。在这种情况下,ListIterator等数据结构如何更改其对应的列表?

这是我写的一个示例迭代器:

public class OdpIterator<E> implements ListIterator<E> {

    private OdpList<E> list;
    private int cursor;

    public OdpIterator(OdpList<E> list) {
        this.list = list;
    }

    @Override
    public void add(E arg0) {
        list.add(arg0);
    }

但是当我尝试将list更改为add()时,它不会更改基础列表,因此以下测试失败:

OdpList<Integer> list = new OdpList<Integer>();
ListIterator<Integer> iter = list.listIterator();
iter.add(42);
assertTrue(list.contains(42));

OdpList add:我认为它是正确的,因为它通过了它的单元测试。

@Override
public boolean add(E arg0) {
    ListCell<E> cell = new ListCell<E>(arg0);

    if (size() > 0) { //if something is already in the list
        tail.setNext(cell);
        tail = cell;
    }
    else {
        head = cell;
        tail = cell;
    }
    return true;
}

ListCell构造函数:

public class ListCell<T> {
    public ListCell(T arg0) {
        this.datum = arg0;
        next = null;
    }
}

OdpList listIterator:

@Override
public ListIterator<E> listIterator() {
    return new OdpIterator<E>(this);
}

OdpList包含:

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) == -1;
}

@Override
public int indexOf(Object arg0) {
    return findAfter(head, arg0, 0);
}

private int findAfter(ListCell<E> o, Object search, int soFar) {
    if (o == null) {
        return -1;
    }
    if (o.getDatum() == null && search != null) {
        return findAfter(o.getNext(), search, soFar + 1);           
    }
    if ((o.getDatum() == null && search == null) || o.getDatum().equals(search)) {
        return soFar;
    }

    return findAfter(o.getNext(), search, soFar + 1);
}

我该怎么做?或者我误解了迭代器是如何工作的?

7 个答案:

答案 0 :(得分:6)

在人们经历过的所有心理练习之后,我几乎不愿意这样说,但是...问题只是一个错字。

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) == -1;
}

应该是

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) != -1;
}
仅当对象在列表中时,

contains才会返回true

答案 1 :(得分:2)

它的工作原理是Java传递引用类型。它们通过价值传递,这是许多人混淆的根源。在我看来,当人们开始说Java通过引用传递对象和按值传递基元时,这种混乱会得到加强。如果您使用的语言同时支持两者,那就更是如此。

所以下面我稍微介绍了一下,并描述了我能做到的最佳方式。


Java按值传递引用类型。 Java Ranch有两篇很好的文章描述了这个:

  1. Cup Size -- a story about variables
  2. Pass-by-Value Please (Cup Size continued)
  3. 我还使用ASCII艺术发布了这个here。让我们再来一次。

    我们有方法:

    void test(StringBuilder fred) {
        fred.append("Fred");
    }
    

    以下代码:

    StringBuilder b = new StringBuilder();
    test(b);
    

    StringBuilder b = new StringBuilder();
    

    在记忆中,这给出了类似的东西:

    b -- > ""
    

    test(b);
    

    然后创建一个新变量“b”,但此变量指向相同的字符串缓冲区。

    在记忆中,这给出了类似的东西:

    b -- +
         +-> ""
    fred-+
    

    fred.append("Fred");
    

    虽然“fred”和“b”是不同的变量,但指向同一件事。所以改变“fred”也会改变“b”。

    在记忆中,这给出了类似的东西:

    b -- +
         +-> "Fred"
    fred-+
    

     }
    

    现在“fred”退出了范围并被吃掉了。

    b -- > "Fred"
    

    这与“通过引用传递”的不同之处在于PBR b和fred变为一。在上面的示例中,它几乎没有区别,除了以下任何地方:

    b -- +
         +-> "Fred"
    fred-+
    

    在PBR中它看起来像:     b,fred - &gt; “佛瑞德”

    当您尝试更改“fred”指向的位置时,PBR确实显示出来。如果我们将方法改为:

    void test(StringBuilder fred) {
        fred = new StringBuilder("Fred");
    }
    

    我们可以看到差异。


    StringBuilder b = new StringBuilder();
    

    在记忆中,这给出了类似的东西:

    b -- > ""
    

    test(b);
    

    对于按值传递引用类型,我们得到类似的内容:

    b -- +
         +-> ""
    fred-+
    

    但对于PBR我们得到:

    b, fred--> ""
    

        fred = new StringBuilder("Fred");
    

    现在我们将看到差异。在Pass Reference By Value(Java支持)中,我们得到:

    b --> ""
    
    fred--> "Fred"
    

    看看我们现在如何打破它们之间的联系。然而,在PBR中我们保留链接。

               "" // Old string builder now float in memory all lost and alone.
    b, fred--> "Fred"
    

答案 2 :(得分:0)

Java通过引用传递所有对象。它按值传递基元。

您的代码应更改基础列表。我会检查你的OdpList类是否存在问题。

答案 3 :(得分:0)

缺少部分代码 - OdpList的定义 - 所以很难说这里发生了什么。显示的代码看起来正确。在列表实现中,我希望看到类似的东西:

public ListIterator<T> listIterator() {
  return new OdpIterator<T>(this);
}

Java不传递引用,但它确实按值传递引用。因此,只要其引用可用,修改列表应该没有问题。

答案 4 :(得分:0)

为什么list.listIterator()会返回OdpIterator(找不到OdpList的代码)?谁说“Java不通过引用传递变量”?所有实例参数都按值传递,但对对象的引用是复制为按值传递的指针。推荐此副本将始终更改原始对象!

答案 5 :(得分:0)

我认为这里的问题不在于迭代器本身,而在于底层泛型和相关类型擦除。在此之上抛出自动装箱,你就会有痛苦的配方。看看你的测试代码:

OdpList<Integer> list = new OdpList<Integer>();
ListIterator<Integer> iter = list.listIterator();
iter.add(42);
assertTrue(list.contains(42));

然后查看this pagehere,了解实际情况。通用类型由编译器使用,然后由运行时环境忽略。

您正在实例化一个类型为Integer的列表,但在运行时,JVM无法确切地说明列表迭代器中的内容。这就像你的显式实例化从未发生过,所以你坚持使用通用对象。这意味着42的自动魔术自动装箱永远不会发生,因为这种行为与Integer类相关联。实际上,您的JVM可能将“list.contains(42)”中的42视为对Object类型对象的引用,这解释了测试失败的原因。

反直观的?是。马虎?是的。沮丧?极大。像这样的例子就是为什么这么多人说用Java打破泛型。

答案 6 :(得分:0)

首先,您没有按值传递原始int类型。您正在通过引用传递就地创建的autoboxed Integer个对象。远离自动装箱有很多好理由。

如果没有查看完整的OdpList列表,很难说清楚,但在审核OdpList.add时,内部看起来有ListCells列表,而不是Integers列表}。所以从某种意义上说,你正在寻找一篮子苹果中的橙子。