Android 开发也要懂得数据结构 - LinkList源码
Posted 进击的包籽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 开发也要懂得数据结构 - LinkList源码相关的知识,希望对你有一定的参考价值。
文章目录
- 上一篇文章 Android 开发也要懂得数据结构 - ArrayList源码 写了 ArrayList 主要方法的源码,这篇文章就看 LinkList。
- 本文章使用的是 JDK1.8 ,不同版本源码有差异。
1.LinkList特点
- LinkList是双向链表,链表的特点就是插入、删除操作时间复杂度为1,而查找的时间复制度为n。
- 增删速度相对于数组快,也不需要扩容操作,而查找、修改(需要先查找)速度相对慢一些。
- android开发中,如果我们的列表比较多增删的操作,比如即时通讯,新消息过来消息就插入且置顶,左滑或者长按删除之类的操作,就比较适合用LinkList。
- LinkList也是非线程安全的。
2.LinkList的继承关系
- LinkList跟ArrayList相识,也是实现List接口,List接口继承于Collection,Collection继承于Iterable。
3.LinkList的常用方法
3.1 构造方法
- 第一种普通的,不用传参的构造方法,里面也没做什么操作。
/**
* Constructs an empty list.
*/
public LinkedList()
- 第二种可以传入Collection集合,比如ArrayList传入之后,可以将ArrayList转化为LinkList。
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c)
this();
addAll(c);
3.2 普通增加数据 add(E e)
- LinkList的每一个元素除了存放内容本身,还有前后指针,分别指向前后节点。因此是双向链表。
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;
- 普通增加,就是在链表尾部添加数据。
- 可以看到源码,把 last 链表最后一个节点,先用 l 保存,然后 last 指向 newNode 新节点。
- 判断如果链表为空,那 first 头结点指向 newNode。
- 否则之前保存的最后一个节点 l 的 next 后指针指向 newNode。
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to @link #addLast.
*
* @param e element to be appended to this list
* @return @code true (as specified by @link Collection#add)
*/
public boolean add(E e)
linkLast(e);
return true;
/**
* Links e as last element.
*/
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++;
3.3 在索引位置插入数据 add(int index, E element)
- 在索引位置插入数据,不需要像数组那样把后面的元素一个个完后移动,也不需要扩容。
- 先检查插入的索引位置 index 是否合法。
- 如果 index位置是尾巴,就直接普通增加数据操作。
- 否则找到索引位置的节点,不为空哦。
- 这里看到优化的代码,先进行判断索引位置是否大于一半,进而选择 – 或者 ++,写这个代码的工程师太细节了
(╯‵□′)╯︵┻━┻。
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException @inheritDoc
*/
public void add(int index, E element)
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index)
// assert isElementIndex(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;
- 插入操作,就是把索引节点的前节点先用 pred 保存。
- 创建一个新的节点 newNode,新节点的前指针指向 pred,后指针指向 succ。
- 索引位置的 succ 的前指针指向 newNode,如果之前 succ 的前指针是空,那 newNode 就是头节点,否则 pred 的后指针就是指向 newNode。
- 指针不熟悉的小伙伴可以画图,一下就懂了。
/**
* Inserts element e before non-null Node 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++;
-
画图比较易懂
-
其他增加数据的方法,addFirst、addLast、addAll也是比较简单,跟 **add(int index, E element)**原理相似。
3.4 默认删除 remove()
- 普通的 remove方法移除第一个节点的内容。
- **element **保存内容,再将引用全部断开,然后再指向下一节点。
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
public E remove()
return removeFirst();
/**
* Removes and returns the first element from this list.
*
* @return the first element from this list
* @throws NoSuchElementException if this list is empty
*/
public E removeFirst()
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
/**
* Unlinks non-null first node 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;
3.5 删除索引位置 remove(int index)
- 这里的 node(index) 在插入时就分析过,一样样,就是找到指定位置的节点。
- element 保存着内容,next 为当前节点的后节点,prev 为前节点。
- 如果前节点为空,那 first 就是 next 节点,否则前节点的后指针指向 next,当前节点的前指针变为 null。
- 如果后节点为空,那 last 就是 prev 节点,否则后节点的前指针指向 prev,当前节点的后指针变为 null。
- 最后返回element。
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException @inheritDoc
*/
public E remove(int index)
checkElementIndex(index);
return unlink(node(index));
/**
* Unlinks non-null node x.
*/
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;
3.6 删除指定元素 remove(Object o)
- 遍历链表,找到元素进行删除。
- LinkList 是可以存放 null 的。
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* @code i such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns @code true if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return @code true if this list contained the specified element
*/
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
for (Node<E> x = first; x != null; x = x.next)
if (o.equals(x.item))
unlink(x);
return true;
return false;
3.7 修改元素 set(int index, E element)
- 找到索引位置的元素,修改内容。
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException @inheritDoc
*/
public E set(int index, E element)
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
3.8 长度 size()
- 返回的是元素的个数。
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size()
return size;
3.9 清空链表 clear()
- 将每一个节点的内容,前指针,后指针全部置 null。
/**
* Removes all of the elements from this list.
* The list will be empty after this call returns.
*/
public void clear()
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; )
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
first = last = null;
size = 0;
modCount++;
3.10 找到元素的索引位置 indexOf(Object o)
- 对比使用的是 Object 的 equals 方法,所以需要重写对象的 equals。
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index @code i such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*
* @param o element to search for
* @return the index of the first occurrence of the specified element in
* this list, or -1 if this list does not contain the element
*/
public int indexOf(Object o)
int index = 0;
if (o == null)
for (Node<E> x = first; x != null; x = x.next)
if (x.item == null)
return index;
index++;
else
for (Node<E> x = first; x != null; x = x.next)
if (o.equals(x.item))
return index;
index++;
return -1;
3.11 是否包含 contains(Object o)
- 是否包含某个元素,用的是 indexOf(Object o) 方法放回是否为 -1,来判断。
/**
* Returns @code true if this list contains the specified element.
* More formally, returns @code true if and only if this list contains
* at least one element @code e such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return @code true if this list contains the specified element
*/
public boolean contains(Object o)
return indexOf(o) != -1;
以上是关于Android 开发也要懂得数据结构 - LinkList源码的主要内容,如果未能解决你的问题,请参考以下文章
Android 开发也要懂得数据结构 - ArrayList源码
Android 开发也要懂得数据结构 - LinkList源码