我习惯了无所谓,却不是真的什么都不在乎。                 请关注:源码猎人

目录

简介

LinkedList 源码解读

LinkedList父类 AbstractSequentialList 抽象类

AbstractSequentialList 构造函数

AbstractSequentialList 方法

LinkedList 类

LinkedList 属性

LinkedList 构造函数

LinkedList  方法

LinkedList 内部类

Vector 向量 源码解读

Vector属性

Vector构造函数

Vector方法

Vector内部类

Stack 栈 源码解读

Stack 方法

常见面试题


简介

LinkedList是特殊List,为什么说它特殊?从类图上就可以看出来,它是List、Queue(Deque继承Queue)的实现,相比ArrayList多实现了Deque接口而少实现了RandomAccess接口,从名字就可以看除内部使用链表结构;Vector 向量是线程安全的动态数组,跟ArrayList很相似,JDK1.0中添加Vector类,Vector一样可以维护插入顺序,但Vector包含了许多传统的方法,这些方法不属于集合框架;Stack栈是一种只能在一端进行插入或删除操作的线性表,先进后出表。

LinkedList 源码解读

LinkedList父类 AbstractSequentialList 抽象类

public abstract class AbstractSequentialList<E> extends AbstractList<E>

AbstractSequentialList继承自AbstractList。AbstractSequentialList是在迭代器的基础上实现的get、set、add和remove方法。并且AbstractSequentialList 只支持按次序访问,而不像 AbstractList 那样支持随机访问

AbstractSequentialList 构造函数

protected AbstractSequentialList() {
}

AbstractSequentialList 方法

// 根据位置取元素
public E get(int index) {try {return listIterator(index).next();} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException("Index: "+index);}
}

通过listIterator确定位置,调用next取值

// 修改指定位置的元素为新值
public E set(int index, E element) {try {ListIterator<E> e = listIterator(index);E oldVal = e.next();e.set(element);return oldVal;} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException("Index: "+index);}
}

覆盖新值返回旧值

// 添加元素到指定位置
public void add(int index, E element) {try {listIterator(index).add(element);} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException("Index: "+index);}
}

如果指定位置有值,将当前处于该位置的后续元素移到右边

// 删除
public E remove(int index) {try {ListIterator<E> e = listIterator(index);E outCast = e.next();e.remove();return outCast;} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException("Index: "+index);}
}

listIterator 取值,调用listIterator删除元素

// 在指定位置添加一个线性集合
public boolean addAll(int index, Collection<? extends E> c) {try {boolean modified = false;ListIterator<E> e1 = listIterator(index);Iterator<? extends E> e2 = c.iterator();while (e2.hasNext()) {e1.add(e2.next());modified = true;}return modified;} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException("Index: "+index);}
}
// 获取迭代器
public Iterator<E> iterator() {return listIterator();
}
// 根据索引获取listIterator
public abstract ListIterator<E> listIterator(int index);

AbstractSequencetialList中的增删改查都是用ListIterator完成,子类必须实现listIterator(int index)方法

LinkedList

public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable

实现List<E>, Deque<E>接口,继承AbstractSequentialList<E>抽象类

LinkedList 属性

// 长度
transient int size = 0;
// 头元素
transient Node<E> first;
// 尾元素
transient Node<E> last;

LinkedList 构造函数

public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {this();addAll(c);
}

从构造函数可以看出初始默认值为0

LinkedList  方法

LinkedList 双向链表基础方法

// 在头上增加元素
private void linkFirst(E e) {final Node<E> f = first;// 构建新节点final Node<E> newNode = new Node<>(null, e, f);// 新节点置为头节点first = newNode;// 旧头节点后移if (f == null)last = newNode;elsef.prev = newNode;// 长度加1size++;// 修改次数加1modCount++;
}
// 在尾部追加元素
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;elsel.next = newNode;// 长度加1size++;// 修改次数加1modCount++;
}
// 在succ节点前插入元素
void linkBefore(E e, Node<E> succ) {final Node<E> pred = succ.prev;// 构建新元素,新节点下级指向succfinal Node<E> newNode = new Node<>(pred, e, succ);// succ上一个节点指向新节点succ.prev = newNode;// succ原来上级节点的next指向新节点if (pred == null)first = newNode;elsepred.next = newNode;// 长度加1size++;// 修改次数加1modCount++;
}
// 删除头节点
private E unlinkFirst(Node<E> f) {final E element = f.item;// 获取下一个节点final Node<E> next = f.next;f.item = null;f.next = null; // help GC// 头节点指向nextfirst = next;if (next == null)last = null;elsenext.prev = null;// 长度减1size--;// 修改次数加1modCount++;return element;
}
// 删除尾节点
private E unlinkLast(Node<E> l) {final E element = l.item;// 获取上一个节点final Node<E> prev = l.prev;l.item = null;l.prev = null; // help GC// 尾节点指向上一个节点last = prev;if (prev == null)first = null;elseprev.next = null;// 长度减1size--;// 修改次数加1modCount++;return element;
}
// 删除节点
E unlink(Node<E> x) {// assert x != null;final E element = x.item;// 获取下级节点final Node<E> next = x.next;// 获取上级节点final Node<E> prev = x.prev;// 上级节点的下级指向当前节点的下级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;
}
// 索引取值
Node<E> node(int 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;}
}

LinkedList 添加

// 添加元素
public boolean add(E e) {// 默认尾部追加linkLast(e);return true;
}
// 头部添加
public void addFirst(E e) {linkFirst(e);
}
// 尾部添加
public void addLast(E e) {linkLast(e);
}

LinkedList 删除

// 移除头元素
public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);
}
// 移除尾元素
public E removeLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return unlinkLast(l);
}
// 删除元素
public boolean remove(Object o) {if (o == null) {// 遍历链表找到为空的元素for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) {unlink(x);// 只会删除第一个return true;}}} else {// 遍历链表找到equals一样的元素for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) {unlink(x);// 只会删除第一个return true;}}}return false;
}

LinkedList 搜索

// 获取头节点
public E getFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return f.item;
}
// 获取尾节点
public E getLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l.item;
}
// 根据所有取值
public E get(int index) {checkElementIndex(index);// 调用node(index)return node(index).item;
}

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

Node<E>用来包装实际元素值,并且提供两个指针prev、next。

Vector 向量 源码解读

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Vector属性

// 元素数组
protected Object[] elementData;
// 元素个数
protected int elementCount;
// 增量长度
protected int capacityIncrement;
// 修改次数, 每次add、remove它的值都会加1(这个属性是AbstractList中的)
protected transient int modCount = 0;
// 默认最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Vector构造函数

public Vector(int initialCapacity, int capacityIncrement) {super();// 初始长度小于0时抛异常if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);// 创建元素数组this.elementData = new Object[initialCapacity];// 指定增长长度this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {// 一个参数构造函数默认指定,增长长度为0this(initialCapacity, 0);
}
public Vector() {// 空参构造函数,制定初始数组长度10this(10);
}
public Vector(Collection<? extends E> c) {// 参数集合转数组,赋值给元素数组elementData = c.toArray();// 设置元素个数elementCount = elementData.length;// 不是Object数组时转为Object数组if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

从构造函数中可以看出创建时就初始化了10个长度的元素数组,增长长度只有第一个构造函数有设置值,其他的都为0

Vector方法

public synchronized int size() {// 返回元素个数return elementCount;
}
public synchronized boolean isEmpty() {// 元素个数为0时返回truereturn elementCount == 0;
}

设置长度

public synchronized void setSize(int newSize) {// 修改次数加1modCount++;// 判断是否需要扩容if (newSize > elementCount) {ensureCapacityHelper(newSize);} else {// 参数以后的元素清空掉for (int i = newSize ; i < elementCount ; i++) {elementData[i] = null;}}elementCount = newSize;
}
private void ensureCapacityHelper(int minCapacity) {// 判断新长度是否大于原数组长度,大于则扩容if (minCapacity - elementData.length > 0)grow(minCapacity);
}

新长度小于元素个数时阶段原数组,新长度大于元素数组长度时使用新长度扩容

扩容

private void grow(int minCapacity) {// 获取原长度int oldCapacity = elementData.length;// 获取新长度// 增长长度不为0时 新长度=原长度+增长长度// 增长长度不为0时 新长度=原长度+增长长度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);
}
private static int hugeCapacity(int minCapacity) {// 长度小于0则抛异常if (minCapacity < 0)throw new OutOfMemoryError();// 长度大于默认最大值取Integer最大值return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

Vector扩容比较特殊,不像ArrayList,ArrayList没有增长长度正常情况下扩容只能为原长度的1.5倍,而Vector在扩容时,先判断增长长度是否为0,不为0时使用增长长度,为0时原长度乘以2

查找元素
从前往后找

public int indexOf(Object o) {return indexOf(o, 0);
}
public synchronized int indexOf(Object o, int index) {if (o == null) {// index为起始位置for (int i = index ; i < elementCount ; i++)// 查找元素为空if (elementData[i]==null)return i;} else {// index为起始位置for (int i = index ; i < elementCount ; i++)// 查找equals一样的元素if (o.equals(elementData[i]))return i;}// 没有找到返回-1return -1;
}

从后往前找

public synchronized int lastIndexOf(Object o) {return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {// 判断index是否超过元素个数if (index >= elementCount)throw new IndexOutOfBoundsException(index + " >= "+ elementCount);if (o == null) {// 从后往前找for (int i = index; i >= 0; i--)if (elementData[i]==null)return i;} else {// 从后往前找for (int i = index; i >= 0; i--)if (o.equals(elementData[i]))return i;}// 没有找到返回-1return -1;
}

元素数组操作

public Enumeration<E> elements() {// 每次调用此方法都会有一个新对象return new Enumeration<E>() {int count = 0;public boolean hasMoreElements() {return count < elementCount;}public E nextElement() {// 虽然方法上没加锁,但是这儿加了,锁的是当前对象synchronized (java.util.Vector.this) {if (count < elementCount) {return elementData(count++);}}throw new NoSuchElementException("Vector Enumeration");}};
}

这里顺带介绍Enumeration,Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。

前后第一个元素

public synchronized E firstElement() {// 数组为空时抛异常if (elementCount == 0) {throw new NoSuchElementException();}return elementData(0);
}
public synchronized E lastElement() {// 数组为空时抛异常if (elementCount == 0) {throw new NoSuchElementException();}// 取最后一个元素return elementData(elementCount - 1);
}

索引找元素

E elementData(int index) {// 通过索引直接从数组中取值return (E) elementData[index];
}
public synchronized E elementAt(int index) {// 判断索引是否大于元素个数if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);}return elementData(index);
}
public synchronized void setElementAt(E obj, int index) {// 超过元素个数抛异常if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);}// 根据索引设值elementData[index] = obj;
}

删除指定位置上的元素

public synchronized void removeElementAt(int index) {modCount++;// 判断指定索引是否超过元素个数if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);}// 索引不能小于0else if (index < 0) {throw new ArrayIndexOutOfBoundsException(index);}// 索引位置以后的元素前向前移动int j = elementCount - index - 1;if (j > 0) {System.arraycopy(elementData, index + 1, elementData, index, j);}// 元素个数减1elementCount--;// 最后一个元素置空elementData[elementCount] = null; /* to let gc do its work */
}

在指定位置上插入元素

public synchronized void insertElementAt(E obj, int index) {modCount++;// 索引不能大于元素个数if (index > elementCount) {throw new ArrayIndexOutOfBoundsException(index+ " > " + elementCount);}// 是否需要扩容ensureCapacityHelper(elementCount + 1);// 索引以后的元素向后移动System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);// 向指定位置设置elementData[index] = obj;elementCount++;
}

追加元素

public synchronized void addElement(E obj) {modCount++;// 判断是否需要扩容ensureCapacityHelper(elementCount + 1);// 末尾增加元素elementData[elementCount++] = obj;
}

删除元素

public synchronized boolean removeElement(Object obj) {modCount++;// 找到元素位置int i = indexOf(obj);// 存在就删除if (i >= 0) {removeElementAt(i);return true;}return false;
}

删除所有元素

public synchronized void removeAllElements() {modCount++;// 遍历索引元素,依次置空for (int i = 0; i < elementCount; i++)elementData[i] = null;elementCount = 0;
}

添加删除

public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;
}
public boolean remove(Object o) {return removeElement(o);
}

添加删除方法主要是还使用上面的方法,这里就不在赘述

沿用父类方法

public synchronized boolean containsAll(Collection<?> c) {return super.containsAll(c);
}
public synchronized boolean removeAll(Collection<?> c) {return super.removeAll(c);
}
public synchronized boolean retainAll(Collection<?> c) {return super.retainAll(c);
}
public synchronized boolean equals(Object o) {return super.equals(o);
}
public synchronized int hashCode() {return super.hashCode();
}
public synchronized String toString() {return super.toString();
}

父类中的方法参照List接口篇

Vector内部类

private class Itr implements Iterator<E>
final class ListItr extends java.util.Vector.Itr implements ListIterator<E>
static final class VectorSpliterator<E> implements Spliterator<E>

Stack 栈 源码解读

public class Stack<E> extends Vector<E>

Stack 方法

public E push(E item) {addElement(item);return item;
}
public synchronized E pop() {E obj;int len = size();obj = peek();removeElementAt(len - 1);return obj;
}
public synchronized E peek() {int len = size();// 判断长度if (len == 0)throw new EmptyStackException();// 调用Vector中的方法return elementAt(len - 1);
}

自有方法很少,主要的就入栈出栈

常见面试题

1、ArrayList和LinkedList 的区别是什么?

  • 数据接口实现:ArrayList:是动态数组数据结构实现的,LinkedList是基于双向链表数据结构实现的.
  • 随机访问效率:ArrayList比LinkedList访问效率高,因为LinkedList是基于线性数据存储的方式,所以要移动指针依次查找数据.
  • 增加和访问的效率:在非首尾添加数据,LinkedList要比ArrayList要高,因为ArrayLIst是基于数组实现的他的添加要影响数组的数据下标.

2、LinkedList 可以添加空元素吗?

LinkedList 虽然实现了队列接口但是它并不是正在意义上的队列,队列不能存放空元素,原因是取的时候有争议,而LinkedList更多意义上是List,一般无视了这个问题,它可以向ArrayList一样添加空元素。

3、ArrayList和Vector 的区别是什么?

  • Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
  • ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList增长为原来的1.5倍。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。

面试官问:除了ArrayList你还看过哪些List源码相关推荐

  1. 当面试官问我ArrayList和LinkedList哪个更占空间时,我这么答让他眼前一亮

    前言 今天介绍一下Java的两个集合类,ArrayList和LinkedList,这两个集合的知识点几乎可以说面试必问的. 对于这两个集合类,相信大家都不陌生,ArrayList可以说是日常开发中用的 ...

  2. 已经成功拿到了几个offer的我来告诉你,Android面试官问的一些问题,看完这一篇就没有拿不到的offer

    前言 我是2020年毕业于中南大学的计算机学院的,大家可以叫我小吴,我嘞毕业之后在华为实习了差不多一年多,一直都从事着Android开发. 然后2021年的时候因为我自己的一些原因打算离职到外面看看, ...

  3. 腾讯3轮面试都问了Android事件分发,原理+实战+视频+源码

    一.架构师专题 想要掌握复杂的技术,必须要理解其原理和架构.本模块结合实际一线互联网大型项目理解架构思维,抽丝剥茧,层层深入,帮助大家成为Android架构师,在思想上对架构认识有一次升华,并知其所以 ...

  4. 字节跳动面试必问:撸了大神写的spring源码笔记

    正文 一些看到我文章的朋友,问我怎么零基础Java"逆袭",又怎么学好Java. 以下是我的一些经验总结吧: 1.制定好一下系统的学习规划,每天定量,学完什么知识点就掌握,能自己应 ...

  5. 胜面试官半子,阿里SpringBoot全栈笔记首发,源码实战齐飞

    对于SpringBoot的学习,一般的人都是花费很多的时间翻看技术博客.官方文档等,在没有找到一份案头书之前,这样零散学习的学习效果其实是不太好的.并且现在很多公司都以 Spring Boot为主来构 ...

  6. 字节跳动面试官问我看过哪些源码,然后就没有然后了

    最近,我的一位朋友在找工作,已经拿到了美团.快手等公司的Offer,准备选择其中一家入职了. 后来他又接到了字节跳动的电话,通知他去参加三面.从二面到三面之间隔了挺久的,他以为都没戏了,结果就收到了通 ...

  7. 图解 Java 线程的生命周期,看完再也不怕面试官问了

    文章首发自个人微信公众号: 小哈学Java www.exception.site/java-concur- 在 Java 初中级面试中,关于线程的生命周期可以说是常客了.本文就针对这个问题,通过图文并 ...

  8. redis怎么修改_面试官问我Redis事务,还问我有哪些实现方式

    ❝ 「第12期」 距离大叔的80期小目标还有68期,今天大叔要跟大家分享的内容是 -- Reids中的事务.同样,这也是redis中重要指数为四颗星的必备基础知识点.下面一起来了解一下吧. ❞ 相信大 ...

  9. 面试官问你斐波那契数列的时候不要高兴得太早 搞懂C语言函数指针 搜索引擎还可以这么玩? 那些相见恨晚的搜索技巧...

    面试官问你斐波那契数列的时候不要高兴得太早 前言 假如面试官让你编写求斐波那契数列的代码时,是不是心中暗喜?不就是递归么,早就会了.如果真这么想,那就危险了. 递归求斐波那契数列 递归,在数学与计算机 ...

最新文章

  1. P2280 [HNOI2003]激光炸弹(二维前缀和的简单应用)难度⭐⭐⭐
  2. HDU4858 项目管理 其他
  3. 数据库-统计信息相关资料
  4. Android Studio Linking an external C++ project 时候 报Invalid file name. Expected: CMakeLists.txt
  5. P4287-[SHOI2011]双倍回文【PAM】
  6. HTTP管线化(HTTP pipelining)
  7. lamp和php,[LAMP]Apache和PHP的结合
  8. 【剑指offer】面试题22:链表中倒数第 K 个节点
  9. 校园表白墙-带后台源码
  10. Webrtc服务器搭建转
  11. java基础知识补漏(1)---内部类,重写、重载
  12. 【matlab】人工智能的仿生优化算法之萤火虫算法讲解(Firefly Algorithm)
  13. VFIO代码分析(5)VFIO-IOMMU驱动
  14. 说到项目管理软件,不得不提的是禅道和JIRA
  15. OpenCV开发笔记(六十一):红胖子8分钟带你深入了解Shi-Tomasi角点检测(图文并茂+浅显易懂+程序源码)
  16. c语言中average的用法,average的用法辨析整理
  17. 长沙市民吴先生乘坐滴滴D1后,取消了买特斯拉的计划
  18. jdk安装https证书
  19. 平面设计【PS】汉堡寿司横幅PSD分层模版素材
  20. Facebook中国程序员之死:年仅38岁跳楼轻生,浙大EE毕业生,去年刚入职

热门文章

  1. Word中序号后面有空格怎么删除?
  2. 免费主机 虚拟主机 香港虚拟主机
  3. 酬乐天扬州初逢席上见赠
  4. win7修改默认锁屏背景
  5. 用户注册及APP使用隐私协议
  6. 即使不提供被告身份证信息,法院也必须受理立案
  7. PYTHON使用chinese_calendar判断日期是否为节假日
  8. 【Pytorch】rgb转lab颜色空间转换
  9. 数据库的应用之(智慧城市)
  10. Netty的深入浅出--79.Netty官方Reference Counted Objects文档说明