欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java ArrayList.add 的實(shí)現(xiàn)方法

 更新時(shí)間:2018年11月06日 09:51:46   作者:EthanSun  
這篇文章主要介紹了Java ArrayList.add 的實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

ArrayList是平時(shí)相當(dāng)常用的List實(shí)現(xiàn), 其中boolean add(E e) 的實(shí)現(xiàn)比較直接:

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
  ensureCapacityInternal(size + 1); // Increments modCount!!
  elementData[size++] = e;
  return true;
}

有時(shí)候也使用 void add(int index, E element) 把元素插入到指定的index上. 在JDK中的實(shí)現(xiàn)是:

/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 *
 * @param index index at which the specified element is to be inserted
 * @param element element to be inserted
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
  rangeCheckForAdd(index);

  ensureCapacityInternal(size + 1); // Increments modCount!!
  System.arraycopy(elementData, index, elementData, index + 1,
           size - index);
  elementData[index] = element;
  size++;
}

略有差別, 需要保證當(dāng)前elementData 數(shù)組容量夠用, 然后把從index處一直到尾部的數(shù)組元素都向后挪一位. 最后把要插入的元素賦給數(shù)組的index處.

一直以來(lái), 我都認(rèn)為 System.arraycopy 這個(gè)native方法, 它的c++實(shí)現(xiàn)是調(diào)用底層的memcpy, 直接方便, 效率也沒(méi)問(wèn)題.

但今天看了openJDK的源碼發(fā)現(xiàn)并非如此.

以openJDK8u60 為例, 在objArrayKlass.cpp 中:

void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
                int dst_pos, int length, TRAPS) {
 assert(s->is_objArray(), "must be obj array");

 if (!d->is_objArray()) {
  THROW(vmSymbols::java_lang_ArrayStoreException());
 }

 // Check is all offsets and lengths are non negative
 if (src_pos < 0 || dst_pos < 0 || length < 0) {
  THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
 }
 // Check if the ranges are valid
 if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
   || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
  THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
 }

 // Special case. Boundary cases must be checked first
 // This allows the following call: copy_array(s, s.length(), d.length(), 0).
 // This is correct, since the position is supposed to be an 'in between point', i.e., s.length(),
 // points to the right of the last element.
 if (length==0) {
  return;
 }
 if (UseCompressedOops) {
  narrowOop* const src = objArrayOop(s)->obj_at_addr<narrowOop>(src_pos);
  narrowOop* const dst = objArrayOop(d)->obj_at_addr<narrowOop>(dst_pos);
  do_copy<narrowOop>(s, src, d, dst, length, CHECK);
 } else {
  oop* const src = objArrayOop(s)->obj_at_addr<oop>(src_pos);
  oop* const dst = objArrayOop(d)->obj_at_addr<oop>(dst_pos);
  do_copy<oop> (s, src, d, dst, length, CHECK);
 }
}

可以看到copy_array在做了各種檢查之后, 最終copy的部分在do_copy方法中, 而這個(gè)方法實(shí)現(xiàn)如下:

// 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 = element;
    } 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);
}

可以看到, 在設(shè)定了heap barrier之后, 元素是在for循環(huán)中被一個(gè)個(gè)挪動(dòng)的. 做的工作比我想象的要多.

如果有m個(gè)元素, 按照給定位置, 使用ArrayList.add(int,E)逐個(gè)插入到一個(gè)長(zhǎng)度為n的ArrayList中, 復(fù)雜度應(yīng)當(dāng)是O(m*n), 或者O(m*(m+n)), 所以, 如果m和n都不小的話, 效率確實(shí)是不高的.

效率高一些的方法是, 建立m+n長(zhǎng)度的數(shù)組或ArrayList, 在給定位置賦值該m個(gè)要插入的元素, 其他位置依次賦值原n長(zhǎng)度List的元素. 這樣時(shí)間復(fù)雜度應(yīng)當(dāng)是O(m+n).

還有, 在前面的實(shí)現(xiàn)中, 我們可以看到有對(duì)ensureCapacityInternal(int) 的調(diào)用. 這個(gè)保證數(shù)組容量的實(shí)現(xiàn)主要在:

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);
}

大家知道由于效率原因, ArrayList容量增長(zhǎng)不是正好按照要求的容量minCapacity來(lái)設(shè)計(jì)的, 新容量計(jì)算的主要邏輯是: 如果要求容量比當(dāng)前容量的1.5倍大, 就按照要求容量重新分配空間; 否則按當(dāng)前容量1.5倍增加. 當(dāng)然不能超出Integer.MAX_VALUE了. oldCapacity + (oldCapacity >> 1) 實(shí)際就是當(dāng)前容量1.5倍, 等同于(int) (oldCapacity * 1.5), 但因這段不涉及浮點(diǎn)運(yùn)算只是移位, 顯然效率高不少.

所以如果ArrayList一個(gè)一個(gè)add元素的話, 容量是在不夠的時(shí)候1.5倍增長(zhǎng)的. 關(guān)于1.5這個(gè)數(shù)字, 或許是覺(jué)得2倍增長(zhǎng)太快了吧. 也或許有實(shí)驗(yàn)數(shù)據(jù)的驗(yàn)證支撐.

關(guān)于這段代碼中出現(xiàn)的Arrays.copyOf這個(gè)方法, 實(shí)現(xiàn)的是重新分配一段數(shù)組, 把elementData賦值給新分配的空間, 如果新分配的空間大, 則后面賦值null, 如果分配空間比當(dāng)前數(shù)組小則截?cái)? 底層還是調(diào)用的System.arraycopy.

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java實(shí)現(xiàn)俄羅斯方塊游戲簡(jiǎn)單版

    Java實(shí)現(xiàn)俄羅斯方塊游戲簡(jiǎn)單版

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)俄羅斯方塊游戲簡(jiǎn)單版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • SpringBoot3使用Jasypt加密數(shù)據(jù)庫(kù)用戶名、密碼等敏感信息

    SpringBoot3使用Jasypt加密數(shù)據(jù)庫(kù)用戶名、密碼等敏感信息

    使用Jasypt(Java Simplified Encryption)進(jìn)行數(shù)據(jù)加密和解密主要涉及幾個(gè)步驟,包括引入依賴、配置加密密碼、加密敏感信息、將加密信息存儲(chǔ)到配置文件中,以下是詳細(xì)的使用說(shuō)明,需要的朋友可以參考下
    2024-07-07
  • idea使用外置tomcat配置springboot詳細(xì)步驟

    idea使用外置tomcat配置springboot詳細(xì)步驟

    昨天小編遇到一個(gè)問(wèn)題使用springboot自帶的tomcat啟動(dòng)沒(méi)有任何問(wèn)題,不知道idea使用外置tomcat配置springboot該如何設(shè)置的,今天小編給大家分享一篇教程幫助大家解決這個(gè)問(wèn)題
    2021-07-07
  • 利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能

    利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能

    這篇文章主要介紹了利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能,需要的朋友可以參考下
    2017-12-12
  • Java基礎(chǔ)之垃圾回收機(jī)制詳解

    Java基礎(chǔ)之垃圾回收機(jī)制詳解

    這篇文章主要介紹了Java基礎(chǔ)之垃圾回收機(jī)制詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 詳解springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑

    詳解springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑

    本文主要介紹了springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • JavaAgent的簡(jiǎn)單例子

    JavaAgent的簡(jiǎn)單例子

    這篇文章主要介紹了JavaAgent的簡(jiǎn)單例子,對(duì)JavaAgent感興趣的同學(xué),可以參考下
    2021-04-04
  • Java中的注解和反射實(shí)例詳解

    Java中的注解和反射實(shí)例詳解

    這篇文章主要給大家介紹了關(guān)于Java中注解和反射的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • SpringBoot框架底層原理解析

    SpringBoot框架底層原理解析

    這篇文章主要介紹了SpringBoot底層原理,包括配置優(yōu)先級(jí)的配置方式給大家講解的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • 完美解決Eclipse 項(xiàng)目有紅感嘆號(hào)的問(wèn)題

    完美解決Eclipse 項(xiàng)目有紅感嘆號(hào)的問(wèn)題

    下面小編就為大家?guī)?lái)一篇完美解決Eclipse 項(xiàng)目有紅感嘆號(hào)的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01

最新評(píng)論