繁体   English   中英

不可变类中的可变对象字段

[英]Mutable object field within an Immutable class

我被分配在Java中创建一个不变的队列类,在此过程中,我所做的实际上是拥有一个私有的最终arraylist,一个startIndex,一个lastIndex作为字段。 每次执行入队操作时,都是在arraylist顶部添加新值,并返回具有更改的开始和最后一个索引值的类的新实例。 出队操作也以类似的方式执行。 这样,在每个入队/出队操作之后,新实例都有其自己的起始索引,数组列表的最后索引值,而前一个实例保留这些字段的旧值。

我的问题是,我可以称此类为不变的吗? 谢谢。

不,类不是一成不变的。

基本上,线程和Java内存模型是语言规范的一部分,因此,每当您询问类的行为时,都必须考虑语言的那些部分。 由于ArrayList并不是线程安全的,并且您对其进行了更改,因此您不能依靠ArrayList表现出预期的行为,例如在存在多个线程的情况下返回其数组中的第一个或最后一个元素。

Java语言规范说:

尽管前几章中的大多数讨论仅涉及一次执行单个语句或表达式(即,通过单个线程)执行的代码的行为,但是Java虚拟机可以一次支持多个执行线程。 这些线程独立执行对驻留在共享主内存中的值和对象进行操作的代码。

线程由Thread类表示。 用户创建线程的唯一方法是创建此类的对象。 每个线程都与这样的对象相关联。 在相应的Thread对象上调用start()方法时,线程将启动。

线程的行为(尤其是在未正确同步的情况下)可能会造成混淆并且违反直觉。

这是在第17.4.5节中拍摄的金钱

更具体地说,如果两个动作共享事前发生关系,则它们不一定必须按照顺序与没有事前发生关系的任何代码发生过关系。 例如,在一个数据竞争中的一个线程中进行写操作而在另一个线程中进行读操作可能看起来与这些读操作的发生顺序不一致。

由于ArrayList不是线程安全的(关系之前没有发生 ),写入可以任何顺序进行-当内部数组初始化为null,某些先前的写入或最新的写入时,您可能会看到初始值。 一个人无法分辨。

因此,这是反直观的,因为您认为如果有一个ArrayList的最后一个元素的索引,则该ArrayList将返回其最后一个元素的值,但是规范说“否”。

阅读我链接到的规范,并获得Brian Goetz编写的Java Concurrency in Practice的副本。 那本书基本上是所有线程和Java的圣经。

为了实现不变性,队列字段的内部状态决不能由于元素入队和出队而改变。 一种方法是创建队列的全新实例,并每次都支持ArrayList。 与通常的队列实现相比,这将表现得很差,因为入队和出队操作都将花费O(n)时间。

我不确定这有什么用,因为通常假设队列会更改状态-但也许您正在寻找这样的东西:

public class ImmutableQueue<T> {

    private final List<T> elements;

    public ImmutableQueue() {
        elements = new ArrayList<T>();
    }

    private ImmutableQueue(List<T> elements) {
        this.elements = elements;
    }

    public ImmutableQueue<T> enqueue(T element) {
        List<T> copy = new ArrayList<T>(elements);
        copy.add(element);
        return new ImmutableQueue<T>(copy);
    }

    public DequeueResult dequeue() {
        if (elements.size() == 0) {
            throw new NoSuchElementException();
        }
        List<T> copy = new ArrayList<T>(elements);
        T head = copy.remove(0);
        return new DequeueResult(head, new ImmutableQueue<T>(copy));
    }

    public class DequeueResult {

        public final T element;
        public final ImmutableQueue<T> queue;

        public DequeueResult(T element, ImmutableQueue<T> queue) {
            this.element = element;
            this.queue = queue;
        }

    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM