LinkedList의 개요
1.LinkedList 달성 설정된 내부 노드 클래스 <E>에 기초하여, 이중 연결리스트의 세트로 구현된다.
2.LinkedList 지원은 이중 목록 액세스, 복제, 시퀀싱을 연결하고, 요소가 반복 될 수있다 주문했다.
3.LinkedList 크기는 초기화없이 팽창기구 헤드 노드를 통해, 테일 노드 반복 검색되지 않는다.
LinkedList의 데이터 구조
데이터 구조, 데이터 구조는 종종 우리는 소스 코드가있는 유일한 방법입니다 분석 다양한 데이터 구조를 이해하고, 컬렉션의 역할과 초점을 제한 거짓말의 컬렉션의 본질이다.
LinkedList의 데이터 구조는 다음과
리스트 기초 첨가 :
1) 단일 연결 목록 :
요소 : 저장하는 데 사용되는 소자
다음 : 노드가 다음 요소를 가리키는 데 사용되는
다음 노드 구조를 연결하도록 각각의 노드에 대한 포인터를 가리키는 통해서 다음 포인트의 마지막 노드를 null로.
2) 단방향 원형 연결리스트
이전과 다음과 소자
링에 해당 기탁되도록 다음 노드는 오히려 NULL로하지 않고, 헤드에서의 노드 점을 연결 마지막 방법이 될 것이다
이중 연결 3)리스트
저장 요소 : 요소
미리 : 가리키는 데 사용 앞 요소는
다음 : 후의 소자 포인트
두 개의 포인터를 포함하는 이중 연결리스트, 노드 앞 가리킨 다음 노드 후의 시점이지만 미리 널, 널 가리키는 꼬리의 마지막 노드를 가리키는 첫 번째 노드의 헤드를 미리.
4) 원 양방향 연결 목록
요소 다음의 전방 사전으로서
마지막 노드에 프리 첫 번째 노드는 제 1 노드의 다음 포인트의 마지막 노드는 또한이 "링."형성
LinkedList의 소스 코드 분석
/**
* LinkedList 使用 iterator迭代器更加 快速
* 用链表实现的集合,元素有序且可以重复
* 双向链表
*/
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
/**
* 实际元素个数
*/
transient int size = 0;
/**
* 头结点
*/
transient Node<E> first;
/**
* 尾结点
*/
transient Node<E> last;
/**
* 无参构造方法.
*/
public LinkedList() {
}
/**
* 集合参数构造方法
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 内部类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 extends AbstractSequentialList<E>
AbstractSequentialList extends AbstractList
AbstractList extends AbstractCollection
java中所有类都继承Object,所以LinkedList的继承结构如上图。
1. AbstractSequentialList是一个抽象类,继承了AbstractList接口,AbstractList抽象类中可以有抽象方法,还可以有具体的实现方法,AbstractList实现接口中一些通用的方法,AbstractSequentialList再继承AbstractList,拿到通用基础的方法,然后自己在重写实现基于链表的方法:add/addAll/get/iterator/listIterator/remove/set,这样的好处是:让代码更简洁,AbstractList随机存取功能基类,AbstractSequentialList链表存取功能基类,父类抽象,子类个性,父类一般是抽象类,由子类来实现丰富。
2.LinkedList实现了List<E>、Deque<E>、Cloneable、Serializable接口。
1)List<E>接口,集合通用操作方法定义。
2)Deque<E>接口,双向队列,在Queue单项队列的基础上增加为双向队列,提高查询/操作效率
3)Cloneable接口,可以使用Object.Clone()方法。
4)Serializable接口,序列化接口,表明该类可以被序列化,什么是序列化?简单的说,就是能够从类变成字节流传输,反序列化,就是从字节流变成原来的类
LinkedList核心方法分析
1. add方法(6种重载实现)--增
1)add(E);//默认直接在末尾添加元素
/**
* 新增元素
*/
public boolean add(E e) {
// 添加到末尾
linkLast(e);
return true;
}
/**
* 链接到末尾.
*/
void linkLast(E e) {
// 保存尾结点,l为final类型,不可更改
final Node<E> l = last;
// 新生成结点的上一个为l,下一个为null
final Node<E> newNode = new Node<>(l, e, null);
// 重新赋值尾结点
last = newNode;
if (l == null) // 尾结点为空
first = newNode; // 赋值头结点
else
l.next = newNode; // 尾结点的下一个为新生成的结点
size++; // 大小加1
modCount++; // 结构性修改加1
}
2)add(int index, E element);//给指定下标,添加元素
/**
* 在index位置插入节点
* 1.如果index等于size,则在末尾新增元素,原因:size为实际元素个数,index为下标,所以index=size时,说明要在末尾插入元素
* 2.如果index不等于size,则根据index下标找到节点,在节点前插入元素,原因:需要占用index下标位置。
*/
public void add(int index, E element) {
//查看下标是否越界
checkPositionIndex(index);
//如果指定下标等于实际元素个数,则添加到末尾
if (index == size)
linkLast(element);
else //否则,找到index位置元素添加到index后
linkBefore(element, node(index));
}
/**
- 判断下标是否越界
*/
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/** - 根据index下标找到节点
- 优化:由于是双向链表,所以判断索引位置(size/2),前半段从头节点开始查找,后半段从尾节点开始查找
*/
Node<E> node(int index) {
// assert isElementIndex(index);
// 判断插入的位置在链表前半段或者是后半段 size/2的1次方
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;
}
}
/** -
在非空节点succ前插入数据
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}3)addAll(Collection<? extends E> c);//添加Collection类型元素
/**
- 添加一个集合
*/
public boolean addAll(Collection<? extends E> c) {
//在末尾添加
return addAll(size, c);
}
4)addAll(int index, Collection<? extends E> c);//指定位置,添加Collection类型元素
/**
- 从指定的位置开始,将指定collection中的所有元素插入到此列表中,新元素的顺序为指定collection的迭代器所返回的元素顺序
*/
public boolean addAll(int index, Collection<? extends E> c) {
// 检查插入的的位置是否合法
checkPositionIndex(index);
// 将集合转化为数组
Object[] a = c.toArray();
// 保存集合大小
int numNew = a.length;
if (numNew == 0) // 集合为空,直接返回
return false;
Node<E> pred, succ; //上一个 下一个
if (index == size) { // 如果插入位置为链表末尾,则后继为null,上一个为尾结点
succ = null;
pred = last;
} else { // 插入位置为其他某个位置
succ = node(index); // 寻找到该结点
pred = succ.prev; // 保存该结点的上一个
}
for (Object o : a) { // 遍历数组
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null); // 生成新结点
if (pred == null) // 表示在第一个元素之前插入(索引为0的结点)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) { // 表示在最后一个元素之后插入
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
// 修改实际元素个数
size += numNew;
// 结构性修改加1
modCount++;
return true;
}
5)addFirst(E e);//头结点添加元素
/**
* 头结点插入元素
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 链接头结点
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);//新建节点,头结点为null,尾节点为first
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
5)addLast(E e);//尾结点添加元素
/**
* 尾节点添加元素
*/
public void addLast(E e) {
linkLast(e);
}
/**
* 链接尾节点
*/
void linkLast(E e) {
// 保存尾结点,l为final类型,不可更改
final Node<E> l = last;
// 新生成结点的上一个为l,下一个为null
final Node<E> newNode = new Node<>(l, e, null);
// 重新赋值尾结点
last = newNode;
if (l == null) // 尾结点为空
first = newNode; // 赋值头结点
else
l.next = newNode; // 尾结点的下一个为新生成的结点
size++; // 大小加1
modCount++; // 结构性修改加1
}
2.remove方法(7种重载实现)--删
1)remove(int index); //根据指定下标 删除元素
/**
* 根据指定下标 删除元素
*/
public E remove(int index) {
//判断索引是否越界
rangeCheck(index);
modCount++;
//获取旧元素
E oldValue = elementData(index);
//将数组elementData中index位置之后的所有元素向前移一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将原数组最后一个位置置为null,由GC清理
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
2)remove(Object o); //根据指定元素 删除元素
/**
* 移除ArrayList中首次出现的指定元素(如果存在),ArrayList中允许存放重复的元素
*/
public boolean remove(Object o) {
// 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
//私有的移除方法,跳过index参数的边界检查以及不返回任何值
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*
* 根据下标快速删除元素
*/
private void fastRemove(int index) {
modCount++;
//将数组elementData中index位置之后的所有元素向前移一位
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
}
/**
* 清空ArrayList,将全部的元素设为null,等待垃圾回收将这个给回收掉,所以叫clear
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
3)removeAll(Collection<?> c); //删除包含在指定容器c中的所有元素
/**
* 删除ArrayList中包含在指定容器c中的所有元素
*/
public boolean removeAll(Collection<?> c) {
//检查指定的对象c是否为空
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 删除全部
* @author jiaxiaoxian
* @date 2019年2月12日
*/
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0; //读写双指针
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement) //判断指定容器c中是否含有elementData[r]元素
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
4)removeIf(Predicate<? super E> filter); //按照一定规则过滤(删除)集合中的元素
/**
* 按照一定规则过滤(删除)集合中的元素
* 如:idList.removeIf(id -> id == nul);
* 去掉 List idList 集合中id 为 null 的
* @param filter
* @return
*/
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
总结:
remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。
3.set方法--改
/**
* 覆盖指定下标元素
*/
public E set(int index, E element) {
//判断下标是否越界
checkElementIndex(index);
//获得下标节点
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
/**
* 判断下标是否越界
*/
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4.get方法--查
/**
-
返回指定索引的值
*/
public E get(int index) {
//判断索引是否越界
rangeCheck(index);
return elementData(index);
}
/**- @author jiaxiaoxian
- @date 2019年2月12日
- 返回下标元素的 值br/>*/
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}5.indexOf方法--查找下标
/**
- 查找下标, 如果为null,直接和null比较,返回下标
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/** -
查找最后出现的下标,从大往下循环查找
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}6.clone方法--克隆
/**
-
复制,返回此ArrayList 的浅拷贝
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}7.trimToSize方法--删除冗余容量
/**
- 判断数据实际容量大小,删除自动增长后冗余的容量
- 该方法用于回收多余的内存。也就是说一旦我们确定集合不在添加多余的元素之后,调用 trimToSize() 方法会将实现集合的数组大小刚好调整为集合元素的大小。
- 注意:该方法会花时间来复制数组元素,所以应该在确定不会添加元素之后在调用
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}LinkedList总结
1) LinkedList의는 기본적으로 일반적인 클래스 E 형 내부 널 저장할 수 있습니다.
2) LinkedList의 삽입은 요소 데이터는 인접 노드 요소와 높은 효율을 수정해야 이동 찾을 수 스크래치 / 꼬리 노드 탐색에서 빠르고 느린 쿼리를 삭제합니다.
3) LinkedList의 부모 클래스의 Iterable 상속, 그래서 통과 할 때이 반복자 루프, 높은 효율을 사용하는 것이 좋습니다.
4) LinkedList의 작동 헤드 / 대응 노드 첫 / 마지막에있어서, 고효율의 꼬리 쿼리 유사한 이분 탐색한다.
5) Deque와 <E> 양단 큐, 팀 목록 관련 스택 / 스택 메소드를 구현 LinkedList의.