在 Java 中使用数组和引用类型别名 [英] Aliasing in Java with Arrays and Reference Types

查看:39
本文介绍了在 Java 中使用数组和引用类型别名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于此,SO 上有许多类似的问题,但我无法找到明确列出别名差异的答案,所以我在这里提问.

There are many similar question on SO about this, but I haven't been able to find an answer that clearly lists the differences in aliasing, so I am asking here.

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

I know that a simple primitive assignment statement copies values:

int x = 1;
int y = x;
x = 2;
StdOut.print(x); // prints 2
StdOut.print(y); // prints 1

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

Then I was told that arrays are 'aliased' during assignment statements. So:

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

但是,如果您将其中一个变量分配给一个完全不同的数组,这种别名消失"了:

However, if you assign one of the variables a completely different array, this aliasing 'disappears':

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

这是什么原因?

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

Then, I come to reference types. When using assignment statements, it is the reference which is copied, not the value. So:

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

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

But what about if I then assign c1 to a new Counter object? What happens to c2; does it reference the original Counter or the new Counter, why?

我问是因为我最初认为引用类型像数组一样工作.如果我创建一个新的 Counter 并将其分配给 c1,那么 c1c2 都指向新创建的计数器.然而,经过一些练习后,我创建了一个可迭代的 Stack ADT,它似乎违反了这个假设.我的代码如下:

I ask because I originally thought that reference types worked like arrays. If I create a new Counter and assign it to c1, then both c1 and c2 point to the newly created Counter. However, after going through some exercises, I created an iterable Stack ADT which seems to violate this assumption. My code is below:

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);
        }
    }

}

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

It's a simple implementation, using a linked list data structure for the items. The main instance variable is first, which holds the first node in the linked list (top of the stack). If you look in the nested ListIterator class, there's an instance variable current which is assigned to first. Now, in the push method, first is reassigned to a newly created Node. Surely then the current variable is still assigned to the old first Node? Why then does this implementation work?

我的预感是 a) 我不明白引用值是如何传递的(请解释)或 b) 当您在 main 方法中运行 for 循环时,它隐式调用了创建一个新的 ListIterator ,在这一点上,将 current 分配给 first 的当前值.如果这是真正的原因,那么这是否意味着每当在类中调用方法时都应该创建一个新的迭代器?例如,如果我显式地创建迭代器,然后将一些项推入堆栈,然后重用迭代器而不重新初始化——它会按预期工作吗?

My hunch is that either a) I don't understand how reference values are passed (please explain) or b) when you run through the for loop in the main method, it implicitly calls creates a new ListIterator which, at that point, assigns current to whatever the current value of first is. If this is the real reason, then does that mean that a new iterator should be created whenever a method is called within the class? For example, if I create the iterator explicitly, then push a few items to the stack, and then reuse the iterator without re-initialising - will it work as intended?

请解释!

推荐答案

这是什么原因?

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

The variables x and y are not arrays. They are array references. An array is an object, and you reassigned x to refer to a different array than y, and only changed x, thus you have different values in the two different arrays.

c2 会发生什么;它是引用原始 Counter 还是新 Counter,为什么?

What happens to c2; does it reference the original Counter or the new Counter, why?

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

In your example there was only one Counter object created, when you called new Counter, there's no original, and you made the Counter reference c2 refer to the same instance that c1 refers to, so they are pointing to the same thing.

当然,那么当前变量仍然分配给旧的第一个节点吗?那么为什么这个实现有效?

Surely then the current variable is still assigned to the old first Node? Why then does this implementation work?

你的 for each 循环调用 iterator() ,它返回一个新的 ListIterator ,它实例化它自己的 current ,它指向 current代码>第一个在你的堆栈中.

Your for each loop invokes iterator() which returns a new ListIterator which instantiates it's own current which points to the latest value of first in your stack.

这篇关于在 Java 中使用数组和引用类型别名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆