List源码分析(包含ArrayList、LinkedList、Vector)

本文对Java中List相关数据结构进行源码分析。涵盖ArrayList底层操作机制,包括不同构造器下的扩容规则;LinkedList的全面说明、底层双向链表机制、模拟实现及增删改查源码;还涉及Vector源码分析,并对Vector和ArrayList进行了比较。

建议Debug模式调试

一、ArrayList的底层操作机制源码分析

  1. ArrayList中维护了一个Object类型的数组elementData。
 //TODO transient:表示瞬间的、短暂的;表示该属性不会被序列化
 transient Object[] elementData
  1. 当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
  2. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
/**
  * ArrayList采用无参构造器创建
  */
 private static void noArgumentConstructor() {
   //使用无参构造器创建ArrayList对象
   ArrayList<Integer> list = new ArrayList<>();
   //使用有参构造器创建ArrayList对象
//    ArrayList<Integer> 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);
 }

二、LinkedList源码分析

1、LinkedList 的全面说明
  • LinkedList底层实现了双向链表和双端队列特点
  • 可以添加任意元素(元素可以重复),包括null
  • 线程不安全,没有实现同步
2、LinkedList 的底层操作机制
  • LinkedList底层维护了一个双向链表
  • LinkedList中维护了两个属性first和last分别指向首节点和尾节点
  • 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
  • 所以LinkedList的元素的添加和删除,不是通过数组完成的。相对来说效率较高
3、模拟LinkedList双向链表
  • 定义一个内部类Node,表示双向链表的一个结点
/**
   * 定义一个 Node 类,Node 对象 表示双向链表的一个结点
   */
  static 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;
    }
  }

main方法

public static void main(String[] args) {
    //模拟一个简单的双向链表
    Node jack = new Node("jack");
    Node tom = new Node("tom");
    Node hsp = new Node("老刘");

    //连接三个结点,形成双向链表
    //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,就是双向链表的尾结点
    //演示,从头到尾进行遍历
    System.out.println("===从头到尾进行遍历===");
    while (true) {
      if (first == null) {
        break;
      }//输出 first 信息
      System.out.println(first);
      first = first.next;
    }
    //演示,从尾到头的遍历
    System.out.println("====从尾到头的遍历====");
    while (true) {
      if (last == null) {
        break;
      }//输出 last 信息
      System.out.println(last);
      last = last.pre;
    }

    //演示链表的添加对象/数据,是多么的方便
    //要求,是在 tom --------- 老刘直接,插入一个对象 smith
    //1. 先创建一个 Node 结点,name 就是 smith
    Node smith = new Node("smith");
    //下面就把 smith 加入到双向链表了
    smith.next = hsp;
    smith.pre = tom;
    hsp.pre = smith;
    tom.next = smith;
    //让 first 再次指向 jack
    first = jack;//让 first 引用指向 jack,就是双向链表的头结点

    System.out.println("===从头到尾进行遍历===");
    while (true) {
      if (first == null) {
        break;
      }
      //输出 first 信息
      System.out.println(first);
      first = first.next;
    }
    last = hsp; //让 last 重新指向最后一个结点
    //演示,从尾到头的遍历
    System.out.println("====从尾到头的遍历====");
    while (true) {
      if (last == null) {
        break;
      }
      //输出 last 信息
      System.out.println(last);
      last = last.pre;
    }

  }

4、LinkedList增删改查源码分析
public static void linkedListCrud() {
    LinkedList linkedList = new LinkedList();
    linkedList.add(1);
    linkedList.add(2);
    linkedList.add(3);
    System.out.println("linkedList=" + linkedList);

    //演示一个删除结点的
    linkedList.remove(); // 这里默认删除的是第一个结点
//    linkedList.remove(2);
    System.out.println("linkedList=" + linkedList);

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

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

    //因为 LinkedList 是 实现了 List 接口, 遍历方式
    System.out.println("===LinkeList 遍历迭代器====");
    Iterator iterator = linkedList.iterator();
    while (iterator.hasNext()) {
      Object next = iterator.next();
      System.out.println("next=" + next);
    }

    System.out.println("===LinkeList 遍历增强 for====");
    for (Object o1 : linkedList) {
      System.out.println("o1=" + o1);
    }
    System.out.println("===LinkeList 遍历普通 for====");
    for (int i = 0; i < linkedList.size(); i++) {
      System.out.println(linkedList.get(i));
    }
    /**
     * TODO 链表add元素:源码阅读.
     *
     *  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> newNode = new Node<>(l, e, null);
     *        last = newNode;
     *        if (l == null)
     *          first = newNode;
     *        else
     *          l.next = newNode;
     *         size++;
     *         modCount++;
     *    }
     *
     */

    //TODO 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;
     *    }
     *
     */
  }

三、Vector源码分析

public static void main(String[] args) {
    //
    Vector<Object> vector = new Vector<>();
    for (int i = 0; i < 10; i++) {
      vector.add(i);
    }
    vector.add(100);//扩容
    System.out.println("vector=" + vector);
    //源码分析
    //1、new Vector<>()实际上还是调用了有参的构造方法,默认容量为10个
    //2、调用add(),内部调用ensureCapacityHelper来确定是否需要扩容(minCapacity - elementData.length > 0则需要扩容)
    //3、vector.add(100)时,触发扩容

  }

四、Vector和ArrayList的比较

名字底层结构版本线程安全(同步)效率扩容备份
ArrayList可变数组jdk1.2不安全,效率高如果使用有参构造器按照1.5倍扩容,如果是无参构造器。1.第一次扩容10;2.从第二次开始按照1.5倍
Vector可变数组Object[]jdk1.0安全,效率不高如果是无参,默认10,满后,按照2倍扩容。如果是指定大小创建Vector,则每次按照2倍扩容.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双木林L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值