【金三银四】ArrayList图解存储原理与数据结构揭秘

前言:

你好,早上、中午、下午、晚上好。我是辛巴哥。一名无缘985,平常996工程师。 java

在这里插入图片描述
今天我辛巴哥来教娜娜学ArrayList

开始快速

import java.util.ArrayList;
/**** * 求关注 微信搜:Java大型网站架构 */
public class ArrayListDemo {


    public static void main(String[] args) {
       ArrayList<String> arrayList= new ArrayList<String>();
       arrayList.add("辛巴");
       arrayList.add("娜娜");
       arrayList.add("微信搜:Java大型网站架构");
       for (String s:arrayList){
           System.out.println(s);
       }
    }
}
复制代码

在这里插入图片描述

你们在平常开发的中咱们是这样去使用的,只要是java开发人员,不管你是1年工做仍是10年工做。我相信Arraylist都是很是熟悉使用的。对我说的是熟悉使用,可是知道其底层实现的人就少不少了,这发生在我身边的事情,小编我如今在某大厂上班,常常面试一些工做5-6年的程序员,问问他们hashmap和arraylist底层实现,都知道是数组、链表、hash算法,可是细问下就露馅了,因此今天有必要和你们分享下arraylist底层实现。固然hashmap的底层实现我也讲过,能够看我以前的文章。程序员

技术本质

不少老铁都知道arraylist底层是用数组为存储结构的。咱们先来熟悉下数组面试

数组算法

数组:采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);对于通常的插入删除操做, 涉及到数组元素的移动,其平均复杂度也为O(n)数组

在这里插入图片描述

Java代码表示bash

//数组:采用一段连续的存储单元来存储数据。
   //特色:指定下标O(1) 删除插入O(N) 数组:查询快 插入慢 ArrayList
   public static void main(String[] args) {
      Integer[] integers = new Integer[10];
      integers[0]=1;
      integers[1]=2;
      integers[2]=3;
      integers[9]=4;
复制代码

插入原理

咱们熟悉了数组的特性以后,咱们在回到刚才咱们快速入门的代码 ,咱们add三个字符串是怎么插入到咱们数组里面去的了, 是怎么插(吃啊插)入进去的了?咱们来看下源码:微信

​ arrayList.add("辛巴"); ​ arrayList.add("娜娜"); ​ arrayList.add("微信搜:Java大型网站架构"); 架构

在这里插入图片描述

在这里插入图片描述

经过上述代码咱们能够肯定泛型e插入到elementData数组里 而且size++。ensureCapacityInternal方法咱们先放放等会说。网站

咱们先说下说怎么插入到数组中,从0开始按顺序size++插入到数组中分别为:ui

在这里插入图片描述
若是有一天插入的数据不数组长度还多了怎么办?

扩容

刚才在add方法有ensureCapacityInternal方法还没说,这个方法咱们来看下源码。

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
复制代码

源码意思:计算最小容量,DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认容量)是DEFAULTCAPACITY_EMPTY_ELEMENTDATA=10

算出最小容量在看下:

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
复制代码

传入最小容量值,修改+1。 minCapacity - elementData.length > 0判断须要的容量与默认容量的大小,差为负值是不须要扩容的。若是大于0说正数就须要扩容调用grow方法

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);
    }
复制代码

源码意思:数组扩容 第一个if条件是: 首先把数组的长度赋给老的容量,也就是10,而后新的容量newCapacity=老的容量(10)+老的容量右移一位(5), 第二个if条件是: 若是新容量-(size+1)为负数,把size+1赋给新容量。若是新容量比数组的最大扩容量都大,会报异常,或者把最大的赋给它。若是都不是,就把新容量拷贝给数组,扩容完成。 对应图以下:

在这里插入图片描述

删除原理

咱们知道add插入的原理,删除其实也不难了吧。大体意思就是经过下表找到数组对应的对象让其为null,可是咱们以前插入时扩容的数组怎么处理了?咱们如今不须要这么多数据了呀?

下标位置删除:

public E remove(int index) {
        rangeCheck(index);//检查index这个值是否有在size里面,保证其正确合法性
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;/ /将index+1以及以后的元素向前移动一位,覆盖被删除的值
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // 该对应下表值赋值为null。

        return oldValue;
    }
复制代码

在这里插入图片描述
在这里插入图片描述
对象元素删除: 指定元素删除:首先判断是否为空,空的时候直接删除

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(index);      
             return true;       
         }   
 }  
 //若是没有匹配元素,返回false
 return false;
}
复制代码

快速删除:不须要检测index,直接删除

private void fastRemove(int index) {   
modCount++;   
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
}
复制代码

在这里插入图片描述

总结:

如今知道为何数组:插入删除慢,查询快(这个下篇文章证实) 由于插入有可能须要库容。 由于删除须要从新分配数组中数据元素。好比图中删除“娜娜” 娜娜下标为1,凡是在下标为1以后的元素都须要往前挪一个位置, 这是件很可怕的事情。由于删除的元素越靠前,那就意味着须要挪动的数据就越多。所以数组插入和删除会变慢的缘由在于此了明白的牛人点个赞或者回复下吧。

好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是牛人。 白嫖很差,创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见!

相关文章
相关标签/搜索