0.LinkedList特点
0.1) 删除或新增效率高:不会移动数据元素,只会维护内部节点的关系
0.2) 查询慢: 在其内部没有下标,也即没有索引,需要使用for循环遍历,LInkedList为了提高效率,采用折半查找算法
1.LinkedList的成员属性
1.1 int size --- LinkedList容器现有的节点个数
1.2 Node<E> first;--- 首节点,目的是为了查询方便,从里可以发起查询
1.3 Node<E> last--- 尾节点,目的: (1) 从这里添加元素 (2)也可以从这里发起查询
2.LinkedList的Node类的数据结构
private static class Node<E>{
E data;//节点数据
Node<E> prev;//上一个节点
Node<E> next;//下一个节点
public Node(E data, Node<E> prev, Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
3.LinkedList的数据结构
4.LinkedList添加元素
private void linkLast(E e) {
//创建一个临时变量存储容器的尾节点
final Node<E> l = last;
//创建一个以当前容器尾节点为上一个节点,null为下一个节点的新节点
final Node<E> newNode = new Node<E>(e, l, null);
//把新节点赋值给尾节点
last = newNode;
//如果首节点为空
if(first == null){
first = newNode;
}else{
l.next = newNode;
}
size ++ ;
}
@Override
public boolean add(E e) {
linkLast(e);
return true;
}
5.LinkedList 指定下标index查询元素
5.1) 数组越界检查 index >=0 && index < size
5.2) 根据当前容器所存储的数据大小进行折半查找
index < size >> 1 ====> index < size/2 从容器的首节点开始查询
否则 从容器的尾节点开始查询
@Override
public E get(int index) {
checkElementIndex(index);
return node(index).data;
}
private void checkElementIndex(int index) {
if (index >= 0 && index < size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private Node<E> node(int index) {
//即要想找到下标为index的节点,则只需要找到其相邻节点即可得到该节点的数据
Node<E> node = null;
if(index < size >> 1){
node = first;
for (int i = 0; i < index; i ++){
node = node.next;
}
}else{
node = last;
for (int i = (size -1); i > index; i --){
node = node.prev;
}
}
return node;
}
6.LinkedList在指定下标index位置添加元素
6.1) 数组下标越界检查 index>=0 && index<=size
6.2) 找到下标所在的节点indexNode (index >=0 && index < size 只能找到这个范围的节点)
6.3) 下标所在的节点为新节点newNode的下一个节点--->newNode.next = indexNode
6.4) 下标所在的节点的上一个节点indexOldPrevNode为新节点的上一个节点--->newNode.prev = indexOldPrevNode
6.4.1) 如果indexNode节点的上一个节点为null,则indexNode当前为first节点,此时需要更新首节点的内容
6.5)下标所在的节点的上一个节点的新值为新节点--->indexNode.prev = newNode
6.6 ) 当index = size时 ,直接在容器的末尾添加节点即可
private void linkBefore(E e, Node<E> indexNode) {
//下标所在的节点的上一个节点
Node<E> prev = indexNode.prev;
//利用构造函数创建新节点,
//新节点的上一个节点为下标所在节点的上一个节点
//新节点的下一个节点为下标所在的节点
Node<E> newNode = new Node<>(e,prev,indexNode);
//下标所在节点的新的上一个节点为新节点
indexNode.prev = newNode;
//如果下标所在节点的老的上一个节点为null
//则说明下标所在节点在插入新的节点之前为首节点
//现在插入新节点之后,新节点则称为首节点
if(prev == null){
first = newNode;
}else{
//如果下标所在节点的老的上一个节点不为null
//则说明下标所在的节点在插入新的节点之前不是首节点
//则下标所在的节点的老的上一个节点的下一个节点为新节点
prev.next = newNode;
}
size++;
}
@Override
public boolean add(int index, E e) {
checkElementIndexInPos(index);
if (index == size){
linkLast(e);
}else{
linkBefore(e,node(index));
}
return true;
}
7.LinkedList指定下标删除节点
7.1) 数组越界检查 index >=0 && index < size
7.2) 找出下标所在的节点 indexNode
7.3) indexNode.prev = oldIndexPrevNode; indexNode.next = oldIndexNextNode;
7.4) 如果 oldIndexPrevNode = null,则 first = indexNode ,即删除的为首节点 此时 需要 first = oldIndexNextNode;
7.5) 如果 oldIndexPrevNode != null 则 oldIndexPrevNode.next = oldIndexNextNode;
7.6) 如果 oldIndexNextNode = null,则 last = indexNode,即删除的是尾节点,需要last = oldIndexPrevNode;
7.7) 如果 oldIndexNextNode != null, 则oldIndexNextNode.prev = oldIndexPrevNode;
private E unlink(Node<E> indexNode) {
final E data = indexNode.data;
final Node<E> oldIndexPrevNode = indexNode.prev;
final Node<E> oldIndexNextNode = indexNode.next;
if(oldIndexPrevNode == null){
first = oldIndexNextNode;
}else{
oldIndexPrevNode.next = oldIndexNextNode;
indexNode.prev = null;//GC回收
}
if(oldIndexNextNode == null){
last = oldIndexPrevNode;
}else{
oldIndexNextNode.prev = oldIndexPrevNode;
indexNode.next = null;//GC回收
}
indexNode.data = null;//GC回收
size--;
return data;
}
@Override
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
8.LIinkedList指定元素值删除
8.1) 找出指定元素所对应的节点
8.2) 根据节点删除数据和步骤7一样
@Override
public boolean remove(Object obj) {
if(obj == null){
//从首节点开始遍历,找到复合条件的节点
for(Node<E> x = first; x.next !=null; x = x.next){
if(x.data == null){
unlink(x);
return true;
}
}
}else{
for(Node<E> x = first; x.next !=null; x = x.next){
if(obj.equals(x.data)){
unlink(x);
return true;
}
}
}
return false;
}
9.自己手写LinkedList源码代码以及Junit测试类
package com.roger.collection.impl;
import com.roger.collection.RogerList;
public class RogerLinkedList<E> implements RogerList<E> {
//容器中数据的大小
private int size;
//容器中首节点
private Node<E> first;
//容器中尾节点
private Node<E> last;
private void linkLast(E e) {
//创建一个临时变量存储容器的尾节点
final Node<E> l = last;
//创建一个以当前容器尾节点为上一个节点,null为下一个节点的新节点
final Node<E> newNode = new Node<E>(e, l, null);
//把新节点赋值给尾节点
last = newNode;
//如果首节点为空
if (first == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
}
@Override
public boolean add(E e) {
linkLast(e);
return true;
}
private void linkBefore(E e, Node<E> indexNode) {
//下标所在的节点的上一个节点
Node<E> prev = indexNode.prev;
//利用构造函数创建新节点,
//新节点的上一个节点为下标所在节点的上一个节点
//新节点的下一个节点为下标所在的节点
Node<E> newNode = new Node<>(e,prev,indexNode);
//下标所在节点的新的上一个节点为新节点
indexNode.prev = newNode;
//如果下标所在节点的老的上一个节点为null
//则说明下标所在节点在插入新的节点之前为首节点
//现在插入新节点之后,新节点则称为首节点
if(prev == null){
first = newNode;
}else{
//如果下标所在节点的老的上一个节点不为null
//则说明下标所在的节点在插入新的节点之前不是首节点
//则下标所在的节点的老的上一个节点的下一个节点为新节点
prev.next = newNode;
}
size++;
}
@Override
public boolean add(int index, E e) {
checkElementIndexInPos(index);
if (index == size){
linkLast(e);
}else{
linkBefore(e,node(index));
}
return true;
}
private void checkElementIndexInPos(int index) {
if (index >= 0 && index <= size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@Override
public E get(int index) {
checkElementIndex(index);
return node(index).data;
}
private void checkElementIndex(int index) {
if (index >= 0 && index < size) {
return;
}
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private Node<E> node(int index) {
//即要想找到下标为index的节点,则只需要找到其相邻节点即可得到该节点的数据
Node<E> node = null;
if(index < size >> 1){
node = first;
for (int i = 0; i < index; i ++){
node = node.next;
}
}else{
node = last;
for (int i = (size -1); i > index; i --){
node = node.prev;
}
}
return node;
}
@Override
public int size() {
return size;
}
private E unlink(Node<E> indexNode) {
final E data = indexNode.data;
final Node<E> oldIndexPrevNode = indexNode.prev;
final Node<E> oldIndexNextNode = indexNode.next;
if(oldIndexPrevNode == null){
first = oldIndexNextNode;
}else{
oldIndexPrevNode.next = oldIndexNextNode;
indexNode.prev = null;//GC回收
}
if(oldIndexNextNode == null){
last = oldIndexPrevNode;
}else{
oldIndexNextNode.prev = oldIndexPrevNode;
indexNode.next = null;//GC回收
}
indexNode.data = null;//GC回收
size--;
return data;
}
@Override
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
@Override
public boolean remove(Object obj) {
if(obj == null){
//从首节点开始遍历,找到复合条件的节点
for(Node<E> x = first; x.next !=null; x = x.next){
if(x.data == null){
unlink(x);
return true;
}
}
}else{
for(Node<E> x = first; x.next !=null; x = x.next){
if(obj.equals(x.data)){
unlink(x);
return true;
}
}
}
return false;
}
private static class Node<E> {
E data;//节点数据
Node<E> prev;//上一个节点
Node<E> next;//下一个节点
public Node(E data, Node<E> prev, Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
}
package com.roger.collection;
public interface RogerList<E> {
boolean add(E e);
boolean add(int index, E e);
E get(int index);
int size();
E remove(int index);
boolean remove(Object obj);
}
package com.roger.collection.impl;
import com.roger.collection.RogerList;
import org.junit.Test;
public class RogerLinkerListTest {
@Test
public void testAdd() {
RogerList<String> rogerArrayList = new RogerLinkedList<>();
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testAddByPos() {
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
rogerArrayList.add(0, "Andy");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testRemove(){
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add("Bruce");
rogerArrayList.add(3, "Andy");
rogerArrayList.remove(3);
rogerArrayList.add("Bruce1");
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
@Test
public void testRemoveByObj(){
RogerList<String> rogerArrayList = new RogerArrayList<String>(1);
rogerArrayList.add("Roger");
rogerArrayList.add("Mary");
rogerArrayList.add(null);
rogerArrayList.add("Bruce");
rogerArrayList.add(3, "Andy");
rogerArrayList.add(null);
rogerArrayList.remove("Andy");
rogerArrayList.remove(null);
for (int i = 0; i < rogerArrayList.size(); i++) {
System.out.println(rogerArrayList.get(i));
}
}
}