Java集合(一)ArrayList、LinkedList、Vector

接口继承关系和实现

Java中集合类存放在java.util包中
主要实现有三种类型:List、Set和Map

1. Collection是集合List、Set、Queue的最基本接口。
2. Map为独立接口,是映射表的基础接口。
3. Iterator用于遍历集合中的数据
集合关系架构图

List接口的实现

Java中List是一中常用的数据类型。List是中储存的是有序的数据,数据可重复。List一共有三个实现类ArrayList、LinkedList、Vector。

ArrayList

ArrayList是最常用的List实现类,内部是通过数据实现的,每个数据在内存中是连续的,允许对元素进行快速随机访问。

ArrayList初始化

当ArrayList在创建时,创建一个空的数组,容量为0

	// elementData用来存储数据的数组
	transient Object[] elementData;
	// 默认扩容容量为10
	private static final int DEFAULT_CAPACITY = 10;
	// 默认空的数组
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
···
	// 无参构造方法
	public ArrayList() {
	    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	}
	
	// initialCapacity指定数组的容量
	public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
···

ArrayList的添加操作

	// 直接添加元素
	public boolean add(E e) {
		// 计算数组容量,判断是否需要扩容。
		// 当添加时,初始数组为空 ,第一次扩容为默认扩容容量DEFAULT_CAPACITY=10
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 在数组最后添加元素
        elementData[size++] = e;
        return true;
    }
	
	// 根据下标插入数据
	public void add(int index, E element) {
		// 检查插入的下标是否越界
        rangeCheckForAdd(index);
		// 判断是否需要扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 复制插入下标index之后的数组,并赋值给自己
        // 将index之后整体向后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 将数据插入到index
        elementData[index] = element;
        size++;
    }

ArrayList的扩容机制

判断是否需要扩容的3个方法

	// 扩容方法的入口
	// minCapacity判断扩容的必要参数  minCapacity=size+1
	// size+1是为了当数组长度达到 (最大容量-1)时候扩容
	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
	
	// 这个方法主要是判断数组是否为空数组
	// 如果为空数组,使用数组默认容量DEFAULT_CAPACITY=10
	// 否则返回返回判断扩容参数minCapacity=size+1
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
	
	// 真正判断扩容的方法
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 注意!!!
        // elementData.length是数组的长度即数组的容量  而不是集合的size
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

扩容方法:grow()

	// 执行扩容的方法
	private void grow(int minCapacity) {
        // oldCapacity 旧的数组容量
        int oldCapacity = elementData.length;
        // newCapacity 扩容后数组的容量 =oldCapacity + oldCapacity / 2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            
        // MAX_ARRAY_SIZE数组长度的最大限制
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            
        // 将旧数组数据复制到扩容后的数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

优点:查询速度块,适合遍历操作
缺点:
当数据中间位置插入或删除时,需要对数组进行复制、移动等操作,效率较低。
当数组扩容时,需要把已有的数据复制到新的数组中
线程不安全

LinkedList

LinkedList是用链表存储结构存储数据的,因此很适合数据的动态插入和删除,查询遍历速度较慢。
链表结构还提供了操作表头和表尾的方法,可以当作堆栈、队列和双向队列使用。

存储数据的节点Node

	// 在链表中,数据的依靠节点来存储,并有上下节点连接
	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;
        }
    }

LinkedList添加数据

LinkedList添加节点通常有两种情况
一:插入节点到链表尾部
二:插入节点到链表头部
三:插入节点到指定下标位置

	// 默认从链表尾部添加节点
	public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    // 尾部添加节点
	void linkLast(E e) {
		// last最后一个节点
        final Node<E> l = last;
        // 当前节点添加到最后节点l的后面,并设置当前节点的上个节点指向l
        final Node<E> newNode = new Node<>(l, e, null);
        // 把添加的节点设置为最后节点
        last = newNode;
        // 如果最后没有节点,就把节点设置为头节点
        if (l == null)
            first = newNode;
        else
       	// 如果有最后节点,设置最后节点指向当前节点
            l.next = newNode;
        size++;
        modCount++;
    }

	// 通过下标插入节点
	public void add(int index, E element) {
		// 检查下标是否越界
        checkPositionIndex(index);
		
		// 如果插入的节点刚好是最后的节点,直接插入到最后
        if (index == size)
            linkLast(element);
        else
        // 插入节点到指定下表
            linkBefore(element, node(index));
    }
    
	// 插入指定下标节点  succ指定下标的节点,断言不等于null
	void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        // pred 指定节点index的上一个节点
        final Node<E> pred = succ.prev;
        // newNode 插入节点到指定下标位置index,并设置当前节点的上下节点指向
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 将指定index节点的上个节点指向当前节点
        succ.prev = newNode;
        // 如果上一个节点为null,设置当前节点为头节点
        if (pred == null)
            first = newNode;
        else
        // 如果上一个节点不为null,设置指向当前节点
            pred.next = newNode;
        size++;
        modCount++;
    }
插入指定节点过程图

插入指定节点图优点: 底层数据结构是链表,查询慢,增删快
缺点: 线程不安全

Vector

Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一 个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此, 访问它比访问ArrayList慢。

	// 初始容量为10
	public Vector() {
        this(10);
    }
···
	// 操作数据的方法都有synchronized的关键字,因此Vector线程是安全的
	public synchronized boolean add(E e) {}
	···
	public synchronized E get(int index) {}
	···
	public synchronized E remove(int index) {}

优点: 底层数据结构是数组,查询快,增删慢
缺点: 线程安全,效率低

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的ArrayListLinkedListVector是三种常见的集合类,它们都实现了List接口,但在实现和使用上有一些区别。 1. 实现方式: - ArrayList是基于数组实现的动态数组,可以动态调整数组的大小。 - LinkedList是基于链表实现的,每个元素都包含一个指向前一个和后一个元素的引用。 - Vector也是基于数组实现的动态数组,类似于ArrayList,但是它是线程安全的。 2. 线程安全性: - ArrayListLinkedList不是线程安全的,多个线程同时访问时需要外部同步控制。 - Vector是线程安全的,它的每个方法都使用了synchronized关键字进行同步,可以在多线程环境下使用。 3. 性能: - ArrayList的性能比LinkedList好,因为它直接通过索引访问元素,而LinkedList需要遍历链表才能找到指定位置的元素。 - Vector由于需要进行同步控制,性能相对较差。 4. 插入和删除操作: - ArrayList在末尾插入和删除元素的性能较好,但在中间或开头插入和删除元素时,需要移动其他元素。 - LinkedList在任意位置插入和删除元素的性能较好,因为只需更改节点的引用。 5. 使用场景: - 如果需要频繁访问集合中的元素,并且对数据的增删操作较少,可以选择ArrayList。 - 如果需要频繁进行插入和删除操作,或者需要使用栈、队列等数据结构,可以选择LinkedList。 - 如果需要在多线程环境中使用,可以选择Vector。 总结:ArrayList适用于读取操作频繁的场景,LinkedList适用于频繁插入、删除操作的场景,Vector适用于多线程环境。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值