System.arraycopy(...)的时间复杂度?

Kow*_*ser 31 java algorithm time-complexity

System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 是一种本地方法.

这种方法的时间复杂度是多少?

bra*_*boy 22

它必须遍历数组中的所有元素才能执行此操作.Array是一种独特的数据结构,您必须在初始化时指定大小.顺序是源数组的大小,或者在大O项中是O(长度).

事实上,这发生在ArrayList内部.ArrayList包装一个数组.虽然ArrayList看起来像一个动态增长的集合,但在内部它必须扩展时才进行arrycopy.

  • O(n)足够接近,但实际上我认为它只是O(长度),即要复制的长度,而不是源数组的长度. (5认同)
  • @alex c - 你能展示一个从int到string的转换的例子吗?我不认为arraycopy做任何转换,不是根据文档:`否则,如果满足以下任何条件,则抛出ArrayStoreException ...°src参数引用具有基本组件类型和dest参数的数组指的是一个引用组件类型为°...`的数组 (5认同)
  • 反应不正确!system.arrayCopy是一种本机方法,可以使用单个memcpy/memmove实现 (4认同)
  • 它不必遍历所有数据类型的所有元素。对于许多数据类型,它能够进行块复制,这可以快得多。只要数组类型相同,就不需要做任何类型检查。使用相同的数组作为源和目标会稍微减慢速度(取决于平台,可能很多!),但总的来说 System.arraycopy 通常比迭代更好,很少比迭代更差。 (3认同)
  • @jdramaix:单个memcpy / memmove仍然需要花费与复制量成正比的时间,并且它并不总是单个memcpy / memmove(由于类型检查和转换)。 (3认同)

Kow*_*ser 6

我做了一些调查,后来决定写一个测试代码,这就是我所拥有的。

我的测试代码如下:

import org.junit.Test;

public class ArrayCopyTest {

  @Test
  public void testCopy() {
    for (int count = 0; count < 3; count++) {
      int size = 0x00ffffff;
      long start, end;
      Integer[] integers = new Integer[size];
      Integer[] loopCopy = new Integer[size];
      Integer[] systemCopy = new Integer[size];

      for (int i = 0; i < size; i++) {
        integers[i] = i;
      }

      start = System.currentTimeMillis();
      for (int i = 0; i < size; i++) {
        loopCopy[i] = integers[i];
      }
      end = System.currentTimeMillis();
      System.out.println("for loop: " + (end - start));

      start = System.currentTimeMillis();
      System.arraycopy(integers, 0, systemCopy, 0, size);
      end = System.currentTimeMillis();
      System.out.println("System.arrayCopy: " + (end - start));
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

它产生如下所示的结果

for loop: 47
System.arrayCopy: 24

for loop: 31
System.arrayCopy: 22

for loop: 36
System.arrayCopy: 22
Run Code Online (Sandbox Code Playgroud)

所以,Bragboy 是正确的。


Haw*_*ker 5

以下是 OpenJDK 8 (openjdk-8-src-b132-03_mar_2014) 的一些相关源代码。我在Java 本机方法源代码的帮助下找到了它(注意:那里的说明令人困惑;我只是在源代码中搜索了相关标识符)。我认为福特船长的评论是正确的;即,在(许多)情况下不需要迭代每个元素。请注意,不迭代每个元素并不一定意味着O(1),它只是意味着“更快”。我认为,无论如何,数组副本本质上必须是 O(x),即使 x 不是数组中的项目数;也就是说,无论你怎么做,数组中的元素越多,复制的成本就会越高,并且如果你有一个非常大的数组,则将花费线性的长时间。警告:我不确定是否是您正在运行的 Java 的实际源代码;只是这是我在 OpenJDK 8 源代码中找到的唯一实现。认为这是一个跨平台的实现,但我可能是错的——我肯定还没有弄清楚如何构建这个代码。另请参阅: Oracle JDK 和 Open JDK 之间的差异。以下来自:/openjdk/hotspot/src/share/vm/oops/objArrayKlass.cpp

// Either oop or narrowOop depending on UseCompressedOops.
template <class T> void ObjArrayKlass::do_copy(arrayOop s, T* src,
                               arrayOop d, T* dst, int length, TRAPS) {

  BarrierSet* bs = Universe::heap()->barrier_set();
  // For performance reasons, we assume we are that the write barrier we
  // are using has optimized modes for arrays of references.  At least one
  // of the asserts below will fail if this is not the case.
  assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt");
  assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well.");

  if (s == d) {
    // since source and destination are equal we do not need conversion checks.
    assert(length > 0, "sanity check");
    bs->write_ref_array_pre(dst, length);
    Copy::conjoint_oops_atomic(src, dst, length);
  } else {
    // We have to make sure all elements conform to the destination array
    Klass* bound = ObjArrayKlass::cast(d->klass())->element_klass();
    Klass* stype = ObjArrayKlass::cast(s->klass())->element_klass();
    if (stype == bound || stype->is_subtype_of(bound)) {
      // elements are guaranteed to be subtypes, so no check necessary
      bs->write_ref_array_pre(dst, length);
      Copy::conjoint_oops_atomic(src, dst, length);
    } else {
      // slow case: need individual subtype checks
      // note: don't use obj_at_put below because it includes a redundant store check
      T* from = src;
      T* end = from + length;
      for (T* p = dst; from < end; from++, p++) {
        // XXX this is going to be slow.
        T element = *from;
        // even slower now
        bool element_is_null = oopDesc::is_null(element);
        oop new_val = element_is_null ? oop(NULL)
                                      : oopDesc::decode_heap_oop_not_null(element);
        if (element_is_null ||
            (new_val->klass())->is_subtype_of(bound)) {
          bs->write_ref_field_pre(p, new_val);
          *p = *from;
        } else {
          // We must do a barrier to cover the partial copy.
          const size_t pd = pointer_delta(p, dst, (size_t)heapOopSize);
          // pointer delta is scaled to number of elements (length field in
          // objArrayOop) which we assume is 32 bit.
          assert(pd == (size_t)(int)pd, "length field overflow");
          bs->write_ref_array((HeapWord*)dst, pd);
          THROW(vmSymbols::java_lang_ArrayStoreException());
          return;
        }
      }
    }
  }
  bs->write_ref_array((HeapWord*)dst, length);
}
Run Code Online (Sandbox Code Playgroud)