LinkedList与链表

目录

 一、ArrayList的缺陷

二、链表

1、链表的概念及结构

2、链表的实现

三、LinkedList的使用

1、什么是LinkedList

2、LinkedList的使用

(1)LinkedList的构造

 (2) LinkedList的其他常用方法介绍

(3)LinkedList的遍历

四、ArrayList和LinkedList的区别 


 一、ArrayList的缺陷

通过之前的学习,我们已经熟悉了 ArrayList 的使用,并且进行了简单模拟实现。通过源码知道, ArrayList 底层使用数组来存储元素。
由于其底层是一段连续空间,当 在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n) ,效率比较低,因此 ArrayList不适合做任意位置插入和删除比较多的场景 因此: java集合中又引入了LinkedList ,即链表结构。

二、链表

1、链表的概念及结构

链表是一种 物理存储结构上非连续 存储结构,数据元素的 逻辑顺序 是通过链表中的 引用链接 次序实现的 。

 

 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向或者双向

 2. 带头或者不带头

 3. 循环或者非循环

虽然有这么多的链表的结构,但是我们重点掌握两种:

无头单向非循环链表 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如哈希桶、图的邻接表等等。另外这种结构在笔试面试 中出现很多。

无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。   


2、链表的实现

那么现在,我们可以利用现有的知识,来模拟完成一个链表的实现过程:

public class TwoWayLinkList<T> {
	//首结点
	private Node head;
	//尾结点
	private Node last;
	//链表长度
	private int N;
	//结点类
	private class Node{
		public T item;
		public Node pre;
		public Node next;
		public Node(T item, Node pre, Node next) {
			this.item = item;
			this.pre = pre;
			this.next = next;
		}
	}
	public TwoWayLinkList() {
		//初始化头结点和尾结点
		this.head = new Node(null,null,null);
		this.last = null;
		//初始化元素个数
		this.N = 0;
	}
	//清空链表
	public void clear() {
		this.head.next = null;
		this.last = null;
		this.N = 0;
	}
	//获取链表长度
	public int length() {
		return this.N;
	}
	//判断链表是否为空
	public boolean isEmpty() {
		return N == 0;
	}
	//获取第一个元素
	public T getFirst() {
		if (isEmpty()) {
			return null;
		}
		return head.next.item;
	}
	//获取最后一个元素
	public T getLast() {
		if (isEmpty()) {
			return null;
		}
		return last.item;
	}
	//插入元素t
	public void insert(T t) {
		//如果链表为空
		if (isEmpty()) {
			//创建新结点
			Node newNode = new Node(t,head,null);
			//让新结点称为尾结点
			last = newNode;
			//让头结点指向尾结点
			head.next = last;
		}else {//如果不为空
			//创建新结点
			Node newNode = new Node(t,last,null);
			//让当前尾结点指向新结点
			last.next = newNode;
			//让新结点成为尾结点
			last = newNode;
		}
		//元素个数加一
		this.N++;
	}
	//指定位置插入元素t
	public void insert(int i, T t) {
		//找到i位置的前一个结点
		Node pre = head;
		for (int j = 0; j < i; j++) {
			pre = pre.next;
		}
		//找到i位置的结点
		Node cur = pre.next;
		//创建新结点
		Node newNode = new Node(t,pre,cur);
		//让前一结点的next变为新结点
		pre.next = newNode;
		//让i位置的前一结点变为新结点
		cur.pre = newNode;
		//元素个数加一
		this.N++;
	}
	//获取指定位置的元素
	public T get(int i) {
		Node now = head.next;
		for (int j = 0; j < i; j++) {
			now = now.next;
		}
		return now.item;
	}
	//找到第一次出现t的位置
	public int indexOf(T t) {
		Node now = head;
		for (int i = 0; now.next != null; i++) {
			now = now.next;
			if (now.item.equals(t)) {
				return i;
			}
		}
		return -1;
	}
	//删除i处的元素,并返回其元素
	public T remove(int i) {
		//找到前一个结点
		Node pre = head;
		for (int j = 0; j < i; j++) {
			pre = pre.next;
		}
		//找到i位置的下一个结点
		Node cur = pre.next;
		//找到下一个结点
		Node nextNode = cur.next;
		//让前一个结点的next指向下一个结点
		pre.next = nextNode;
		//让下一个结点的pre指向前一个结点
		nextNode.pre = pre;
		//元素个数减一
		this.N--;
		return cur.item;
	}
}


三、LinkedList的使用

1、什么是LinkedList

关于LinkedList的具体介绍,我们可以看一下它的官方文档;LinkedList 的官方文档

LinkedList的底层是双向链表结构 ( 链表后面介绍 ) ,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

在集合框架中,LinkedList也实现了List接口,具体如下:

那么,这里关于LinkedList,我们要注意几点说明:

1. LinkedList 实现了 List 接口
2. LinkedList 的底层使用了双向链表
3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问
4. LinkedList 的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
5. LinkedList 比较适合任意位置插入的场景

2、LinkedList的使用

(1)LinkedList的构造

 (2) LinkedList的其他常用方法介绍

 那么,接下来我们来看一下如何使用这些方法:

public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1); // add(elem): 表示尾插
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list.size());
        System.out.println(list);
// 在起始位置插入0
        list.add(0, 0); // add(index, elem): 在index位置插入元素elem
        System.out.println(list);
        list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
        list.removeFirst(); // removeFirst(): 删除第一个元素
        list.removeLast(); // removeLast(): 删除最后元素
        list.remove(1); // remove(index): 删除index位置的元素
        System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
        if(!list.contains(1)){
        list.add(0, 1);
        }
        list.add(1);
        System.out.println(list);
        System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
        System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
        int elem = list.get(0); // get(index): 获取指定位置元素
        list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
        System.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
        List<Integer> copy = list.subList(0, 3);
        System.out.println(list);
        System.out.println(copy);
        list.clear(); // 将list中元素清空
        System.out.println(list.size());
        }

(3)LinkedList的遍历

和其它的遍历差不多,LinkedList的遍历也有三种方法:

1、foreach遍历

for (int e:list) {
    System.out.print(e + " ");
}
System.out.println();

2、使用迭代器遍历---正向遍历

ListIterator<Integer> it = list.listIterator();

while(it.hasNext()){
    System.out.print(it.next()+ " ");
}

System.out.println();
3、使用反向迭代器---反向遍历
ListIterator<Integer> rit = list.listIterator(list.size());

while (rit.hasPrevious()){
    System.out.print(rit.previous() +" ");
}

System.out.println();

当然,还有一种是最常见的循环遍历LinkedList,我们在这里就不赘述了


四、ArrayListLinkedList的区别 

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值