Java 集合&ArrayList源码分析

1-使用集合的好处
	数组:长度固定、一旦指定,不能修改
    	  保存同类型的元素
          增加、删除元素比较麻烦
          int[] ints = new int[1];
          ints[0] = 1;
          //数组扩容
          int[] ints1 = new int[ints.length + 1];
          for (int i = 0 ; i < ints.length; i ++) {
             ints1[i] = ints[i];
          }
     使用集合可以保存任意多的不同类型的元素

集合体系图

虚线代表接口,实现代表继承关系

img

img

Iterator

迭代器,遍历Collection中的元素
iterator本身不存放元素
# 迭代器的使用
List l = new ArrayList<>();//向上转型
l.add("1");
Iterator iterator = l.iterator();
while(iterator.hasNext()){
      String s = (String) iterator.next();
      System.out.println(s);
 }
# 调用next()之前必须先调用hashNext(),否则会抛出NoSuchElementException()
    List l = new ArrayList<>();
    l.add("1");
    l.add("2");

    Iterator iterator = l.iterator();
    while(true){
        String s = (String) iterator.next();
        System.out.println(s);
    }
Exception in thread "main" java.util.NoSuchElementException

增强for循环可以替代Iterator

List

1-实现了List的子接口和List类存入元素和取出元素顺序一致,是有序的,元素可以重复

List类的三种遍历方式

        List l = new ArrayList<>();
        l.add("1");
        l.add("2");

        // 第一种遍历方式:Iterator迭代器
        Iterator iterator = l.iterator();
        while(true){
            String s = (String) iterator.next();
            System.out.println(s);
        }
        
        //第二种遍历方式:普通for循环
        for (int i = 0; i < l.size(); i++){
            l.get(i);
        }

        //第三种方式:增强for
        for (Object str : l) {
            System.out.println(str);
        }

ArrayList

# ArrayList无参构造方法,初始化容量是10 
# ArrayList可以存储null,并且可以存储多个null
List l = new ArrayList<>();
l.add("1");
l.add("2");
l.add(null);
l.add(null);

Iterator iterator = l.iterator();
while (iterator.hasNext()){
      System.out.println(iterator.next() == null);
}

#ArrayList基本等同于Vector,是线程不安全的,效率高,多线程下不建议使用ArrayList,是因为底层方法
 没有Synchornized修饰
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
 

ArrayList底层源码分析(JDK 11)

基本属性

//transient修饰,表明该属性不会被序列化.elementData是一个存储元素的数组
transient Object[] elementData;
//在添加新的元素之前,elementData数组中元素的个数
private int size;
 

add()方法-在数组尾部添加元素

	public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    /**
     * 先判断当前集合中元素的个数是否等于集合的容量
     * 如果相等,调用扩容方法
     * 如果不相等,直接在尾部添加该元素
     * 元素个数加1之后,方法调用结束
     */
    private void add(E e, Object[] elementData, int s) {
        
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
	//扩容方法
	private Object[] grow() {
        return grow(size + 1);
    }
    
    //当前元素个数+1
    private Object[] grow(int minCapacity) {
        //数组扩容
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }


    /**
     * 扩容方法-计算新数组的容量
     * minCapacity = 当前元素个数 + 1
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        // 当前容量
        int oldCapacity = elementData.length;
        // 新容量 = 当前容量 + (当前容量/2^1) = 当前容量的*1.5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 新容量 - (当前元素个数 + 1) <= 0
        if (newCapacity - minCapacity <= 0) {
            // 如果是第一次添加元素,返回10作为数组容量
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            // 如果当前元素个数 + 1 < 0 ,报内存溢出
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        // 从第二次添加元素开始,新数组容量 = 10 * 1.5、10*1.5*1.5....(当前容量的1.5倍)
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
                ? newCapacity
                : hugeCapacity(minCapacity);
    }

remove(index i)

        /**
         * 根据下标删除一个元素,返回该元素
         */
        public E remove(int index) {
            // 先判断index是否大于size或者index<0,抛出异常
            Objects.checkIndex(index, size);
            final Object[] es = elementData;

            @SuppressWarnings("unchecked")
            // 要被删除的元素赋值给oldValue
            E oldValue = (E) es[index];
            //调用删除方法
            fastRemove(es, index);

            //返回要被删除的元素
            return oldValue;
        }

        /**
         * 删除方法
         */
        private void fastRemove(Object[] es, int i) {
            modCount++;
            final int newSize;
            //判断元素个数-1是否大于要删除的元素下标
            if ((newSize = size - 1) > i)
                //数组拷贝
                System.arraycopy(es, i + 1, es, i, newSize - i);
            //将被删除的元素赋值为null
            es[size = newSize] = null;
        }

remove(e)

        /**
         * 删除ArrayList中的某个元素
         */
        public boolean remove(Object o) {
            final Object[] es = elementData;
            final int size = this.size;
            int i = 0;
            found: {
                // 如果要删除的元素为null,循环该数组,判断哪个元素等于null
                if (o == null) {
                    for (; i < size; i++)
                        if (es[i] == null)
                            break found;
                } else {
                    //如果要删除的元素不为null,循环遍历数组,判断哪个元素等于要删除的元素
                    for (; i < size; i++)
                        if (o.equals(es[i]))
                            break found;
                }
                //如果都没有,返回false
                return false;
            }
            //调用根据下标删除元素的方法
            fastRemove(es, i);
            return true;
        }
ArrayList底层是数组,在删除和添加元素时,都需要对数组进行扩容或者缩容,在这个过程中
由于数组中元素是连续的,内存空间必须一次性分配够,需要重新分配一块更大的空间,再把数据全部复制过去,
时间复杂度是O(N)ArrayList查询快,增删慢
# 增强for循环中,删除元素会抛异常
ArrayList<String> l = new ArrayList<String>();
l.add("1");
l.add("2");
l.add("3");
for(String str : l){
    if(str.equals("1")){
        l.remove(str);
    }
}

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
   
//原因
增强for循环底层是迭代器实现的,List在调用Iterator方法时,维护了一个int expectedModCount = modCount;
modCount表示集合被修改的次数,expectedModCount只在add时赋值,因此在删除后,expectedModCount != modCount,
此时会报错ConcurrentModificationException。

遍历修改集合时应该使用:
while (iterator.hasNext()){
       String a = "1";
       if(iterator.next().equals(a)){
       		//使用迭代器的remove方法
            iterator.remove();
       }
}
    
        /**
         * 多线程对ArrayList读写操作,会抛异常ConcurrentModificationException
         */
        List<String> a = new ArrayList<String>();
        a.add("a");
        a.add("b");
        a.add("c");
        final ArrayList<String> lists = new ArrayList<String>(
                a);
        Thread t = new Thread(new Runnable() {
            int count = -1;

            @Override
            public void run() {
                while (true) {
                    lists.add(count++ + "");
                }
            }
        });
        t.setDaemon(true);
        t.start();
        Thread.currentThread().sleep(300);
        for (String s : lists) {
            System.out.println(s);
        }
1-ArrayList不是线程安全的
2-使用Collections.synchornizedList(new ArrayList<>())或者
   Vector或者CopyOnWriteArrayList
        /**
         * 1-调用构造方法,会创建一个数组    private transient volatile Object[] array;
         */
        List<String> list = new CopyOnWriteArrayList<String>();
        /**
         * 2-调用CopyOnWriteArrayList的add方法,会将元素添加进
         *     public boolean add(E e) {
         *         synchronized (lock) {
         *             Object[] es = getArray();
         *             int len = es.length;
         *             es = Arrays.copyOf(es, len + 1);
         *             es[len] = e;
         *             setArray(es);
         *             return true;
         *         }
         *     }
         */
        list.add("1");
         Object[] es = getArray();
         *             int len = es.length;
         *             es = Arrays.copyOf(es, len + 1);
         *             es[len] = e;
         *             setArray(es);
         *             return true;
         *         }
         *     }
         */
        list.add("1");
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值