java数据结构04_LinkedList底层源码分析

LinkedList底层用双向链表实现的存储。

特点:查询效率低,增删效率高,线程不安全。

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。
在这里插入图片描述
所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。
在这里插入图片描述
LinkedList的存储结构图

每个节点都应该有3部分内容:

class Node {
	Node prev;         // 前一个节点
    Object element;   // 本节点保存的数据
    Node next;         // 后一个节点
}

我们查看LinkedList的源码,可以看到里面包含了双向链表的相关代码:
在这里插入图片描述
对LinkedList的操作,其实就是对双链表的操作,下面我们来分析LinkedList的底层实现:

  • 节点类:

节点类很简单,element存放业务数据,previous与next分别存放前后节点的信息(在数据结构中我们通常称之为前后节点的指针)。

public class LinkedList {
	// 链表节点
	private static class Node {
		// 前一个节点
		Node prev;  
		// 本节点保存的数据
        Object element; 
        // 后一个节点
        Node next; 
        // 构造方法
        Node(Node prev, Object element, Node next) {
            this.element = element;
            this.next = next;
            this.prev = prev;
        }
	}
}
  • 私有属性:

LinkedList中之定义了三个属性:

public class LinkedList {
	// 链表首节点
	private Node firstNode;
	// 链表尾节点
	private Node lastNode;
	// 节点个数(元素个数)
	private int size;
	// ...省略链表节点类...
}
  • 构造方法:

LinkedList提供了两个构造方法。

第一个构造方法不接受参数,第二个构造方法接收一个Collection参数c,然后通过addAll方法将c中的元素全部添加到链表中。

public class LinkedList {
	// ...省略私有属性...
    // 无参构造方法
	public LinkedList() {}
	// 有参构造方法
	public LinkedList(Collection c) {
        addAll(c); // 此方法实现省略
    }
	// ...省略链表节点类...
}
  • 添加元素方法:

在链表的尾部追加一个节点,但是要注意判断当前链表是否为空链表。

public class LinkedList {
	// ...省略私有属性和构造方法...
    // 追加元素方法
	public void add(Object element) {
		// 1.把元素内容包装为一个节点对象
		Node newNode = new Node(null, element, null);
		// 2.判断链表中尾节点是否存在,如果不存在则证明链表中还没有节点
		if (lastNode == null) {
			// 2.1设置首尾节点都为newNode
			firstNode = newNode;
			lastNode = newNode;
		}
		// 3.链表中存在节点,那么把尾节点和newNode链接起来
		else {
			// 3.1把尾节点和newNode链接起来
			lastNode.next = newNode;
			newNode.prev = lastNode;
			// 3.2更新尾节点
			lastNode = newNode;
		}
		// 4.链表中节点递增
		size++;
   }
   // ...省略链表节点类...
}
  • 获取元素方法:

获取链表中指定位置的元素,首先判断索引是否合法,然后通过循环来找到对应索引位置的节点,从而拿到节点中存放的内容。

扫描二维码关注公众号,回复: 9493072 查看本文章
public class LinkedList {
	// ...省略私有属性和构造方法...
	// 根据索引获取元素
	public Object get(int index) {
		// 1.检查索引是否合法
		checkElementIndex(index);
		// 2.获取元素节点的内容
		return node(index).element;
	}
	// 检查索引是否合法
	private void checkElementIndex(int index) {
		if (index < 0 || index >= size)
			throw new IndexOutOfBoundsException("索引越界异常");
	}
	// 根据索引获取节点
	private Node node(int index) {
		// 1.如果索引在前半区,则从前往后开始找
		if (index < (size >> 1)) {
			// 1.1准备开始从首节点开始查找
			Node currentNode = firstNode;
			// 1.2从前往后遍历节点,一直到index所在位置
			for (int i = 0; i < index; i++)
				// 1.3找到index索引对应的节点
				currentNode = currentNode.next;
			// 1.4返回找到的节点
			return currentNode;
		}
		// 2.如果索引在后半区,从后往前开始查找
		else {
			// 2.1准备开始从尾节点开始查找
			Node currentNode = lastNode;
			// 2.2从后往前遍历节点,一直到index所在位置
			for (int i = size - 1; i > index; i--)
				// 2.3找到index索引对应的节点
				currentNode = currentNode.prev;
			// 2.4返回找到的节点
			return currentNode;
		}
	}
	// ...省略链表节点类...
}
  • 修改元素方法:

修改链表中指定位置的元素,首先判断索引是否合法,然后通过循环来找到对应索引位置的节点,最后再修改节点中存放的内容。

public class LinkedList {
	// ...省略私有属性和构造方法...
	// 根据索引修改元素
	public Object set(int index, Object element) {
		// 1.检查索引是否合法
		checkElementIndex(index);
		// 2.根据索引获取元素节点
		Node node = node(index);
		// 3.获取节点以前存放的内容
		Object oldVal = node.element;
		// 4.修改节点中存放的内容
		node.element = element;
		// 5.返回节点修改之前的内容
		return oldVal;
	}
    // ...省略链表节点类...
}
  • 插入元素方法:

在链表中指定位置插入元素,首先判断索引是否合法,然后通过循环来找到对应索引位置的节点,最后执行插入操作。

public class LinkedList {
	// ...省略私有属性和构造方法...
	// 插入元素方法
	public void add(int index, Object element) {
		// 1.检查索引是否合法(index和size可以相同)
		isPositionIndex(index); // 此处判断索引是否合法与checkElementIndex不同
		// 2.节点插入操作
		// 2.1如果index和size相等,那么直接就是节点追加
		if (index == size) { // 此操作还包含空链表的情况
			add(element);
		}
		// 2.2如果index和size不相等,则进行插入操作
		else {
			// 2.3根据索引获取链表中的节点
			Node node = node(index);
			// 2.4进行插入操作
			linkBefore(element, node);
			// 2.5链表中节点递增
			size++;
		}
	}
	// 插入节点操作
	public void linkBefore(Object element, Node targetNode) {
		// 1.获取目标节点的上一个节点
		Node preNode = targetNode.prev;
		// 2.把元素内容包装为一个节点对象
		Node newNode = new Node(preNode, element, targetNode);
		// 3.把targetNode节点的prev指向newNode
		targetNode.prev = newNode;
		// 4.把preNode的next指向newNode
		// 4.1如果preNode存在,正常处理
		if (preNode != null) 
			preNode.next = newNode;
		// 4.2如果preNode不存在,则证明newNode为首节点
		else 
			firstNode = newNode;
	}
	// 检查索引是否合法
	private void isPositionIndex(int index) {
		if (index < 0 || index > size)
			throw new IndexOutOfBoundsException("索引越界异常");
	} 
    // ...省略链表节点类...
}
  • 移除元素方法:

根据索引来移除元素,首先先判断索引是否合法,然后通过循环来找到对应索引位置的节点,最后删除该节点。

根据元素来移除元素,首先找到该元素在数组中所在的索引,如果没有找到则证明移除失败,如果找到则删除该节点。

public class LinkedList {
	// ...省略私有属性和构造方法...
	// 移除元素方法
	public Object remove(int index) {
		// 1.检查索引是否合法
		checkElementIndex(index);
		// 2.找到需要移除的节点对象
		Node node = node(index);
		// 3.获取被删除节点中存放的内容
		Object value = node.element;
		// 4.执行移除节点操作
		unlink(node);
		// 5.返回被删除节点的内容
		return value;
	}
	// 根据内容移除元素方法
	public boolean remove(Object o) {
		// 因为集合中可以存放null,所以判断之前需判断元素是否为null
		if(o == null) {
			for(Node node = firstNode; node != null; node = node.next) {
				// 判断元素是否为null
				if(node.element == null) {
					// 执行移除节点操作
					unlink(node);
					return true;
				}
			}
		}
		else {
			for(Node node = firstNode; node != null; node = node.next) {
				// 判断元素是否和传入的元素相同
				if(node.element.equals(o)) {
					// 执行移除节点操作
					unlink(node);
					return true;
				}
			}
		}
		return false;
	}
	// 移除节点操作
	public void unlink(Node node) {
		// 1.获取被移除节点的上一个节点和下一个节点
		Node prev = node.prev;
		Node next = node.next;
		// 2.判断prev是否为空
		if(prev == null) 
			// 2.1prev为null,则证明删除node后,next就为链表的首节点
			firstNode = next;
		else 
			// 2.2prev不为null,则把prev.next设置为next
			prev.next = next;
		// 3.判断next是否为空
		if(next == null)
			// 3.1next为null,则证明删除node后,prev就为链表的尾节点
			lastNode = prev;
		else
			// 3.2next不为null,则把next.prev设置为prev
			next.prev = prev;
		// 4.释放node对象的引用关系
		node.next = null;
		node.prev = null;
		node.element = null;
		// 5.链表中节点递减
		size--;
   } 
   // ...省略链表节点类...
}

ps:如需最新的免费文档资料和教学视频,请添加QQ群(627407545)领取。

发布了35 篇原创文章 · 获赞 0 · 访问量 364

猜你喜欢

转载自blog.csdn.net/zhoujunfeng121/article/details/104535479