集合--ArrayList、Vector、LinkedList

3.4 ArrayList底层结构和源码分析

ArrayList 注意事项:

  1. permits all elements ,including null,ArrayList可以存放 null ,并且多个
  2. ArrayList 是由数组来实现数据存储
  3. ArrayList 基本等同于 Vector ,除了ArrayList是线程不安全(执行效率高),看源码,多线程情况下不建议使用 ArrayList

ArrayList的扩容机制

  1. ArrayList中维护了一个Object类型的数组 elementData
  • transient Object[] elementData;

    //transient 表示瞬间、短暂的,表示该属性不会被序列化

  1. 当创建对象时,如果使用的是无参构造器 (ArrayList()) ,则初始 elementData 容量为0 。

第1次添加,则扩容 elementData 为10;如果需要再次扩容的话,则扩容 elementData 为1.5

  1. 如果使用的是指定大小的构造器 (ArrayList( int )) ,则初始 elementData 容量为指定大小。如果需要扩容,则直接扩容 elementData 为1.5倍。

ArrayList的底层源码分析

注意:Idea 默认情况下, Debug 显示的数据是简化后的,如果希望看到完整的数据,需要进行设置

集合 4.3.5

1.使用无参构造

//使用无参构造器创建ArrayList对象
ArrayList list = new ArrayList();

//使用for循环给list集合添加 1-10 数据
for(int i = 1; i<= 10; i++){
    list.add(i);
}
//使用for循环给list集合添加 11-15 数据
for(int i = 11; i<= 15; i++){
    list.add(i);
}

list.add(100);
list.add(200);
list.add(null);

![集合 4.3.1](D:\桌面\博客\picture\集合 4.3.1.png)

![集合 4.3.2](D:\桌面\博客\picture\集合 4.3.2.png)

![集合 4.3.3](D:\桌面\博客\picture\集合 4.3.3.png)

![集合 4.3.4](D:\桌面\博客\picture\集合 4.3.4.png)

然后一层层往上返回到 add ,再往下返回到 grow()

如果 ensureExplicitCapacity(int minCapacity) 中,if语句不满足,则直接返回

2.使用有参构造

//使用有参构造器创建ArrayList对象
ArrayList list = new ArrayList(8);

//使用for循环给list集合添加 1-10 数据
for(int i = 1; i<= 10; i++){
    list.add(i);
}
//使用for循环给list集合添加 11-15 数据
for(int i = 11; i<= 15; i++){
    list.add(i);
}

list.add(100);
list.add(200);
list.add(null);

![集合 4.3.5](D:\桌面\博客\picture\集合 4.3.5.png)

后续和无参构造的流程相同,只是扩容机制开始就按1.5倍

3.5 Vector底层结构和源码分析

Vector 基本介绍:

  1. Vector类的定义说明

    public class Vector<E>
        extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, Serializable
    
  2. Vector底层也是一个对象数组,protected Object[] elementData;

  3. Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized

    public synchronized E get(int index) {
    	if (index >= elementCount)
    		throw new ArraylndexOutOfBoundsException(index);
    	return elementData(index);
    }
    
  4. 在开发中,需要线程同步安全时,考虑使用Vector

Vector 和 ArrayList 的比较:

底层结构 版本 线程安全(同步)效率 扩容倍数
ArrayList 可变数组 jdk1.2 不安全,效率高 如果有参构造:1.5倍;如果是无参:第一次10,第二次开始1.5倍
Vector 可变数组Object[] jdk1.0 安全,效率不高 如果是无参,默认10,满后按2倍扩容;如果指定大小,则每次直接2倍扩

Vector源码

//无参构造器

Vector vector = new Vector;
for(int i = 1; i<= 10; i++){
    vector.add(i);
}
vector.add(100);
/*
1.new Vector() 底层
    public Vector(){
   	  this(10);
    }
2.vector.add(i)
2.1 //下面这个方法就是添加数据到 Vector 集合
    public synchronized boolean add(E e){
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
2.2 //确定是否需要扩容   条件: minCapacity - elementData.length>0
    private void ensureCapacityHelper(int minCapacity){
        //overflow-conscious code
        if(minCapacity - elementData.length>0){
            grow(minCapacity);
        } 
    }
2.3 //如果需要的数组大小不够用,就扩容,扩容的算法  
	//newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
	//就是扩容两倍
    private void grow(int minCapacity){
        //overflow-conscious code
        int oLdCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
        if(newCapacity - minCapacity<0)
        	newCapacity = minCapacity;
        if(newCapacity - MAX_ARRAY_SIZE > 0)
        	newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
     }
*/

3.6 LinkedList底层结构和源码分析

LinkedList 全面说明:

  1. LinkedList底层实现了双向链表和双端队列特点
  2. 可以添加任何元素(元素可以重复),包括null;
  3. 线程不安全,没有实现同步

LinkedList 底层操作机制:

  1. LinkedList底层维护了一个双向链表.

  2. LinkedList中维护了两个属性first和last,分别指向首节点和尾节点

  3. 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过 prev 指向前一个,通过 next 指向后一个节点。最终实现双向链表。

  4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

  5. 模拟一个简单的双向链表

    集合 3.6.1
    public class LinkedList{
        public static void main(String[] args){
            //模拟一个简单的双向链表
            
            Node jack = new Node("jack");
            Node tom = new Node("tom");
            Node hsp = new Node("hsp");
            
            //连接三个结点,形成双向链表
            //jack -> tom -> hsp
            jack.next = tom;
            tom.next = hsp;
            //hsp -> tom -> jack
            hsp.pre = tom;
            tom.pre = jack;
            
            Node first = jack;//让first引用指向jack,就是双向链表的头结点
            Node last = hsp;//让last引用指向hsp,就是双向链表的尾结点
            
            
            //演示,从头到尾进行遍历
            while(true){
                if(first == null){
                    break;
                }
                //输出 first 信息
                System.out.println(first);
                first = first.next;
            }
            
            //演示,从尾到头进行遍历
            while(true){
                if(last == null){
                    break;
                }
                //输出 last 信息
                System.out.println(last);
                last = last.pre;
            }
            
            
            //演示链表的添加对象/数据,是多么方便
            //要求,在 tom 和 hsp 之间添加,插入一个对象 smith
            
            //1.先创建一个 Node 结点, name就是 smith
            Node smith = new Node("smith");
            //下面就把 smith 加入到双向链表
            smith.next = hsp;
            smith.pre = tom;
            hsp.pre = smith;
            tom.next = smith;
        }
    }
    
    //定义一个 Node 类,Node 对象表示一个双向链表的一个结点
    class Node{
        public Object item; //真正存放数据
        public Node next;//指向后一个结点
        public Node pre;//指向前一个结点
        public Node(Object name){
            this.item = name;
        }
        public String toString(){
            return "Node name=" + item;
        }
    }
    

LinkedList 增删改查案例:

LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);

/*  添加:
	1. LinkedList linkedList = new LinkedList();
		public LinkedList() {}
	2.这时 LinkeList 的属性 first = null    last = null
	3.执行 添加
		pubLic boolean add(E e){
            linkLast(e);
            return true;
  		}
  	4.将新的结点,加入到双向链表的最后
        void linkLast(E e){
            final Node<E> l= last;
            final Node<E> nerode = new Node<>(l, e, null);
            last = newNode;
            if (l == nuLL)
            	first = newNode;
            else
            	l.next = newNode;
            size++;
            modCount++;
    	}
*/



//演示一个删除节点
linkedList.remove();//这里默认删除的是第一个结点

/*  删除:linkedList.remove();//这里默认删除的是第一个结点
	1. 执行 removeFirst
		public E remove(){
			return removeFirst();
		}
    2.执行
        public E removeFirst(){
            final Node<E> f = first;
            if (f == nuLL)
                throw new NoSuchELementException();
            return unlinkFirst(f);
        }
    3.执行 unlinkFirst ,将 f 指向的双向链表的第一个结点拿掉
        private E unlinkFirst(Node<E> f){
            //assert f == first && f != null;
            final E element= f.item;
            final Node<E> next = f.next;
            f.item = null;
            f.next = null; // help GC
            first = next;
            if (next == nuLl)
            	last= null;
            else
            	next.prev = null;
            size--;
            modCount++;
            return element;
         }
*/



//修改某个结点对象
LinkedList.set(1, 999);
System.out.printLn("linkedList=" + linkedList);



//得到某个结点对象
//get(1)是得到双向链表的第二个对象
Object o = linkedList.get(1);
System.out.println(o);//999



//因为LinkedList是 实现了List接口,遍历方式
Iterator iterator= linkedList.iterator();
while(iterator.hasNext()){
    Object next = iterator.next();
}


//LinkedList的增强for
for(Object o1: linkedList){
    System.out.println(o1);
}

//传统for循环也可以

ArrayList 和 LinkedList 的比较:

底层结构 增删的效率 改查的效率
ArrayList 可变数组 较低,数组扩容 较高
LinkedList 双向链表 较高,链表扩容 较低

如何选择ArrayList和LinkedList

  1. 如果我们改查的操作多,选择ArrayList
  2. 如果我们增删的操作多,选择LinkedList
  3. 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择 ArrayList
  4. 一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另-个模块是LinkedList.
posted @ 2025-08-26 21:38  lu璐  阅读(5)  评论(0)    收藏  举报