1. ArrayList存在的問題
- ArrayList底層使用連續(xù)的空間,任意位置插入或刪除元素時,需要將該位置后序元素整體往前或者往后搬移,故時間復(fù)雜度為O(N)
- 增容需要申請新空間,拷貝數(shù)據(jù),釋放舊空間。會有不小的消耗。
- 增容一般是呈2倍的增長,勢必會有一定的空間浪費。例如當前容量為100,滿了以后增容到200,我們再繼續(xù)插入了5個數(shù)據(jù),后面沒有數(shù)據(jù)插入了,那么就浪費了95個數(shù)據(jù)空間。
由于其底層是一段連續(xù)空間,當在ArrayList任意位置插入或者刪除元素時,就需要將后序元素整體往前或者往后搬移,時間復(fù)雜度為O(n),效率比較低,因此ArrayList不適合做任意位置插入和刪除比較多的場景。因此:java集合中又引入了LinkedList,即鏈表結(jié)構(gòu)。
2. 鏈表定義
2.1 鏈表的概念及結(jié)構(gòu)
鏈表是一種常見的數(shù)據(jù)結(jié)構(gòu),它由一系列節(jié)點組成,每個節(jié)點包含兩部分:數(shù)據(jù)元素 (value) 和指向下一個節(jié)點的指針 ( next 域 )。通過這些節(jié)點的連接,可以形成一個鏈式結(jié)構(gòu)。
【單個節(jié)點】:
節(jié)點(Node):鏈表的基本單元,包含數(shù)據(jù)域和next指針域。數(shù)據(jù)域可以是任意類型的數(shù)據(jù),指針指向下一個節(jié)點。每個節(jié)點都是一個對象。
【節(jié)點組成的鏈式結(jié)構(gòu)】:
結(jié)論: 鏈表是一種物理存儲結(jié)構(gòu)上非連續(xù)存儲結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的引用鏈接次序?qū)崿F(xiàn)的 。
2.2 鏈表的組合類型
1、單向或者雙向
根據(jù)節(jié)點的指針數(shù)量,鏈表可以分為單向鏈表和雙向鏈表。
單向鏈表每個節(jié)點只有一個指針,指向下一個節(jié)點;
雙向鏈表每個節(jié)點有兩個指針,分別指向前一個節(jié)點和后一個節(jié)點。
2、帶頭或者不帶頭
帶頭節(jié)點的鏈表在第一個節(jié)點之前有一個額外的頭節(jié)點,用于標識鏈表的起始位置。(head的value是無意義的,如果想從最開頭插入數(shù)據(jù)時,head是不可變的,從head后面插入)
不帶頭節(jié)點的鏈表則直接以第一個節(jié)點作為鏈表的起始位置。(head是第一個節(jié)點,有value的,如果想從最開頭插入數(shù)據(jù)時,head是可變的,變成新插入的節(jié)點)
3、是否循環(huán):
循環(huán)鏈表是在鏈表的尾部節(jié)點和頭部節(jié)點之間形成一個循環(huán)連接,使得鏈表的最后一個節(jié)點指向頭部節(jié)點。
結(jié)合上述結(jié)構(gòu)可以組合出8中鏈表種類:
單向、帶頭節(jié)點、非循環(huán)鏈表
單向、帶頭節(jié)點、循環(huán)鏈表
單向、不帶頭節(jié)點、非循環(huán)鏈表(重點)
單向、不帶頭節(jié)點、循環(huán)鏈表
雙向、不帶頭節(jié)點、非循環(huán)鏈表(重點)
雙向、不帶頭節(jié)點、循環(huán)鏈表
雙向、帶頭節(jié)點、非循環(huán)鏈表
雙向、帶頭節(jié)點、循環(huán)鏈表
3. 鏈表的實現(xiàn)
3.1 單向、不帶頭、非循環(huán)鏈表的實現(xiàn)
- 節(jié)點的定義
//靜態(tài)內(nèi)部類定義節(jié)點
public static class ListNode {
public int data;//數(shù)據(jù)域
public ListNode next;//指向下一個節(jié)點
public ListNode(int data) {
this.data = data;
}
}
public ListNode head; // 表示當前鏈表的頭節(jié)點 方便找到鏈表的第一個元素
2. 創(chuàng)建鏈表(窮舉法)
public void createLinkedList() {
ListNode node1 = new ListNode(12);
ListNode node2 = new ListNode(23);
ListNode node3 = new ListNode(34);
ListNode node4 = new ListNode(45);
ListNode node5 = new ListNode(56);
//把鏈表的節(jié)點連起來
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
//使用head節(jié)點來記錄鏈表的入口
this.head = node1;
}
使用debug,發(fā)現(xiàn)所有節(jié)點鏈接起來了
之后,可以使用head節(jié)點,獲取所有的節(jié)點
- 打印鏈表
(1) 怎么從一個節(jié)點走向下一個節(jié)點
移動head,走向下一個節(jié)點,使用head = head.next;就可以實現(xiàn)
(2)怎么判斷所有節(jié)點都遍歷完
當head指向null時,則所有節(jié)點遍歷完畢
public void display() {
ListNode cur = this.head;//防止其他方法無法使用head,無法找到鏈表的第一個元素,使用臨時變量cur
while (cur != null) {//當cur指向null時,則所有節(jié)點遍歷完,循環(huán)結(jié)束
System.out.print(cur.value + " ");
cur = cur.next;//不斷移動cur指向下一個節(jié)點
}
System.out.println();
}
- 頭插法添加數(shù)據(jù)
頭插法插入數(shù)據(jù)分為三步:
(1)實例化一個節(jié)點
(2)改變插入結(jié)點的next指向原來鏈表的第一個節(jié)點
(3)改變head指向新節(jié)點
@Override
public void addFirst(int data) {
ListNode listNode = new ListNode(data);
if (this.head == null) {
this.head = listNode;
} else {
listNode.next = this.head;
this.head = listNode;
}
}
- 尾插法添加數(shù)據(jù)
尾插法添加數(shù)據(jù)分為二步:
(1)實例化一個節(jié)點
(2)找到最后一個節(jié)點
(3)將鏈表的原來的最后一個節(jié)點指向新的節(jié)點
public void addLast(int data) {
ListNode listNode = new ListNode(data);
if (this.head == null) {
this.head = listNode;
} else {
ListNode cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = listNode;
}
}
【小總結(jié)】:
- 如果想讓cur指向最后一個節(jié)點的位置,使用cur.next == null
![]()
- 如果想把整個鏈表遍歷完成,那么就是cur == null
![]()
5.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
在鏈表隨意位置插入數(shù)據(jù)分為四步:
(1)找到該節(jié)點要插入位置的前一個節(jié)點cur(前一個節(jié)點的原因,可以由前一個節(jié)點得到后一個節(jié)點,無法無法從后一個節(jié)點得到前一個結(jié)點),讓cur走index-1步。
(2)實例化一個節(jié)點
(3)讓新插入的節(jié)點的next指向要插入位置的節(jié)點,listNode.next = cur.next;
(4)讓要插入位置的前一個節(jié)點cur的next指向新節(jié)點,cur.next = listNode;
注意:(3)和(4)的順序要正確,再插入一個節(jié)點時,一定要先綁定后面這個節(jié)點
public void addIndex(int index, int data) {
if(index<0||index>size()){//要先判斷index的合法性
throw new IndexOutOfBounds("鏈表下標越界");//自定義異常鏈表下標越界
}
if(index == 0){//在第一個位置插入,使用頭插法的方法
addFirst(data);
}else if(index == size()){//在最后一個位置插入,使用尾插法的方法
addLast(data);
}else {//在中間位置插入
ListNode listNode = new ListNode(data);//要插入的節(jié)點
ListNode cur = searchPrev(index);//要插入位置的前一個節(jié)點
listNode.next = cur.next;
cur.next = listNode;
}
}
private ListNode searchPrev(int index){
ListNode cur = this.head;
int count = 0;
while (count != index -1){
cur = cur.next;
count++;
}
return cur;
}
- 刪除第一次出現(xiàn)關(guān)鍵字為key的節(jié)點
刪除鏈表中的節(jié)點分為三步:
(1)找到要刪除節(jié)點的前一個節(jié)點cur
(2) 標記要刪除的要素(也可以不要這一步)ListNode del = cur.next;
(3)讓cur的next指向要刪除的節(jié)點的下一個節(jié)點(要刪除的節(jié)點沒有被引用,JVM會自動回收)
在第一步中找到要刪除節(jié)點的前一個節(jié)點cur,需要找到cur.next.value 和 key的值相等的節(jié)點,如果是引用類型時,現(xiàn)需要使用equals()方法,而不是==
還有一種情況,當要刪除的數(shù)據(jù)是第一個節(jié)點時,只需要讓head指向第二個節(jié)點即可做到刪除第一個節(jié)點
public void remove(int key) {
//當要刪除的數(shù)據(jù)是第一個節(jié)點
if (this.head.value == key) {
this.head = this.head.next;
} else {
//找到前驅(qū)
ListNode cur = getPrev(key);
//判斷前驅(qū)是否存在
if (cur == null) {
System.out.println("要刪除的數(shù)字不存在");
} else {
//刪除節(jié)點
ListNode del = cur.next;
cur.next = del.next;
}
}
}
private ListNode getPrev(int key) {
ListNode cur = this.head;
while (cur != null) {
if (cur.next.value == key) {
return cur;
}
cur = cur.next;
}
return null;
}
- 刪除所有值為key的節(jié)點
刪除所有值為key的節(jié)點分為三步:
(1)遍歷鏈表,找到要刪除節(jié)點cur
(2) 讓cur的上一個節(jié)點指向cur的下一個節(jié)點,刪除cur
(3)繼續(xù)遍歷鏈表,找到所有與key相等的節(jié)點,重復(fù)(1)(2)步驟,直至遍歷完鏈表
還有要考慮一種情況,當要刪除的數(shù)據(jù)是第一個節(jié)點時,只需要讓head指向第二個節(jié)點即可做到刪除第一個節(jié)點
public void removeAllKey(int key) {
if(this.head == null){
return;
}
ListNode prev = this.head;
ListNode cur = this.head.next;
while (cur != null) {
if (cur.value == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (this.head.value == key) {
this.head = this.head.next;
return;
}
}
- 清空鏈表
清空鏈表分為兩種方式:
方式一:直接將head置為null(不推薦)
方式二:遍歷節(jié)點,將每個節(jié)點的數(shù)值域和next域置為空
public void clear() {
ListNode cur = this.head;
while (cur != null) {
ListNode curNext = cur.next;
// cur.value = null;// 引用類型value要置為null
cur.next = null;
cur = curNext;
}
this.head = null;
}
完整代碼:
ILst接口:
public interface IList {
//頭插法
public void addFirst(int data);
//尾插法
public void addLast(int data);
//任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
public void addIndex(int index, int data);
//查找是否包含關(guān)鍵字key是否在單鏈表當中
public boolean contains(int key);
//刪除第一次出現(xiàn)關(guān)鍵字為key的節(jié)點
public void remove(int key);
//刪除所有值為key的節(jié)點
public void removeAllKey(int key);
//得到單鏈表的長度
public int size();
public void clear();
public void display();
}
自定義異常IndexOutOfBounds類
public class IndexOutOfBounds extends RuntimeException{
public IndexOutOfBounds(String message) {
super(message);
}
}
MyLinkedList類:
public class MyLinkedList implements IList {
//靜態(tài)內(nèi)部類
public static class ListNode {
public int value;
public ListNode next;
public ListNode(int value) {
this.value = value;
}
}
public ListNode head; //方便找到鏈表的第一個元素
public void createLinkedList() {
ListNode node1 = new ListNode(12);
ListNode node2 = new ListNode(23);
ListNode node3 = new ListNode(34);
ListNode node4 = new ListNode(45);
ListNode node5 = new ListNode(56);
//把表節(jié)點連起來
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
//使用head節(jié)點來記錄鏈表的入口
this.head = node1;
}
//頭插法
@Override
public void addFirst(int data) {
ListNode listNode = new ListNode(data);
if (this.head == null) {
this.head = listNode;
} else {
listNode.next = this.head;
this.head = listNode;
}
}
//尾插法
@Override
public void addLast(int data) {
ListNode listNode = new ListNode(data);
if (this.head == null) {
this.head = listNode;
} else {
ListNode cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = listNode;
}
}
@Override
public void addIndex(int index, int data) {
if (index < 0 || index > size()) {//要先判斷index的合法性
throw new IndexOutOfBounds("鏈表下標越界");//自定義異常鏈表下標越界
}
if (index == 0) {//在第一個位置插入,使用頭插法的方法
addFirst(data);
} else if (index == size()) {//在最后一個位置插入,使用尾插法的方法
addLast(data);
} else {//在中間位置插入
ListNode listNode = new ListNode(data);//要插入的節(jié)點
ListNode cur = searchPrev(index);//要插入位置的前一個節(jié)點
listNode.next = cur.next;
cur.next = listNode;
}
}
private ListNode searchPrev(int index) {
ListNode cur = this.head;
int count = 0;
while (count != index - 1) {
cur = cur.next;
count++;
}
return cur;
}
@Override
public boolean contains(int key) {
ListNode cur = this.head;
while (cur != null) {
if (cur.value == key) {
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
if (this.head.value == key) {
this.head = this.head.next;
} else {
ListNode cur = getPrev(key);
if (cur == null) {
System.out.println("要刪除的數(shù)字不存在");
} else {
ListNode del = cur.next;
cur.next = del.next;
}
}
}
private ListNode getPrev(int key) {
ListNode cur = this.head;
while (cur != null) {
if (cur.next.value == key) {
return cur;
}
cur = cur.next;
}
return null;
}
@Override
public void removeAllKey(int key) {
if (this.head == null) {
return;
}
ListNode prev = this.head;
ListNode cur = this.head.next;
while (cur != null) {
if (cur.value == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (this.head.value == key) {
this.head = this.head.next;
return;
}
}
@Override
public int size() {
int count = 0;
ListNode cur = this.head;
while (cur != null) {
cur = cur.next;
count++;
}
return count;
}
@Override
public void clear() {
ListNode cur = this.head;
while (cur != null) {
ListNode curNext = cur.next;
// cur.value = null;
cur.next = null;
cur = curNext;
}
this.head = null;
}
@Override
public void display() {
ListNode cur = this.head;
while (cur != null) {
System.out.print(cur.value + " ");
cur = cur.next;
}
System.out.println();
}
}
3.2 雙向、不帶頭節(jié)點、非循環(huán)鏈表的實現(xiàn)
- 結(jié)點的定義
static class ListNode {
public int val;//數(shù)值域
public ListNode next;//指針域,指向下一個節(jié)點
public ListNode prev;//指針域,指向前一個節(jié)點
public ListNode(int val) {
this.val = val;
}
}
2. 頭插法
第一步:讓新插入的next 指向head節(jié)點
第二步:讓head節(jié)點的prev指向新插入的節(jié)點
第三步:讓head指向新插入的節(jié)點
public void addFirst(int data) {
ListNode node = new ListNode(data);
if(head == null){
head = node;
last = node;
}else {
node.next = head;
head.prev = node;
head = node;
}
}
- 尾插法
第一步:讓last節(jié)點的next指向新插入的節(jié)點
第二步:讓新插入的prev指向last節(jié)點
第三步:讓last指向新插入的節(jié)點
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
last.next = node;
node.prev = last;
last = node;
}
}
- 在指定位置插入元素
第一步:判斷要插入的位置是否合法(0<=index<=鏈表的長度)
第二步:判斷要插入的節(jié)點是否在0位置,如果在0位置,那么就使用頭插法
第三步:判斷要插入的節(jié)點是否在最后一個位置,如果在最后一個位置,那么就使用尾插法
第四步:如果要在中間插入,需要插入位置前一個節(jié)點的next域指向新節(jié)點,新節(jié)點的next域指向插入位置的原來的節(jié)點,新節(jié)點的prev域指向需要插入位置前一個節(jié)點,需要插入位置的原來的節(jié)點的prev域指向新節(jié)點,改變四個指針域的指向便可以將新節(jié)點插入需要插入的位置。
public void addIndex(int index, int data) {
int len = size();
//index小于0,或者大于鏈表的長度,插入位置不合法
if (index < 0 || index > len) {
throw new IndexOutOfBounds("要插入的位置小于0或者超過鏈表的長度,該位置不合法");
}
//如果在第一個節(jié)點插入,使用頭插法
if (index == 0) {
addFirst(data);
return;
}
//如果在最后一個節(jié)點插入,使用尾插法
if (index == len) {
addLast(data);
return;
}
//中間位置
//獲取要插入的位置的節(jié)點
ListNode cur = getNode(index);
//新插入的節(jié)點
ListNode node = new ListNode(data);
//在中間位置插入節(jié)點元素
ListNode prev = cur.prev;
prev.next = node;
node.next = cur;
node.prev = prev;
cur.prev = node;
}
public ListNode getNode(int index) {
ListNode cur = this.head;
while (index != 0) {
index--;
cur = cur.next;
}
return cur;
}
5.刪除指定節(jié)點
總共分為三種情況,分別如下:
特殊情況:還需要考慮一種特殊情況,當量表中只存在一個節(jié)點,并且該結(jié)點時需要刪除的節(jié)點的情況
public void remove(int key) {
ListNode cur = this.head;
while (cur != null) {
//判斷是否找到目標節(jié)點
if(cur.val == key) {
//要刪除的節(jié)點是頭節(jié)點的情況
if(cur == head) {
head = head.next;
//鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
if(head == null) {
last = null;
}else {
//鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
head.prev = null;
}
return;
}
//要刪除的節(jié)點是尾節(jié)點的情況
if(cur == last) {
cur.prev.next = cur.next;
last = last.prev;
return;
}
//要刪除的節(jié)點是中間的節(jié)點的情況
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
return;
}else {
cur = cur.next;
}
}
System.out.println("該節(jié)點不存在");
}
- 鏈表置空
方式一:
head=null;
last=null;
方式二:
1.使用cur局部變量,遍歷鏈表,將每一個節(jié)點都置為null
2.將頭節(jié)點和尾節(jié)點置為空
public void clear() {
ListNode cur = this.head;
while (cur != null) {
ListNode curNext = cur.next;
//如果是引用類型,還需要將數(shù)值域置為空
// cur.val = null;
cur.prev = null;
cur.next = null;
cur = curNext;
}
head = null;
last = null;
}
【完整代碼】:
雙向、不帶頭節(jié)點、非循環(huán)鏈表的實現(xiàn):LinkedList .class
public class LinkedList2 implements IList {
static class ListNode {
public int val;//數(shù)值域
public ListNode next;//指針域,指向下一個節(jié)點
public ListNode prev;//指針域,指向前一個節(jié)點
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
@Override
public void addFirst(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
@Override
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
} else {
last.next = node;
node.prev = last;
last = node;
}
}
@Override
public void addIndex(int index, int data) {
int len = size();
//index小于0,或者大于鏈表的長度,插入位置不合法
if (index < 0 || index > len) {
throw new IndexOutOfBounds("要插入的位置小于0或者超過鏈表的長度,該位置不合法");
}
//如果在第一個節(jié)點插入,使用頭插法
if (index == 0) {
addFirst(data);
return;
}
//如果在最后一個節(jié)點插入,使用尾插法
if (index == len) {
addLast(data);
return;
}
//中間位置
//獲取要插入的位置的節(jié)點
ListNode cur = getNode(index);
//新插入的節(jié)點
ListNode node = new ListNode(data);
//在中間位置插入節(jié)點元素
ListNode curPrev = cur.prev;
curPrev.next = node;
node.next = cur;
node.prev = curPrev;
cur.prev = node;
}
public ListNode getNode(int index) {
ListNode cur = this.head;
while (index != 0) {
index--;
cur = cur.next;
}
return cur;
}
@Override
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
@Override
public void remove(int key) {
ListNode cur = this.head;
while (cur != null) {
//判斷是否找到目標節(jié)點
if (cur.val == key) {
//要刪除的節(jié)點是頭節(jié)點的情況
if (cur == head) {
head = head.next;
//鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
if (head == null) {
last = null;
} else {
//鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
head.prev = null;
}
} else if (cur == last) {
//要刪除的節(jié)點是尾節(jié)點的情況
cur.prev.next = cur.next;
last = last.prev;
} else {
//要刪除的節(jié)點是中間的節(jié)點的情況
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}
return;
} else {
cur = cur.next;
}
}
System.out.println("該節(jié)點不存在");
}
@Override
public void removeAllKey(int key) {
ListNode cur = this.head;
while (cur != null) {
//判斷是否找到目標節(jié)點
if (cur.val == key) {
//要刪除的節(jié)點是頭節(jié)點的情況
if (cur == head) {
head = head.next;
//鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
if (head == null) {
last = null;
} else {
//鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
head.prev = null;
}
} else if (cur == last) {
//要刪除的節(jié)點是尾節(jié)點的情況
cur.prev.next = cur.next;
last = last.prev;
} else {
//要刪除的節(jié)點是中間的節(jié)點的情況
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
}
cur = cur.next;
}
System.out.println("該節(jié)點不存在");
}
}
@Override
public int size() {
int count = 0;
ListNode cur = head;
while (cur != null) {
cur = cur.next;
count++;
}
return count;
}
@Override
public void clear() {
ListNode cur = this.head;
while (cur != null) {
ListNode curNext = cur.next;
// cur.val = null;
cur.prev = null;
cur.next = null;
cur = curNext;
}
head = null;
last = null;
}
@Override
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
}
4.LinkedList的使用
4.1 什么是LinkedList
LinkedList 的官方文檔
LinkedList的底層是雙向鏈表結(jié)構(gòu)(鏈表后面介紹),由于鏈表沒有將元素存儲在連續(xù)的空間中,元素存儲在單獨的節(jié)點中,然后通過引用將節(jié)點連接起來了,因此在在任意位置插入或者刪除元素時,不需要搬移元素,效率比較高。
在集合框架中,LinkedList也實現(xiàn)了List接口,具體如下:
【說明】文章來源:http://www.zghlxwxcb.cn/news/detail-832182.html
1. LinkedList實現(xiàn)了List接口
2. LinkedList的底層使用了雙向鏈表
3. LinkedList沒有實現(xiàn)RandomAccess接口,因此LinkedList不支持隨機訪問
4. LinkedList的任意位置插入和刪除元素時效率比較高,時間復(fù)雜度為O(1)
5. LinkedList比較適合任意位置插入的場景文章來源地址http://www.zghlxwxcb.cn/news/detail-832182.html
4.2 LinkedList的使用
4.2.1. LinkedList的構(gòu)造
方法 | 解釋 |
---|---|
LinkedList() | 無參構(gòu)造 |
public LinkedList(Collection<? extends E> c) | 使用其他集合容器中元素構(gòu)造List |
public static void main(String[] args) {
// 構(gòu)造一個空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new java.util.ArrayList<>();
list2.add("JavaSE");
list2.add("JavaWeb");
list2.add("JavaEE");
// 使用ArrayList構(gòu)造LinkedList
List<String> list3 = new LinkedList<>(list2);
}
4.2.2. LinkedList的其他常用方法介紹
方法 | 解釋 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E element) | 將 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 刪除 index 位置元素 |
boolean remove(Object o) | 刪除遇到的第一個 o |
E get(int index) | 獲取下標 index 位置元素 |
E set(int index, E element) | 將下標 index 位置元素設(shè)置為 element |
void clear() | 清空 |
boolean contains(Object o) | 判斷 o 是否在線性表中 |
int indexOf(Object o) | 返回第一個 o 所在下標 |
int lastIndexOf(Object o) | 返回最后一個 o 的下標 |
List subList(int fromIndex, int toIndex) | 截取部分 list |
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(): 刪除第一個元素,內(nèi)部調(diào)用的是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位置的元素設(shè)置為elem
System.out.println(list);
// subList(from, to): 用list中[from, to)之間的元素構(gòu)造一個新的LinkedList返回
List<Integer> copy = list.subList(0, 3);
System.out.println(list);
System.out.println(copy);
list.clear(); // 將list中元素清空
System.out.println(list.size());
}
4.2.3. 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());
//方式一: foreach遍歷
for (int e:list) {
System.out.print(e + " ");
}
System.out.println();
//方式二:for循環(huán)
for (int i = 0;i < list.size();i++) {
System.out.print(list.get(i) + " ");
}
System.out.println();
//方式三:迭代器
// 使用迭代器遍歷---正向遍歷
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
System.out.print(it.next()+ " ");
}
System.out.println();
// 使用反向迭代器---反向遍歷
ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
System.out.print(rit.previous() +" ");
}
System.out.println();
}
5. ArrayList和LinkedList的區(qū)別
不同點 | ArrayList | LinkedList |
---|---|---|
存儲空間上 | 物理上一定連續(xù) | 邏輯上連續(xù),但物理上不一定連續(xù) |
隨機訪問 | 支持O(1) | 不支持:O(N) |
頭插 | 需要搬移元素,效率低O(N) | 只需修改引用的指向,時間復(fù)雜度為O(1) |
插入 | 空間不夠時需要擴容 | 沒有容量的概念 |
應(yīng)用場景 | 元素高效存儲+頻繁訪問 | 任意位置插入和刪除頻繁 |
- 如果是經(jīng)常根據(jù)下標進行查找使用順序表(ArrayList)
- 如果是經(jīng)常插入和刪除操作的可以使用鏈表(LinkedList)
到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!