使用数组和引用类型在Java中进行别名

Kei*_*ons 1 java pass-by-reference pass-by-value variable-assignment

关于这一点有很多类似的问题,但是我还没有找到一个明确列出别名差异的答案,所以我在这里问.

我知道一个简单的原始赋值语句复制值:

int x = 1;
int y = x;
x = 2;
StdOut.print(x); // prints 2
StdOut.print(y); // prints 1
Run Code Online (Sandbox Code Playgroud)

然后我被告知在赋值语句中数组是"别名".所以:

int[] x = {1, 2, 3, 4, 5};
int[] y = x;
x[0] = 6;
StdOut.print(x[0]); // prints 6
StdOut.print(y[0]); // prints 6
Run Code Online (Sandbox Code Playgroud)

但是,如果您将其中一个变量指定为完全不同的数组,则此别名会"消失":

int[] x = {1, 2, 3, 4, 5};
int[] y = x;
x = new int[]{1, 2, 3, 4, 5};
x[0] = 6;
StdOut.print(x[0]); // prints 1
StdOut.print(y[0]); // prints 6
Run Code Online (Sandbox Code Playgroud)

这是什么原因?

然后,我来参考类型.使用赋值语句时,它是复制的引用,而不是值.所以:

Counter c1 = new Counter("ones");
Counter c1.incrememnt(); // 0 -> 1
Counter c2 = c1;
c2.increment();
StdOut.print(c1); // prints 2
StdOut.print(c2); // prints 2
Run Code Online (Sandbox Code Playgroud)

但是,如果我再分配c1给一个新Counter对象呢?会发生什么c2; 它是参考原始Counter还是新的Counter,为什么?

我问,因为我原本认为引用类型像数组一样工作.如果我创建一个新的Counter,并把它分配给c1,那么这两个c1c2点到新创建的Counter.然而,经过一些练习后,我创建了一个可迭代的StackADT,这似乎违反了这个假设.我的代码如下:

import java.util.Iterator;

public class MyStack<Item> implements Iterable<Item> {

    private Node first; // top of stack
    private int N; // number of items

    private class Node {
        Item item;
        Node next;
    }

    public MyStack() {}

    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item> {

        private Node current = first;

        public boolean hasNext() {
            return current != null;
        }

        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }

    }

    public void push(Item item) {
        Node oldfirst = first;
        first = new Node();
        first.item = item;
        first.next = oldfirst;
        N++;
    }

    public Item pop() {
        if(!isEmpty()) {
            Node oldfirst = first;
            first = first.next;
            return oldfirst.item;
        } else throw new RuntimeException("Stack underflow");
    }

    public boolean isEmpty() { return size() == 0; }

    public int size() { return N; }

    public static void main(String[] args) {
        MyStack<Integer> stack = new MyStack<>();
        stack.push(5);
        stack.push(6);
        stack.push(7);
        stack.push(8);
        stack.push(9);
        for(int i : stack) {
            StdOut.println(i);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的实现,使用项目的链表数据结构.主实例变量是first,它保存链表中的第一个节点(堆栈顶部).如果查看嵌套ListIterator类,current则会分配一个实例变量first.现在,在该push方法中,first被重新分配给新创建的Node.当然,current变量仍然分配给旧first节点?为什么这个实现工作呢?

我的预感是a)我不明白参考值是如何传递的(请解释)或b)当你for在main方法中运行循环时,它隐式调用创建一个新的ListIterator,在那时,分配current给任何东西目前的价值first是.如果这是真正的原因,那么这是否意味着每当在类中调用方法时都应该创建一个新的迭代器?例如,如果我显式创建迭代器,然后将几个项目推送到堆栈,然后重用迭代器而不重新初始化 - 它是否按预期工作?

请解释!

NES*_*ove 6

这是什么原因?

变量x和y不是数组.它们是数组引用.数组是一个对象,并且您重新分配x以引用与y不同的数组,并且仅更改了x,因此在两个不同的数组中具有不同的值.

c2会发生什么; 它是参考原始计数器还是新计数器,为什么?

在您的示例中,只有一个Counter对象被创建,当您调用时new Counter,没有原始对象,并且您使Counter参考c2引用了c1引用的相同实例,因此它们指向同一个东西.

当然,当前变量仍然分配给旧的第一个节点?为什么这个实现工作呢?

你的每个循环调用iterator()都返回一个新的ListIterator,它实例化它自己的current,它指向first你的堆栈中的最新值.