有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!


共 (5) 个答案

  1. # 1 楼答案

    以下是来自OpenJDK 8的一些相关源代码(OpenJDK-8-src-b132-03_mar_2014)。我在Java native method source code的帮助下找到了它(注意:那里的说明很混乱;我只是在源代码中搜索相关的标识符)。我认为Captain Ford's comment是正确的;i、 例如,在(许多)情况下,不需要迭代每个元素。注意,不迭代每个元素并不一定意味着O(1),它只是意味着“更快”。我认为,不管怎样,数组副本基本上必须是O(x),即使x不是数组中的项数;i、 例如,不管你怎么做,数组中的元素越多,复制的成本就越高,如果你有一个非常大的数组,这将花费很长的时间。警告:我不能肯定这是您正在运行的Java的实际源代码;只是这是我在OpenJDK 8源代码中可以找到的唯一的实现。我认为这是一个跨平台的实现,但我可能错了——我肯定还没有弄清楚如何构建这段代码。另见:Differences between Oracle JDK and 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);
    }
    
  2. # 2 楼答案

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

    实际上,这在ArrayList内部发生。ArrayList包装一个数组。虽然ArrayList看起来像是一个动态增长的集合,但在内部,它在需要扩展时会执行arrycopy

  3. # 3 楼答案

    我不明白科瑟的回答如何回答他自己的问题。我认为要检查算法的时间复杂度,必须比较不同大小输入的运行时间,如下所示:

    import org.junit.Test;
    
    public class ArrayCopyTest {
    
      @Test
      public void testCopy() {
        int size = 5000000;
        for (int count = 0; count < 5; count++) {
          size = size * 2;
          long start, end;
          Integer[] integers = new Integer[size];
          Integer[] systemCopy = new Integer[size];
    
          start = System.currentTimeMillis();
          System.arraycopy(integers, 0, systemCopy, 0, size);
          end = System.currentTimeMillis();
          System.out.println(end - start);
        }
      }
    
    }
    

    输出:

    10
    22
    42
    87
    147
    
  4. # 4 楼答案

    总结一下关于另一个问题的相关评论(标记为本问题的副本)

    Surely, it's just adding to the new array with all the entries of the other? Which would be O(n) where n is the number of values to be added.

    bragboy的回答当然同意,但我当时认为获得肯定答案的唯一方法是找到源代码以获得规范答案,但这是不可能的。这是System.arraycopy();的声明

    public static native void arraycopy(Object src, int src_position,  
                                        Object dst, int dst_position,  
                                        int length);
    

    它是native,用操作系统的语言编写,这意味着arraycopy()的实现依赖于平台

    因此,总的来说,它可能是O(n),但可能不是

  5. # 5 楼答案

    我做了一些调查,后来决定写一个测试代码,下面是我的

    我的测试代码如下所示:

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

    它产生如下所示的结果

    for loop: 47
    System.arrayCopy: 24
    
    for loop: 31
    System.arrayCopy: 22
    
    for loop: 36
    System.arrayCopy: 22
    

    所以,Bragboy是正确的