一、什么是链表?
链表是⼀种物理存储单元上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表由⼀系列结点(链表中每⼀个元素称为结点)组成,结点可以在运⾏时动态⽣成。链表的存储⽅式是,每个位置上存储的元素分为两个部分,⼀部分是当前存储的元素的值,另外⼀部分是存储⼀下⼀个元素的内存地址。
二、链表的存储
1,存储方式
通过存储下个元素的地址,这样避免了数组需要连续存储空间的问题,虽然在内存中链表存储的元素是不连续的,但是也是由于这个问题,链表存储元素才被拆分为了两部分。这样才能通过内部维护的引⽤,知道整个链表的存储的位置关系。
2,链表的分类
三、用代码实现链表
代码如下(示例):
/**
* 单链表实现
* @param <E>
*/
public class LinkedList<E> {
private final Node<E> DUMMY_FRONT = new Node<>(null,null); // 当前链表的首元素 虚拟头结点
private int size; // 存储链表中元素的个数
public LinkedList(){
}
/**
* 添加元素
* 默认情况下添加元素到链表头位置
* @param e
*/
public void addFirst(E e){
add(0,e);
}
/**
* 添加元素
*/
public void add(E e){
addLast(e);
}
/**
* 添加元素到末尾
* @param e
*/
public void addLast(E e){
add(size,e);
}
/**
* 在指定的索引位置上添加元素
* @param index
* @param e
*/
public void add(int index,E e){
// 判定索引是否越界
checkIndex(index);
// 获取虚拟头结点
Node pre = DUMMY_FRONT;
// 获取待添加元素的前一个元素
for (int i = 0; i < index; i++)
pre = pre.next;
pre.next = new Node<>(e, pre.next);
//维护元素的个数
size++;
}
/**
* 根据指定索引删除元素
* @param index
* @return
*/
public E remove(int index){
checkBounds(index);
// 声明待删除节点的前一个元素
Node<E> pre = DUMMY_FRONT;
for (int i = 0;i<index;i++)
pre = pre.next;
// 获取当前待删除节点
Node<E> cur = pre.next;
pre.next = cur.next;
// 将待删除节点的下一个节点置为null
cur.next = null;
// 维护size
size--;
return cur.item;
}
/**
* 删除首元素
* @return
*/
public E removeFirst(){
return remove(0);
}
/**
* 删除尾元素
* @return
*/
public E removeLast(){
return remove(size-1);
}
/**
* 删除指定的元素
* @param e
*/
public void remove(E e){
int index = indexOf(e);
remove(index);
}
/**
* 修改指定位置上的元素为对应的指定元素
* 返回修改之前的元素
* @param e
* @param index
*/
public E set(E e,int index){
// 判定索引是否有效
checkBounds(index);
// 查找需要修改的元素
Node<E> cur = DUMMY_FRONT.next;
for (int i = 0;i<index;i++)
cur = cur.next;
E retItem = cur.item;
cur.item = e;
return retItem;
}
/**
* 确定索引是否有效
* @param index
*/
private void checkBounds(int index) {
if (index<0||index>=size)
throw new IllegalArgumentException("the linkedlist size:"+size+",index:"+index);
}
/**
* 确定索引是否有效
* @param index
*/
private void checkIndex(int index) {
if (index<0||index>size)
throw new IllegalArgumentException("the linkedlist size:"+size+",index:"+index);
}
// LinkedList存储的元素就是一个个的Node节点对象
private static class Node<E>{
E item; // 存储的元素
Node next; // 下一个节点对象
public Node(E item,Node next){
this.item = item;
this.next = next;
}
}
/**
* 获取集合中的元素个数
* @return
*/
public int getSize(){
return size;
}
/**
* 获取集合是否为空
* @return
*/
public boolean isEmpty(){
return size==0;
}
/**
* 查询指定索引位置上的元素
* @param index
* @return
*/
public E get(int index){
checkBounds(index);
Node<E> cur = DUMMY_FRONT.next;
for (int i = 0;i<index;i++)
cur = cur.next;
return cur.item;
}
/**
* 查询链表中的首元素
* @return
*/
public E getFirst(){
return get(0);
}
/**
* 查询链表中的尾元素
* @return
*/
public E getLast(){
return get(size-1);
}
/**
* 查询集合总是否包含某个元素
* @param e
* @return
*/
public boolean contains(E e){
return indexOf(e) != -1;
}
/**
* 查询元素对应的索引位置
* @param e
* @return
*/
public int indexOf(E e) {
Node<E> node = DUMMY_FRONT.next;
if (e == null){
for (int i = 0;i<size;i++){
if (e==node.item)
return i;
node = node.next;
}
}else{
for (int i = 0;i<size;i++){
if (e.equals(node.item))
return i;
node = node.next;
}
}
return -1;
}
public String toString(){
StringBuilder retStr = new StringBuilder();
retStr.append("size:"+size+"{");
Node<E> node = DUMMY_FRONT.next;
for (int i = 0;i<size;i++){
retStr.append(node.item);
if (i != size-1)
retStr.append("->");
if (node.next != null)
node = node.next;
}
retStr.append("}");
return retStr.toString();
}
}
总结
不同的应用场景,应该使用不同的数据结构,链表在需要无限递增的数据的时候,应该比较好用。