js 中转换成list集合_程序员:java集合介绍-List,具说很详细,你不来看看?
Java集合介紹
作為一個程序猿,Java集合類可以說是我們在工作中運用最多、最頻繁的類。相比于數組(Array)來說,集合類的長度可變,更加方便開發。
Java集合就像一個容器,可以存儲任何類型的數據,也可以結合泛型來存儲具體的類型對象。在程序運行時,Java集合可以動態的進行擴展,隨著元素的增加而擴大。在Java中,集合類通常存在于java.util包中。
Java集合主要由2大體系構成,分別是Collection體系和Map體系,其中Collection和Map分別是2大體系中的頂層接口。
Collection主要有三個子接口,分別為List(列表)、Set(集)、Queue(隊列)。其中,List、Queue中的元素有序可重復,而Set中的元素無序不可重復。
List中主要有ArrayList、LinkedList兩個實現類;Set中則是有HashSet實現類;而Queue是在JDK1.5后才出現的新集合,主要以數組和鏈表兩種形式存在。
Map同屬于java.util包中,是集合的一部分,但與Collection是相互獨立的,沒有任何關系。Map中都是以key-value的形式存在,其中key必須唯一,主要有HashMap、HashTable、treeMap三個實現類。
List介紹
在Collection中,List集合是有序的,可對其中每個元素的插入位置進行精確地控制,可以通過索引來訪問元素,遍歷元素。
在List集合中,我們常用到ArrayList和LinkedList這兩個類。
ArrayList集合
1,ArrayList底層通過數組實現,隨著元素的增加而動態擴容。
2,ArrayList是Java集合框架中使用最多的一個類,是一個數組隊列,線程不安全集合。
它繼承于AbstractList,實現了List, RandomAccess, Cloneable, Serializable接口。
ArrayList集合的特點:
- 容量不固定,隨著容量的增加而動態擴容(閾值基本不會達到)
- 有序集合(插入的順序==輸出的順序)
- 插入的元素可以為null
- 增刪改查效率更高(相對于LinkedList來說)
- 線程不安全
ArrayList的底層數據結構:
LinkedList集合
它繼承AbstractSequentialList,實現了List, Deque, Cloneable, Serializable接口。
- LinkedList實現List,得到了List集合框架基礎功能;
- LinkedList實現Deque,Deque 是一個雙向隊列,也就是既可以先入先出,又可以先入后出,說簡單點就是既可以在頭部添加元素,也可以在尾部添加元素;
- LinkedList實現Cloneable,得到了clone()方法,可以實現克隆功能;
- LinkedList實現Serializable,表示可以被序列化,通過序列化去傳輸,典型的應用就是hessian協議。
List常用方法
A:添加功能
boolean add(E e):向集合中添加一個元素
void add(int index, E element):在指定位置添加元素
boolean addAll(Collection extends E> c):向集合中添加一個集合的元素。
B:刪除功能
void clear():刪除集合中的所有元素
E remove(int index):根據指定索引刪除元素,并把刪除的元素返回
boolean remove(Object o):從集合中刪除指定的元素
boolean removeAll(Collection> c):從集合中刪除一個指定的集合元素。
C:修改功能
E set(int index, E element):把指定索引位置的元素修改為指定的值,返回修改前的值。
D:獲取功能
E get(int index):獲取指定位置的元素
Iterator iterator():就是用來獲取集合中每一個元素。
E:判斷功能
boolean isEmpty():判斷集合是否為空。
boolean contains(Object o):判斷集合中是否存在指定的元素。
boolean containsAll(Collection> c):判斷集合中是否存在指定的一個集合中的元素。
F:長度功能
int size():獲取集合中的元素個數
G:把集合轉換成數組
Object[] toArray():把集合變成數組。
ArrayList 基本操作
public class ArrayListTest {
public static void main(String[] agrs){
//創建ArrayList集合:
List list = new ArrayList();
System.out.println("ArrayList集合初始化容量:"+list.size());
// ArrayList集合初始化容量:0
//添加功能:
list.add("Hello");
list.add("world");
list.add(2,"!");
System.out.println("ArrayList當前容量:"+list.size());
// ArrayList當前容量:3
//修改功能:
list.set(0,"my");
list.set(1,"name");
System.out.println("ArrayList當前內容:"+list.toString());
// ArrayList當前內容:[my, name, !]
//獲取功能:
String element = list.get(0);
System.out.println(element);
// my
//迭代器遍歷集合:(ArrayList實際的跌倒器是Itr對象)
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
/**
my
name
!
*/
//for循環迭代集合:
for(String str:list){
System.out.println(str);
}
/**
my
name
!
*/
//判斷功能:
boolean isEmpty = list.isEmpty();
boolean isContain = list.contains("my");
//長度功能:
int size = list.size();
//把集合轉換成數組:
String[] strArray = list.toArray(new String[]{});
//刪除功能:
list.remove(0);
list.remove("world");
list.clear();
System.out.println("ArrayList當前容量:"+list.size());
// ArrayList當前容量:0
}
}
LinkedList 基本操作
public class LinkedListTest {
public static void main(String[] agrs){
List linkedList = new LinkedList();
System.out.println("LinkedList初始容量:"+linkedList.size());
// LinkedList初始容量:0
//添加功能:
linkedList.add("my");
linkedList.add("name");
linkedList.add("is");
linkedList.add("jiaboyan");
System.out.println("LinkedList當前容量:"+ linkedList.size());
// LinkedList當前容量:4
//修改功能:
linkedList.set(0,"hello");
linkedList.set(1,"world");
System.out.println("LinkedList當前內容:"+ linkedList.toString());
// LinkedList當前內容:[hello, world, is, jiaboyan]
//獲取功能:
String element = linkedList.get(0);
System.out.println(element);
// hello
//遍歷集合:(LinkedList實際的迭代器是ListItr對象)
Iterator iterator = linkedList.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
/**
hello
world
is
jiaboyan
*/
//for循環迭代集合:
for(String str:linkedList){
System.out.println(str);
}
/**
hello
world
is
jiaboyan
*/
//判斷功能:
boolean isEmpty = linkedList.isEmpty();
boolean isContains = linkedList.contains("jiaboyan");
//長度功能:
int size = linkedList.size();
//刪除功能:
linkedList.remove(0);
linkedList.remove("jiaboyan");
linkedList.clear();
System.out.println("LinkedList當前容量:" + linkedList.size());
// LinkedList當前容量:0
}
}
ArrayList和LinkedList比較
(1)元素新增性能比較
網上很多說的是,在做新增操作時,ArrayList的效率遠不如LinkedList,因為Arraylist底層時數組實現的,在動態擴容時,性能會有所損耗,而LinkedList不存在數組擴容機制,所以LinkedList的新增性能較好。究竟時哪個好呢,我們用實踐得到結果。
public class ListTest{
// 迭代次數
public static int ITERATION_NUM = 100000;
public static void main(String[] args) {
try{
insertPerformanceCompare();
}catch (Exception e){}
}
//新增性能比較:
public static void insertPerformanceCompare() throws InterruptedException {
Thread.sleep(5000);
System.out.println("LinkedList新增測試開始");
long start = System.nanoTime();
List linkedList = new LinkedList();
for (int x = 0; x < ITERATION_NUM; x++) {
linkedList.add(x);
}
long end = System.nanoTime();
System.out.println(end - start);
System.out.println("ArrayList新增測試開始");
start = System.nanoTime();
List arrayList = new ArrayList();
for (int x = 0; x < ITERATION_NUM; x++) {
arrayList.add(x);
}
end = System.nanoTime();
System.out.println(end - start);
}
}
測試結果:
第一次:
LinkedList新增測試開始
10873720
ArrayList新增測試開始
5535277
第二次:
LinkedList新增測試開始
13097503
ArrayList新增測試開始
6046139
第三次:
LinkedList新增測試開始
12004669
ArrayList新增測試開始
6509783
結果與預想的有些不太一樣,ArrayList的新增性能并不低。
原因:
? 可能是經過JDK近幾年的更新發展,對于數組復制的實現進行了優化,以至于ArrayList的性能也得到了提高。
(2)元素獲取比較
由于LinkedList是鏈表結構,沒有角標的概念,沒有實現RandomAccess接口,不具備隨機元素訪問功能,所以在get方面表現的差強人意,ArrayList再一次完勝。
public class ListTest {
//迭代次數,集合大小:
public static int ITERATION_NUM = 100000;
public static void main(String[] agrs) {
try{
getPerformanceCompare();
}catch (Exception e){}
}
//獲取性能比較:
public static void getPerformanceCompare()throws InterruptedException {
Thread.sleep(5000);
//填充ArrayList集合:
List arrayList = new ArrayList();
for (int x = 0; x < ITERATION_NUM; x++) {
arrayList.add(x);
}
//填充LinkedList集合:
List linkedList = new LinkedList();
for (int x = 0; x < ITERATION_NUM; x++) {
linkedList.add(x);
}
//創建隨機數對象:
Random random = new Random();
System.out.println("LinkedList獲取測試開始");
long start = System.nanoTime();
for (int x = 0; x < ITERATION_NUM; x++) {
int j = random.nextInt(x + 1);
int k = linkedList.get(j);
}
long end = System.nanoTime();
System.out.println(end - start);
System.out.println("ArrayList獲取測試開始");
start = System.nanoTime();
for (int x = 0; x < ITERATION_NUM; x++) {
int j = random.nextInt(x + 1);
int k = arrayList.get(j);
}
end = System.nanoTime();
System.out.println(end - start);
}
}
測試結果:
第一次:
LinkedList獲取測試開始
8190063123
ArrayList獲取測試開始
8590205
第二次:
LinkedList獲取測試開始
8100623160
ArrayList獲取測試開始
11948919
第三次:
LinkedList獲取測試開始
8237722833
ArrayList獲取測試開始
6333427
從結果可以看出,ArrayList在隨機訪問方面表現的十分優秀,比LinkedList強了很多。
原因:
? 這主要是LinkedList的代碼實現所致,每一次獲取都是從頭開始遍歷,一個個節點去查找,每查找一次就遍歷一次,所以性能自然得不到提升。
ArrayList源碼分析
接下來,我們對ArrayList集合進行源碼分析,其中先來幾個問題:
(1)ArrayList的構造
(2)增刪改查的實現
(3)迭代器——modCount
(4)為什么數組對象要使用transient修飾符
(5)System.arraycopy()參數含義 Arrays.copyOf()參數含義
我們通過上面幾個問題,對ArrayList的源碼進行一步一步的解析。
ArrayList構造器
1,在JDK1.7版本中,ArrayList的無參構造方法并沒有生成容量為10的數組。
2,elementData對象是ArrayList集合底層保存元素的實現。
3,size屬性記錄了ArrayList集合中實際元素的個數。
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable {
//實現Serializable接口,生成的序列版本號:
private static final long serialVersionUID = 8683452581122892189L;
//ArrayList初始容量大小:在無參構造中不使用了
private static final int DEFAULT_CAPACITY = 10;
//空數組對象:初始化中默認賦值給elementData
private static final Object[] EMPTY_ELEMENTDATA = {};
//ArrayList中實際存儲元素的數組:
private transient Object[] elementData;
//集合實際存儲元素長度:
private int size;
//ArrayList有參構造:容量大小
public ArrayList(int initialCapacity) {
//即父類構造:protected AbstractList() {}空方法
super();
//如果傳遞的初始容量小于0 ,拋出異常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
//初始化數據:創建Object數組
this.elementData = new Object[initialCapacity];
}
//ArrayList無參構造:
public ArrayList() {
//即父類構造:protected AbstractList() {}空方法
super();
//初始化數組:空數組,容量為0
this.elementData = EMPTY_ELEMENTDATA;
}
//ArrayList有參構造:Java集合
public ArrayList(Collection extends E> c) {
//將集合轉換為數組:
elementData = c.toArray();
//設置數組的長度:
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
}
add() 添加
1,在JDK1.7當中,當第一個元素添加時,ensureCapacityInternal()方法會計算ArrayList的擴容大小,默認為10。
2,其中grow()方法最為重要,如果需要擴容,那么擴容后的大小是原來的1.5倍,實際上最終調用了Arrays.copyOf()方法得以實現。
//添加元素e
public boolean add(E e) {
ensureCapacityInternal(size + 1);
//將對應角標下的元素賦值為e:
elementData[size++] = e;
return true;
}
//得到最小擴容量
private void ensureCapacityInternal(int minCapacity) {
//如果此時ArrayList是空數組,則將最小擴容大小設置為10:
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判斷是否需要擴容:
ensureExplicitCapacity(minCapacity);
}
//判斷是否需要擴容
private void ensureExplicitCapacity(int minCapacity) {
//操作數+1
modCount++;
//判斷最小擴容容量-數組大小是否大于0:
if (minCapacity - elementData.length > 0)
//擴容:
grow(minCapacity);
}
//ArrayList動態擴容的核心方法:
private void grow(int minCapacity) {
//獲取現有數組大小:
int oldCapacity = elementData.length;
//位運算,得到新的數組容量大小,為原有的1.5倍:
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新擴容的大小依舊小于傳入的容量值,那么將傳入的值設為新容器大小:
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新容器大小,大于ArrayList最大長度:
if (newCapacity - MAX_ARRAY_SIZE > 0)
//計算出最大容量值:
newCapacity = hugeCapacity(minCapacity);
//數組復制:
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 計算ArrayList最大容量:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
//如果新的容量大于MAX_ARRAY_SIZE。將會調用hugeCapacity將int的最大值賦給newCapacity:
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
remove() 刪除
1,remove(int index)是針對于角標來進行刪除,不需要去遍歷整個集合,效率更高。
2,而remove(Object o)是針對于對象來進行刪除,需要遍歷整個集合進行equals()方法比對,所以效率較低。
3,不過,無論是哪種形式的刪除,最終都會調用System.arraycopy()方法進行數組復制操作,所以效率都會受到影響。
//在ArrayList的移除index位置的元素
public E remove(int index) {
//檢查角標是否合法:不合法拋異常
rangeCheck(index);
//操作數+1:
modCount++;
//獲取當前角標的value:
E oldValue = elementData(index);
//獲取需要刪除元素 到最后一個元素的長度,也就是刪除元素后,后續元素移動的個數;
int numMoved = size - index - 1;
//如果移動元素個數大于0 ,也就是說刪除的不是最后一個元素:
if (numMoved > 0)
// 將elementData數組index+1位置開始拷貝到elementData從index開始的空間
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//size減1,并將最后一個元素置為null
elementData[--size] = null;
//返回被刪除的元素:
return oldValue;
}
//在ArrayList的移除對象為O的元素,不返回被刪除的元素:
public boolean remove(Object o) {
//如果o==null,則遍歷集合,判斷哪個元素為null:
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
//快速刪除,和前面的remove(index)一樣的邏輯
fastRemove(index);
return true;
}
} else {
//同理:
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//快速刪除:
private void fastRemove(int index) {
//操作數+1
modCount++;
//獲取需要刪除元素 到最后一個元素的長度,也就是刪除元素后,后續元素移動的個數;
int numMoved = size - index - 1;
//如果移動元素個數大于0 ,也就是說刪除的不是最后一個元素:
if (numMoved > 0)
// 將elementData數組index+1位置開始拷貝到elementData從index開始的空間
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//size減1,并將最后一個元素置為null
elementData[--size] = null;
}
set() 修改
由于ArrayList實現了RandomAccess,所以具備了隨機訪問特性,調用elementData()可以獲取到對應元素的值。
//設置index位置的元素值了element,返回該位置的之前的值
public E set(int index, E element) {
//檢查index是否合法:判斷index是否大于size
rangeCheck(index);
//獲取該index原來的元素:
E oldValue = elementData(index);
//替換成新的元素:
elementData[index] = element;
//返回舊的元素:
return oldValue;
}
get() 獲取元素
通過elementData()方法獲取對應角標元素,在返回時候進行類型轉換。
//獲取index位置的元素
public E get(int index) {
//檢查index是否合法:
rangeCheck(index);
//獲取元素:
return elementData(index);
}
//獲取數組index位置的元素:返回時類型轉換
E elementData(int index) {
return (E) elementData[index];
}
modCount含義
1,在Itr迭代器初始化時,將ArrayList的modCount屬性的值賦值給了expectedModCount。
2,通過上面的例子中,我們可以知道當進行增刪改時,modCount會隨著每一次的操作而+1,modCount記錄了ArrayList內發生改變的次數。
3,當迭代器在迭代時,會判斷expectedModCount的值是否還與modCount的值保持一致,如果不一致則拋出異常。
AbstractList類當中定義的變量:
protected transient int modCount = 0;
1
ArrayList獲取迭代器對象:
//返回一個Iterator對象,Itr為ArrayList的一個內部類,其實現了Iterator接口
public Iterator iterator() {
return new java.util.ArrayList.Itr();
}
迭代器實現:
//Itr實現了Iterator接口,是ArrayList集合的迭代器對象
private class Itr implements Iterator {
//類似游標,指向迭代器下一個值的位置
int cursor;
//迭代器最后一次取出的元素的位置。
int lastRet = -1;
//Itr初始化時候ArrayList的modCount的值。
int expectedModCount = modCount;
//利用游標,與size之前的比較,判斷迭代器是否還有下一個元素
public boolean hasNext() {
return cursor != size;
}
//迭代器獲取下一個元素:
public E next() {
//檢查modCount是否改變:
checkForComodification();
int i = cursor;
//游標不會大于等于集合的長度:
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = java.util.ArrayList.this.elementData;
//游標不會大于集合中數組的長度:
if (i >= elementData.length)
throw new ConcurrentModificationException();
//游標+1
cursor = i + 1;
//取出元素:
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
//檢查modCount是否改變:防止并發操作集合
checkForComodification();
try {
//刪除這個元素:
java.util.ArrayList.this.remove(lastRet);
//刪除后,重置游標,和當前指向元素的角標 lastRet
cursor = lastRet;
lastRet = -1;
//重置expectedModCount:
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//并發檢查:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
transient 修飾符
1,當我們序列化對象時,如果對象中某個屬性不進行序列化操作,那么在該屬性前添加transient修飾符即可實現。例如:
private transient Object[] elementData;
那么問題來了,為什么ArrayList不想對elementData屬性進行序列化呢?elementData可是集合中保存元素的數組啊,如果不序列化elementData屬性,那么在反序列化時候,豈不是丟失了原先的元素?
原因是 ArrayList在添加元素時,可能會對elementData數組進行擴容操作,而擴容后的數組可能并沒有全部保存元素。
例如:我們創建了new Object[10]數組對象,但是我們只向其中添加了1個元素,而剩余的9個位置并沒有添加元素。當我們進行序列化時,并不會只序列化其中一個元素,而是將整個數組進行序列化操作,那些沒有被元素填充的位置也進行了序列化操作,間接的浪費了磁盤的空間,以及程序的性能。
所以,ArrayList才會在elementData屬性前加上transient修飾符。
接下來,我們來看下ArrayList的writeObject()、readObject()
//序列化寫入:
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
int expectedModCount = modCount;
s.defaultWriteObject();
s.writeInt(size);
for (int i=0; i
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
// 序列化讀取:
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
s.defaultReadObject();
s.readInt();
if (size > 0) {
ensureCapacityInternal(size);
Object[] a = elementData;
for (int i=0; i
a[i] = s.readObject();
}
}
}
1,ArrayList在序列化時會調用writeObject(),直接將elementData寫入ObjectOutputStream。
2,反序列化時則調用readObject(),從ObjectInputStream獲取elementData。
Arrays.copyOf() 數組擴容
該方法在內部創建了一個新數組,底層實現是調用System.arraycopy()。
public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
original - 要復制的數組
newLength - 要返回的副本的長度
newType - 要返回的副本的類型
System.arraycopy()
該方法是用了native關鍵字,調用的為C++編寫的底層函數。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
src - 源數組。
srcPos - 源數組中的起始位置。
dest - 目標數組。
destPos - 目標數據中的起始位置。
length - 要復制的數組元素的數量。
LinkedList源碼分析
看到網上都說LinkedList是一個環形鏈表結構,頭尾相連。但,當我開始看源碼的時候,發現并不是環形鏈表,是一個直線型鏈表結構,我一度以為是我理解有誤。后來發現,JDK1.7之前的版本是環形鏈表,而到了JDK1.7以后進行了優化,變成了直線型鏈表結構。
集合基礎結構
1,在LinkedList中,內部類Node對象最為重要,它組成了LinkedList集合的整個鏈表,分別指向上一個點、下一個結點,存儲著集合中的元素。
2,成員變量中,first表明是頭結點,last表明是尾結點。
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable {
//LinkedList的元素個數:
transient int size = 0;
//LinkedList的頭結點:Node內部類
transient java.util.LinkedList.Node first;
//LinkedList尾結點:Node內部類
transient java.util.LinkedList.Node last;
//空實現:頭尾結點均為null,鏈表不存在
public LinkedList() {
}
//調用添加方法:
public LinkedList(Collection extends E> c) {
this();
addAll(c);
}
//節點的數據結構,包含前后節點的引用和當前節點
private static class Node {
//結點元素:
E item;
//結點后指針
java.util.LinkedList.Node next;
//結點前指針
java.util.LinkedList.Node prev;
Node(java.util.LinkedList.Node prev, E element, java.util.LinkedList.Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
add() 添加
LinkedList的添加方法,主要分為2種,一是直接添加一個元素,二是在指定角標下添加一個元素。
(1)add(E e)底層調用linkLast(E e)方法,就是在鏈表的最后面插入一個元素。
(2)add(int index, E element),插入的角標如果==size,則插入到鏈表最后;否則,按照角標大小插入到對應位置。
//添加元素:添加到最后一個結點;
public boolean add(E e) {
linkLast(e);
return true;
}
//last節點插入新元素:
void linkLast(E e) {
//將尾結點賦值個體L:
final java.util.LinkedList.Node l = last;
//創建新的結點,將新節點的前指針指向l:
final java.util.LinkedList.Node newNode = new java.util.LinkedList.Node<>(l, e, null);
//新節點置為尾結點:
last = newNode;
//如果尾結點l為null:則是空集合新插入
if (l == null)
//頭結點也置為 新節點:
first = newNode;
else
//l節點的后指針指向新節點:
l.next = newNode;
//長度+1
size++;
//操作數+1
modCount++;
}
//向對應角標添加元素:
public void add(int index, E element) {
//檢查傳入的角標 是否正確:
checkPositionIndex(index);
//如果插入角標==集合長度,則插入到集合的最后面:
if (index == size)
linkLast(element);
else
//插入到對應角標的位置:獲取此角標下的元素先
linkBefore(element, node(index));
}
//在succ前插入 新元素e:
void linkBefore(E e, java.util.LinkedList.Node succ) {
//獲取被插入元素succ的前指針元素:
final java.util.LinkedList.Node pred = succ.prev;
//創建新增元素節點,前指針 和 后指針分別指向對應元素:
final java.util.LinkedList.Node newNode = new java.util.LinkedList.Node<>(pred, e, succ);
succ.prev = newNode;
//succ的前指針元素可能為null,為null的話說明succ是頭結點,則把新建立的結點置為頭結點:
if (pred == null)
first = newNode;
else
//succ前指針不為null,則將前指針的結點的后指針指向新節點:
pred.next = newNode;
//長度+1
size++;
//操作數+1
modCount++;
}
對于LinkedList集合增加元素來說,可以簡單的概括為以下幾點:
1,將添加的元素轉換為LinkedList的Node對象節點。
2,增加該Node節點的前后引用,即該Node節點的prev、next屬性,讓其分別指向哪一個節點)。
3,修改該Node節點的前后Node節點中pre/next屬性,使其指向該節點。
remove() 刪除元素
LinkedList的刪除也提供了2種形式,其一是通過角標刪除元素,其二就是通過對象刪除元素;不過,無論哪種刪除,最終調用的都是unlink來實現的。
//刪除對應角標的元素:
public E remove(int index) {
checkElementIndex(index);
//node()方法通過角標獲取對應的元素,在后面介紹
return unlink(node(index));
}
//刪除LinkedList中的元素,可以刪除為null的元素,逐個遍歷LinkedList的元素,重復元素只刪除第一個:
public boolean remove(Object o) {
//如果刪除元素為null:
if (o == null) {
for (java.util.LinkedList.Node x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
//如果刪除元素不為null:
for (java.util.LinkedList.Node x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
//移除LinkedList結點:remove()方法中調用
E unlink(java.util.LinkedList.Node x) {
//獲取被刪除結點的元素E:
final E element = x.item;
//獲取被刪除元素的后指針結點:
final java.util.LinkedList.Node next = x.next;
//獲取被刪除元素的前指針結點:
final java.util.LinkedList.Node prev = x.prev;
//被刪除結點的 前結點為null的話:
if (prev == null) {
//將后指針指向的結點置為頭結點
first = next;
} else {
//前置結點的 尾結點指向被刪除的next結點;
prev.next = next;
//被刪除結點前指針置為null:
x.prev = null;
}
//對尾結點同樣處理:
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
set() 修改元素
1,LinkedList的set(int index, E element)方法與add(int index,E element)的設計思路基本一致,都是創建新Node節點,插入到對應的角標下,修改前后節點的prev、next屬性。
2,其中,node(int index)方法至關重要,通過對應角標獲取到對應的集合元素。
3,可以看到,node()中是根據角標的大小是選擇從前遍歷還是從后遍歷整個集合。也可以間接的說明,LinkedList在隨機獲取元素時性能很低,每次的獲取都得從頭或者從尾遍歷半個集合。
//設置對應角標的元素:
public E set(int index, E element) {
checkElementIndex(index);
//通過node()方法,獲取到對應角標的元素:
java.util.LinkedList.Node x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//獲取對應角標所屬于的結點:
java.util.LinkedList.Node node(int index) {
//位運算:如果位置索引小于列表長度的一半,則從頭開始遍歷;否則,從后開始遍歷;
if (index < (size >> 1)) {
java.util.LinkedList.Node x = first;
//從頭結點開始遍歷:遍歷的長度就是index的長度,獲取對應的index的元素
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//從集合尾結點遍歷:
java.util.LinkedList.Node x = last;
//同樣道理:
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
get() 獲取元素
get(int index)
在通過node(int index)獲取到對應節點后,返回節點中的item屬性,該屬性就是我們所保存的元素。
//獲取相應角標的元素:
public E get(int index) {
//檢查角標是否正確:
checkElementIndex(index);
//獲取角標所屬結點的 元素值:
return node(index).item;
}
迭代器
在LinkedList中,并沒有自己實現iterator()方法,而是使用其父類AbstractSequentialList的iterator()方法。
List linkedList = new LinkedList();
Iterator iterator = linkedList.iterator();
父類AbstractSequentialList中的 iterator():
public abstract class AbstractSequentialList extends AbstractList {
public Iterator iterator() {
return listIterator();
}
}
父類AbstractList中的 listIterator():
public abstract class AbstractList extends AbstractCollection implements List {
public ListIterator listIterator() {
return listIterator(0);
}
}
LinkedList中的 listIterator():
public ListIterator listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator {}
總結
本篇文章簡單介紹了Java中List集合,由于純手打,難免會有紕漏,如果發現錯誤的地方,請第一時間告訴我,這將是我進步的一個很重要的環節。
總結
以上是生活随笔為你收集整理的js 中转换成list集合_程序员:java集合介绍-List,具说很详细,你不来看看?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《AutoCAD 2016中文版从入门到
- 下一篇: Linux网络/firewalld和ne