ArrayList花园里得小秘密

                                                ArrayList 解析

                                                          ArrayList 从一开始到如今,它的身影如影如行,常伴吾身 。

                                                          面试时也经常碰到询问该数据结构及原理等问题

 比如说:

  1. ArrayList实现原理
  2. ArrayList 为什么线程不安全
  3. ArrayList 如何实现扩容
  4. ArrayList 为什么使用for-each进行删除报错(ConcurrentModificationException)
  5. ArrayList subList是深复制还是浅复制
  6. ArrayList与线程安全的集合(Ventor)有何区别,有几种方式让ArrayList线程安全
  7. ArrayList与LinkedList区别

嘿嘿,潇潇其实用了这么久的java集合框架,真的没有做很深的研究与探讨 ,趁着这段良辰岁月,于是乎绝对好好看一下好朋友的蒙娜丽莎的面纱,哈哈 都怪自己放荡不羁爱自由吧(咳咳咳 其实都怪自己爱玩游戏0.0.)

 

废话说的太多 闪了舌头,上Code

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList :

AbstractList 定义了一些集合的基本行为,比如说增加,修改,删除等

Cloneable 这个类,嘻嘻就不用多说了 ,用于复制

Serializable 用于序列化,至于好处,后面有时间我在深入补充一下 嘎嘎

RandomAccess 这个挺有意思的,随机访问,说白就是根据不同算法选择不同访问数据(也就是get),实现该类 有兴趣的同学可以自己进行百度和google了解,潇潇认为并不是很重要

 

ArrayList 实现原理

数据存储有序(基于数组实现)

访问数据快(基础数组下表获取数据)---> 空间复杂度 O(1)

ArrayList 为什么线程不安全

/** * Appends the specified element to the end of this list. 
* 将指定的元素追加到此列表的末尾 
* 试想下再多线程,并发情况下同时添加数据,有可能造成数据覆盖,List扩容问题
 */
 public boolean add(E e) { 
        ensureCapacityInternal(size + 1); // Increments modCount!! 
        elementData[size++] = e;
        return true;
 }

 

ArrayList怎么实现自动扩容

嘻嘻,具体看上列源码add(E e)

/**
 * 初始容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 存储ArrayList的元素的数组缓冲区。
 * 被transient拒绝序列化
 * 只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
 */
 transient Object[] elementData; // non-private to simplify nested class access
 
 /**
 * Shared empty array instance used for empty instances.
 * 用于ArrayList空实例的共享空数组实例 
 * 包含elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
 * 添加第一个元素时,
  *将扩展为DEFAULT_CAPACITY
 */
private static final Object[] EMPTY_ELEMENTDATA = {};
 
 /**
 * 共享的空数组实例,用于默认大小的空实例。 我们将此与EMPTY_ELEMENTDATA区别开来
 * 以了解添加第一个元素时需要充气多少。
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  //增加mod计数
    elementData[size++] = e; //获取计数后下表,进行添加元素
    return true;
}
/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

private void ensureCapacityInternal(int minCapacity) {
    // 当第一次调用add(E e)方法的时候,判读是不是无参构造函数创建的对象,
    // 如果将DEFAULT_CAPACITY即10作为ArrayList的容量,此时minCapacity = 1
    // 那么minCapacity  =DEFAULT_CAPACITY(10 )
    // 看到上面一段代码就明白了
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;  ///增加mod计数
    // overflow-conscious code 判断是否溢出   增加数必须大于当前数组长度
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//minCapacity 所需最小容量 因为默认再数组最后一条追加   
private void grow(int minCapacity) {
    // overflow-conscious code
    //现在数组的长度
    int oldCapacity = elementData.length;
    //oldCapacity + (oldCapacity >> 1) 默认新得容量扩大 * 2 
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 默认扩容增加俩倍 小于 minCapacity   那么赋值给newCapacity 
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
     //如果扩容长度大于 MAX_ARRAY_SIZE 
     // 默认长度(minCapacity > MAX_ARRAY_SIZE) 
     //?Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //调用JNI System.arraycopy 进行数组复制 扩容,不详细讲解参数 很简单 看一下就明白了
    elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

ArrayList 为什么使用for-each进行删除报错

嘿嘿,曾经那个晚上?哪个时辰? 被我给碰到了,具体得那一年得某年某月某日可能记得不大清楚了,毕竟潇潇忘性大 总有一些风华雪月 再人生中错过 。 咳咳咳

查看ArrayList 源码发现ArrayList中创建了一个内部迭代器Itr,并实现了Iterator接口,而for-each遍历正是基于这个迭代器的hasNext()和next()方法来实现 See See See ~~~~!!!

/**
 * An optimized version of AbstractList.Itr
 * 基于AbstractList 优化后得itr 迭代器
 */
private class Itr implements Iterator<E> {
    //下一个要返回得元素索引
    int cursor;       // index of next element to return
    //返回的最后一个元素的索引;  没有返回 -1
    int lastRet = -1; // index of last element returned; -1 if no such
    //外部的变量modCount计数  也就是ArrayList 里面modCount 变量
    // 期望的修改次数
   int expectedModCount = modCount;  
   public boolean hasNext() {
        return cursor != size;
    }
    //   ............有兴趣得自己打开源码查看
   }
   /**
 // 删除  
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                //fastRemove 跳过边界检查 ,返回返回值
                fastRemove(index);
                return true;
            }
    }
    return false;
}
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
} 
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

/*
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 */
private void fastRemove(int index) {
    modCount++;       
    // ArrayList modCount进行改变
    // 内部迭代器Itr expectedModCount 没有进行更新
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
    //此处进行 size
}
进行往下深入 hasNext() 判断是否还有元素 ,如果前面删除成功,remove()返回肯定为
true(如果删除若之前删除的是倒数第二个元素,此处的cursor就是最后一个索引值size(
)-1,而由于已成功删除一个元素,此处的siz也是原size()-1,两者相等,此处会返回false),
再次调用Next() checkForComodification()会抛出异常
//AbstractList 优化版本  for for each 
private class ListItr extends Itr implements ListIterator<E> {}

ArrayList subList是深复制还是浅复制

根据我得实战和经验 属于浅拷贝 经过线上代码 soci 毒打发现该问题,如果需要subList深复制请重新源码其它方式我并没有深入研究,或者重写该类Clone方法

深复制 拷贝数据 创建新得对象

浅复制 拷贝引用得本身 不会创建新得对象

ArrayList与线程安全的集合(Ventor)有何区别,有几种方式让ArrayList线程安全

这个也太简单了点!!!!

几种方式让ArrayList线程安全 简单列举几个 Collections.syn.....() , syncharizertion

操作加锁等等

一个执行速度快 ,线程不安全

一个线程安全执行速度慢 ,可以看源码 啊 嘎嘎, 因为太多jvm syncharizertion 修饰,syncharizertion 这个java同步关键字也有太多得道道和东西了 有时间整理一波资料好好叨叨!!!

 

ArrayList与LinkedList区别

ArrayList 查询速度快, 基于线性结构 线程不安全 有序

LinkedList 查询速度比ArrayList慢 ,基于双向链表结构 (实现Deque) 删除快等

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

1.7 和1.8得区别不做阐述 ,感觉区别并不是很大0.0.

由于昨日只始开始写博客,写得不对,或者有问题得地方,请大家多多指教 0,0 喵喵!!!!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值