如何将一个ArrayList的内容移动到另一个?

时间:2012-04-05 03:52:18

标签: java collections arraylist

有没有办法将ArrayList的全部内容移动到O(1)中的另一个ArrayList实例?

即:只有对支持数组的引用从一个实例传递给另一个实例(元素不会逐个复制)。

例如:

ArrayList<String> a = new ArrayList<>(Arrays.asList("A", "B", "C"));
ArrayList<String> b = new ArrayList<>();
a.moveContentsTo(b);
// 'a' is now empty, while 'b' contains everything that 'a' did before and 'a != b'
// It is desired that the 'moveContentsTo' method is O(1)

更好的是,是否有ArrayList#swapContents(ArrayList)方法?


进一步说明和用例

进一步说明 1:不得交换“a”和“b”的引用。我不是在寻找tmp = a; a = b; b = tmp;类型的解决方案。

进一步说明 2:操作必须是〜O(1)及时。

用例:当一个对象想要封装外部构造的列表时,这很有用:

public class A {
    private ArrayList<String> items = new ArrayList<>();

    /**
     * This method takes the sole ownership of the contents. Whoever
     * passed the list from the outside will not be able to modify
     * contents of 'this.items' from outside the class.
     */ 
    public AnImmutableObject(ArrayList<String> items) {
        if (items != null) {
            items.moveContentsTo(this.items);
        }
    }

    /**
     * Other collections that do not provide the 'move' functionality
     * must be copied. If we just stored the reference to 'items' we
     * would break encapsulation as whoever called the constructor
     * still have write capabilities to the collection.
     */ 
    public A(Collection<String> items) {
        if (items != null) {
            this.items.addAll(items);
        }
    }

    public List<String> getItems() {
        return Collections.unmodifiableList(items);
    }
}

请注意,我们希望避免复制(以提高速度并减少内存使用量)。关键的一点是被调用者必须失去修改(现在封装的)ArrayList的能力。

3 个答案:

答案 0 :(得分:2)

这应该这样做:

ArrayList<String> a = new ArrayList<>(Arrays.asList("A", "B", "C"));
ArrayList<String> b = a;
a = new ArrayList<>();

从概念上讲,a现在为空,b包含之前包含的a。只有一个任务,没有数据复制,这是你能做到的最快速度。这是否满足您的要求,或者您是否真的希望a仍然引用相同的数组,除非给定的数组现在应为空?

更新

我不相信在C ++中移动操作的时间复杂度也是O(1)。同样谨慎地指出“因为Java中的类使用引用语义,所以从来没有任何隐含的对象在这些语言中。问题移动语义解决不存在,也从未存在于Java中。“(参见FredOverflow的回答:C++ Rvalue references and move semantics

  

有没有办法将ArrayList的全部内容移动到另一个ArrayList,以便只有对后备数组的引用从一个传递到另一个(即,不会逐个复制元素)。

鉴于上述语句,如果您在Java中将某些内容从数组a复制到数组b,则两个数组都将引用相同的数据。你在C ++中使用移动语义所做的就是保存需要创建的临时对象以便进行这样的复制:

X foo();
X x;
// perhaps use x in various ways
x = foo();

最后一个做:

destructs the resource held by x,
clones the resource from the temporary returned by foo,
destructs the temporary and thereby releases its resource. 

移动语义:

swap resource pointers (handles) between x and the temporary,
temporary's destructor destruct x's original resource.

你保存了一个destruct,但只在C ++中... Java中不存在上述问题!有关移动语义的更多详细信息,请参阅此文章:http://thbecker.net/articles/rvalue_references/section_02.html

答案 1 :(得分:2)

@Lirik答案更高+1。但是,如果您正在寻找真实的ArrayList#swapContents(ArrayList),请按以下步骤操作:

public static void swapContents(ArrayList<String> listA, ArrayList<String> listB)
{
    List<String> temp = new ArrayList<String>(listA);
    listA.clear();
    listA.addAll(listB);
    listB.clear();
    listB.addAll(temp);
}

答案 2 :(得分:2)

AFAIK,跟踪引用的“所有权”(至少在程序员方面)并不像Java那样,这就是为什么我怀疑你会发现你想要的std::move()类功能。它在Java中并不常见。 我猜C ++需要明确跟踪对象所有权,因为没有垃圾收集。

我认为最好的办法是在构造函数中创建一个防御性副本,并依靠不可变对象的副本构造函数来节省空间:

public class AnImmutableObject {

    private final List<String> items;

    /**
     * Creates a new immutable object with the given list of items.
     * Always deep copy from an outside source, because we can't know if the
     * calling code will modify that collection later.
     */ 
    public AnImmutableObject(Collection<String> items) {
        // It's a constructor. We know items needs to be set.
        // We are creating a new array list, using its constructor which deep
        // copies the collection, and immediately wrapping it to make it truly
        // immutable. We are guaranteed that no one will hold a reference to the
        // mutable view of the new ArrayList.
        this.items = Collections.unmodifiableList(new ArrayList<String>(items));
    }

    /**
     * Creates a new immutable object with the same items as another.
     * Copying the reference here is completely safe because we
     * enforce the immutability of the items array.
     */ 
    public AnImmutableObject(AnImmutableObject source) {
        items = source.items;
    }

    public List<String> getItems() {
        return items;
    }
}

此时,你可以在你自己的不可变对象之间“在O(1)中传递数组(真正地分享它们):

ImmutableObject a = new ImmutableObject(Arrays.asList("A", "B", "C")); // O(n)
ImmutableObject b = new ImmutableObject(a); // O(1)

希望这样的事情能够满足你的目的。

您可以使用的另一条路线是使用GuavaImmutableList。由于这些是不可变的,因此您可以安全地将引用复制到构造函数中的ImmutableList

主要方法是让您安全地复制对列表的引用,而不是对它们拥有所有权。