List实现原理

一、ArrayList实现原理

1.1、ArrayList 父类和实现接口

在这里插入图片描述

1.2、add方法实现

编写java代码

List<String> list = new ArrayList<>();
list.add("java");

点击add(),进入查看
在这里插入图片描述
进入实现类ArrayList的add()
在这里插入图片描述

private int size; //
ensureCapacityInternal(size + 1);  //ensureCapacityInternal(1);

进入ensureCapacityInternal()
在这里插入图片描述

int minCapacity = 1;
transient Object[] elementData;
calculateCapacity(elementData, minCapacity)

进入calculateCapacity()
在这里插入图片描述

minCapacity = 1
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
    
    };
private static final int DEFAULT_CAPACITY = 10;
判断相等
执行Math.max(DEFAULT_CAPACITY, minCapacity);
return Max.max(10,1)

进入ensureExplicitCapacity()
在这里插入图片描述

ensureExplicitCapacity() //ensureExplicitCapacity(10)
int minCapacity = 10

AbstractList抽象类中定义了
protected transient int modCount = 0;

modCount++; //modCount = 1
进入判断
结果为true,执行grow(minCapacity);

grow(minCapacity); // grow(10);

进入grow(minCapacity)
在这里插入图片描述

grow(minCapacity); // grow(10);
int minCapacity = 10
int oldCapacity = 0
int newCapacity = 0

判断结果为true,执行
if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity; //newCapacity = 10
elementData = 10
elementData[size++] = e; //elementData[0] = e; 给数组设置值
size = 1;
return true;

1.3、使用反射测试初始化数组容量

List<String> list = new ArrayList<>();
list.add("java");

Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);

Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());

结果
在这里插入图片描述

1.4、ArrayList 扩容机制

List<String> list = new ArrayList<>();
for (int i = 0; i < 11; i++) {
    
    
    list.add("java");
}

在这里插入图片描述

//i = 11时
size = 10
ensureCapacityInternal(11)

进入ensureCapacityInternal()
在这里插入图片描述

elementData = 10
minCapacity = 11
calculateCapacity(elementData, minCapacity) // calculateCapacity(10, 11)

进入calculateCapacity()
在这里插入图片描述

判断结果为true,执行
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    
    
    return Math.max(DEFAULT_CAPACITY, minCapacity);
}
返回11

进入ensureExplicitCapacity()

ensureExplicitCapacity() //ensureExplicitCapacity(11)
minCapacity = 11;
modCount = 11
判断结果为true,执行
if (minCapacity - elementData.length > 0)
    grow(minCapacity);

进入grow()
在这里插入图片描述

grow()  //grow(11)
minCapacity = 11
oldCapacity = 10
newCapacity = 10 + 10 >> 1  //15
elementData = 15

然后下一步
在这里插入图片描述

elementData[size++] = e; //给赋值elementData[10]
return true;

1.5、使用反射测试扩容容量

List<String> list = new ArrayList<>();
for (int i = 0; i < 11; i++) {
    
    
    list.add("java");
}

Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);

Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());

在这里插入图片描述

1.6、测试数组定义时容量

List<String> list = new ArrayList<>();

Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);

Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());

结果
在这里插入图片描述

1.7、remove

remove

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;
            }
    }
    return false;
}

fastRemove

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
    }

System.arraycopy(elementData, index+1, elementData, index,
numMoved);
System.arraycopy(原数组, 从元数据起始位置开始, 目标数组, 目标数组的起始位置,
要copy数组的长度);

二、LinkedList实现原理

底层基于链表结构
数组与链表结构区别

  • 数组:保证元素有序性,可以基于下标查询,时间复杂度:O(1)
  • 链表:单向链表和双向链表,可以基于下标查询,时间复杂度:O(n)
  • 数组适合于:基于下标查询arrays[1]增删有可能会对我们数组实现移动,效率非常低。
  • 链表适合于:增删,只需要该引用指针关系
    HashMap和LinkedList对比
  • HashMap底层单链表,不能保证key有序性
  • LinkedList底层双向链表,保证key有序性

LInkedList为什么需要用一个变量记录下头结点

  • 后期遍历数据知道从哪里开始

2.1、ArrayList 父类和实现接口

在这里插入图片描述## 2.2、add方法实现

public boolean add(E e) {
    
    
 	linkLast(e);
    return true;
}

进入linkLast(e)方法

void linkLast(E e) {
    
    
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

第一次添加

last保存的是最后一个节点
final Node l = last;
创建一个新节点 ,上个节点指向l
final Node newNode = new Node<>(l, e, null);

如果最后一个节点为null,说明是第一次添加
first = newNode
把他设为第一个节点
transient int size = 0;
size++; size=1
protected transient int modCount = 0;
modCount++; modCount=1
第二次添加
final Node newNode = new Node<>(l, e, null);
l指向的是第一次添加
if判断执行else操作,将上次添加的的节点的尾指针当前节点
size++; size = 2
modCount++; modCount = 2

2.3、get方法实现

public E get(int index) {
    
    
    checkElementIndex(index);
    return node(index).item;
}

进入checkElementIndex(index)方法

if (!isElementIndex(index))
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

判断索引是否越界
进入isElementIndex(index)

private boolean isElementIndex(int index) {
    
    
	 return index >= 0 && index < size;
}

判断索引是否大于或等于0并且小于size
如果为false,抛出索引越界异常
如果为true,进入node(index),这里使用折半算法,时间复杂度O(logN),找到节点,返回节点的item属性

 Node<E> node(int index) {
    
    
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
    
    
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
    
    
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

2.3、remove方法实现

public E remove(int index) {
    
    
    checkElementIndex(index);
    return unlink(node(index));
}

首先进入checkElementIndex(index),判断索引是否越界,越界抛出索引越界异常
然后进入node(index)方法,使用折半算法,查找到节点

Node<E> node(int index) {
    
    
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
    
    
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
    
    
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

再进入unlink()方法

unlink(Node<E> x) {
    
    
   // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

	//头节点指向的节点为null,将first改为尾指针指向的节点,否则指向尾指针指向的节点
	//如果尾指针指向的节点为null,说明当前节点是尾节点,将头指针指向的节点赋值给last,否则将头指针指向的节点和尾指针指向当前指针尾节点指向的节点
	//将item设为null,size减1,modCount加1;
	//返回当前节点
    if (prev == null) {
    
    
        first = next;
    } else {
    
    
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
    
    
        last = prev;
    } else {
    
    
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

十、总结

ArrayList 的内部实现,其实是用一个对象数组进行存放具体的值,然后通过Arrays.copy()扩容,进行数组的动态增长。

  • 定义时容量为0
  • 初始化容量为10
  • 扩容为原来容量的1.5倍,(当前容量 + 当前容量 >> 1)
  • 存放元素有序
  • 线程不安全
  • 基于数组实现 类型Object类型

时间复杂度 o(1)/o(n)/o(log n )

  • o(1) 只需要通过查询一次就能找到元素 get(index) 基于下标查询
  • o(n) 需要从头查询到尾部 例如根据元素值查询 链表
  • o(log n) 二叉树 红黑树

数组:时间复杂度o(1) 基于下标查询
链表:时间复杂度o(n)

ArrayList和Vector的区别

  • 相同点
    都是基于数组实现,默认初始容量为10
  • 不同点
    ArrayList线和不安全,Vector线程是安全的
    扩容ArrayList是1.5倍,Vector是2倍

おすすめ

転載: blog.csdn.net/qq_37242720/article/details/118578322