List接口簡(jiǎn)介
??List是Collection的子接口,其最大的特點(diǎn)是允許保存有重復(fù)元素?cái)?shù)據(jù),該接口的定義如下:
public interface List<E> extends Collection<E>
??但是需要清楚的是List子接口對(duì)于Collection接口進(jìn)行了方法擴(kuò)充。
-
獲取指定索引上的數(shù)據(jù):
E get?(int index)
- 修改指定索引上的數(shù)據(jù):
E set?(int index, E element)
- 返回ListIterator接口對(duì)象:
ListIterator<E> listIterator()
??但是List本身依然是一個(gè)接口,接口要想使用則一定要子類來(lái)完成定義夯到,在List子接口中有三個(gè)常用子類:ArrayList枣申、Vector售葡、LinkedList。
??從JDK1.9開始忠藤,List接口中追加有一些static方法挟伙,以方便用戶的處理。
范例:觀察List的靜態(tài)方法
import java.util.Arrays;
import java.util.List;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all = List.of("hello", "world", "你好", "MLDN", "餓了么模孩?");
Object[] result = all.toArray();
System.out.println(Arrays.toString(result));//[hello, world, 你好, MLDN, 餓了么尖阔?]
}
}
這些操作方法并不是List的傳統(tǒng)用法,是在JDK1.9后添加的新功能榨咐。
ArrayList子類
??ArrayList是List子接口中使用最多的一個(gè)子類介却,但是這個(gè)子類在使用時(shí)也是有前提要求的,所以本次來(lái)對(duì)這個(gè)類的相關(guān)定義以及源代碼組成進(jìn)行分析块茁,在Java里面ArrayList類的定義如下:
public class ArrayList<E> extends AbstractList<E> mplements List<E>, RandomAccess, Cloneable, Serializable
范例:使用ArrayList實(shí)例化List父接口
import java.util.ArrayList;
import java.util.List;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all=new ArrayList();//為L(zhǎng)ist父接口進(jìn)行實(shí)例化
all.add("hello");
all.add("hello");//重復(fù)數(shù)據(jù)
all.add("wolrd");
all.add("MLDN");
System.out.println(all);//[hello, hello, wolrd, MLDN]
}
}
通過(guò)本程序可以發(fā)現(xiàn)List的存儲(chǔ)特征:
- 保存的順序就是其存儲(chǔ)的順序齿坷;
- List集合里面允許存在有重復(fù)數(shù)據(jù);
??在以上的程序中雖然實(shí)現(xiàn)了集合的輸出数焊,但是這種輸出的操作時(shí)直接利用了每一個(gè)類提供的toString()方法實(shí)現(xiàn)的永淌,為了方便的進(jìn)行輸出處理,在JDK1.8后Iterable定義有一個(gè)forEach方法佩耳,方法定義如下:
- 輸出支持:
default void forEach?(Consumer<? super T> action)
范例:利用forEach()方法進(jìn)行輸出(非標(biāo)準(zhǔn)輸出)
import java.util.ArrayList;
import java.util.List;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all = new ArrayList();//為L(zhǎng)ist父接口進(jìn)行實(shí)例化
all.add("hello");
all.add("hello");//重復(fù)數(shù)據(jù)
all.add("wolrd");
all.add("MLDN");
all.forEach((s -> {
System.out.print(s + "仰禀、");
}));
//hello、hello蚕愤、wolrd答恶、MLDN饺蚊、
}
}
需要注意的是,此種輸出并不是正常開發(fā)情況下要考慮的操作形式悬嗓。
范例:觀察List集合的其它操作方法
import java.util.ArrayList;
import java.util.List;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all = new ArrayList();//為L(zhǎng)ist父接口進(jìn)行實(shí)例化
System.out.printf("集合是否為空污呼?%s、集合元素個(gè)數(shù):%s\n",all.isEmpty(),all.size());//集合是否為空包竹?true燕酷、集合元素個(gè)數(shù):0
//集合是否為空?true周瞎、集合元素個(gè)數(shù):0
all.add("hello");
all.add("hello");//重復(fù)數(shù)據(jù)
all.add("wolrd");
all.add("MLDN");
all.forEach((s -> {
System.out.print(s + "苗缩、");
}));
//hello、hello声诸、wolrd酱讶、MLDN、
}
}
??如果以方法的功能為例彼乌,那么ArrayList中操作支持與之前編寫的鏈表形式是非常相似的泻肯,但是它并不是使用鏈表來(lái)實(shí)現(xiàn)的,通過(guò)類名稱實(shí)際上就已經(jīng)可以清楚的發(fā)現(xiàn)了慰照,ArrayList應(yīng)該封裝的是一個(gè)數(shù)組灶挟。
ArrayList構(gòu)造:public ArrayList()
/**
* JDK1.8:Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
ArrayList構(gòu)造:public ArrayList?(int initialCapacity)
transient Object[] elementData;
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
通過(guò)有參構(gòu)造方法可以發(fā)現(xiàn),在ArrayList中所包含的數(shù)據(jù)實(shí)際上就是一個(gè)對(duì)象數(shù)組毒租。在進(jìn)行數(shù)據(jù)追加時(shí)發(fā)現(xiàn)ArrayList集合中保存的對(duì)象數(shù)組長(zhǎng)度不夠時(shí)稚铣,那么會(huì)將會(huì)開辟新的數(shù)組,同時(shí)將原始的舊數(shù)組內(nèi)容拷貝到新數(shù)組中墅垮。
而后數(shù)組的開辟操作:
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity);
}
??如果在實(shí)例化ArrayList類對(duì)象時(shí)沒有傳遞初始化額長(zhǎng)度惕医,則默認(rèn)情況下會(huì)使用空數(shù)組,但是如果在進(jìn)行數(shù)據(jù)增加時(shí)噩斟,發(fā)現(xiàn)數(shù)組容量不夠,則會(huì)判斷當(dāng)前的增長(zhǎng)容量與默認(rèn)的容量的大小孤个,使用較大的一個(gè)數(shù)值進(jìn)行新的數(shù)組開辟剃允,所以可以得出結(jié)論:
JDK1.7+:ArrayList默認(rèn)的構(gòu)造只會(huì)使用默認(rèn)的空數(shù)組,使用時(shí)才會(huì)開辟數(shù)組齐鲤,默認(rèn)的開辟長(zhǎng)度為10斥废,自動(dòng)增長(zhǎng)倍數(shù)為1.5倍:oldCapacity + (oldCapacity >> 1)
//JDK1.7
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
JDK1.6及以前:ArrayList默認(rèn)的構(gòu)造實(shí)際上就會(huì)默認(rèn)開辟大小為10的數(shù)組,自動(dòng)增長(zhǎng)倍數(shù)為(1.5倍+1):(oldCapacity * 3)/2 + 1
// ArrayList帶容量大小的構(gòu)造函數(shù)给郊。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
// 新建一個(gè)數(shù)組
this.elementData = new Object[initialCapacity];
}
// ArrayList構(gòu)造函數(shù)牡肉。默認(rèn)容量是10。
public ArrayList() {
this(10);
}
public void ensureCapacity(int minCapacity) {
//對(duì)于modCount變量淆九,我們后面會(huì)給出解釋
modCount++;
// 當(dāng)前數(shù)組的長(zhǎng)度
int oldCapacity = elementData.length;
// 最小需要的容量大于當(dāng)前數(shù)組的長(zhǎng)度則進(jìn)行擴(kuò)容
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
// 新擴(kuò)容的數(shù)組長(zhǎng)度為舊容量的1.5倍+1
int newCapacity = (oldCapacity * 3) / 2 + 1;
// 如果新擴(kuò)容的數(shù)組長(zhǎng)度還是比最小需要的容量小统锤,則以最小需要的容量為長(zhǎng)度進(jìn)行擴(kuò)容
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
// 進(jìn)行數(shù)據(jù)拷貝毛俏,Arrays.copyOf底層實(shí)現(xiàn)是System.arrayCopy()
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
在JDK1.7+中,當(dāng)ArrayList之中保存的容量不足時(shí)會(huì)采用1.5倍的方式進(jìn)行增長(zhǎng)饲窿,原始長(zhǎng)度為0煌寇,擴(kuò)容:(0->10->15->22->33->49->73->……)。
??ArrayList擴(kuò)容采用的是將原數(shù)組的內(nèi)容拷貝到擴(kuò)容后的新數(shù)組中逾雄,所以在使用ArrayList類或其子類時(shí)一定要估算實(shí)際存儲(chǔ)的數(shù)據(jù)量大小阀溶,一般采用有參構(gòu)造進(jìn)行創(chuàng)建,以避免ArrayList重復(fù)擴(kuò)容影響性能和產(chǎn)生過(guò)多的垃圾數(shù)組空間鸦泳。
ArrayList保存自定義類
??通過(guò)之前的分析已經(jīng)清楚了ArrayList子類的實(shí)現(xiàn)原理以及List核心操作银锻,但是在測(cè)試的時(shí)候使用的是系統(tǒng)提供的String類,這是一個(gè)設(shè)計(jì)比較完善的類做鹰,而對(duì)于類集而言也可以實(shí)現(xiàn)自定義類對(duì)象的保存击纬。
范例:實(shí)現(xiàn)自定義類對(duì)象保存
import java.util.ArrayList;
import java.util.List;
@lombok.Data//會(huì)自動(dòng)生成getter、setter誊垢、equals掉弛、hashCode、toString
@lombok.NoArgsConstructor//會(huì)自動(dòng)生成無(wú)參構(gòu)造
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return String.format("姓名:%s喂走、年齡:%s", name, age);
}
}
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<Person> all = new ArrayList();
all.add(new Person("張三", 30));
all.add(new Person("李四", 28));
all.add(new Person("王五", 32));
System.out.println(all.contains(new Person("王五", 32)));
all.remove(new Person("王五", 32));
all.forEach(System.out::println);//方法引用代替了消費(fèi)型的接口
/**
* true
* 姓名:張三殃饿、年齡:30
* 姓名:李四、年齡:28
*/
}
}
在使用List保存自定義對(duì)象時(shí)芋肠,如果需要使用到contains()乎芳、remove()方法進(jìn)行查詢或刪除處理時(shí)一定要保證類中已經(jīng)覆寫了equals()方法。
LinkedLIst子類
??在List接口中還有一個(gè)比較常用的子類:LinkedList帖池,這個(gè)類通過(guò)名稱就可以發(fā)現(xiàn)其特點(diǎn):基于鏈表的實(shí)現(xiàn)奈惑。那么首先觀察一下LinkedList的定義:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
范例:使用LinkedList實(shí)現(xiàn)集合操作
import java.util.LinkedList;
import java.util.List;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all = new LinkedList<>();
System.out.printf("集合是否為空?%s睡汹、集合元素個(gè)數(shù):%s\n",all.isEmpty(),all.size());
all.add("hello");
all.add("hello");
all.add("wolrd");
all.add("MLDN");
all.forEach(System.out::println);
}
}
??如果現(xiàn)在只是觀察程序的功能會(huì)發(fā)現(xiàn)和ArrayList使用時(shí)完全一樣的肴甸,但是其內(nèi)部實(shí)現(xiàn)機(jī)制是完全不同的,首先觀察LinkedList構(gòu)造方法中并沒有提供像ArrayList那樣的初始化大小的方法囚巴,而只是提供了無(wú)參構(gòu)造處理:“public LinkedList()
”原在。隨后觀察add()方法的具體實(shí)現(xiàn)。
public boolean add(E e) {
linkLast(e);
return true;
}
在之前編寫自定義鏈表時(shí)彤叉,是判斷了傳入數(shù)據(jù)是否為null庶柿,如果為null則不進(jìn)行保存,但在LinkedList中并沒有做這樣的處理秽浇,而是所有的數(shù)據(jù)都可以保存,而后此方法調(diào)用了linkLast()方法(再最后一個(gè)節(jié)點(diǎn)后追加)浮庐。
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
在LinkedList類中保存的數(shù)據(jù)都是利用Node節(jié)點(diǎn)進(jìn)行的封裝處理,同時(shí)為了提高程序執(zhí)行性能柬焕,每一次都會(huì)保存上一個(gè)追加的節(jié)點(diǎn)(最后一個(gè)節(jié)點(diǎn))审残,就可以在增加數(shù)據(jù)的時(shí)候避免遞歸處理梭域,在增加數(shù)據(jù)時(shí)要進(jìn)行數(shù)據(jù)保存?zhèn)€數(shù)的追加。
??通過(guò)上面的分析维苔,可以發(fā)現(xiàn)LinkedList封裝的就是一個(gè)鏈表實(shí)現(xiàn)碰辅。
Vector子類
??Vector是一個(gè)原始古老的程序類,這個(gè)類是在JDK1.0時(shí)提供的介时。到了JDk1.2時(shí)由于許多開發(fā)者已經(jīng)習(xí)慣于使用Vector没宾,并且許多系統(tǒng)類也是Vector實(shí)現(xiàn)的,考慮到請(qǐng)使用的廣泛性沸柔,所以類集框架將其保留了下來(lái)循衰,并讓其多實(shí)現(xiàn)了一個(gè)List接口,觀察Vector的結(jié)構(gòu)定義:
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
繼承結(jié)構(gòu)與ArrayList是相同的褐澎,所以來(lái)講這個(gè)類繼承結(jié)構(gòu)如下会钝。
范例:Vector類使用
import java.util.List;
import java.util.Vector;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
List<String> all = new Vector();
System.out.printf("集合是否為空?%s工三、集合元素個(gè)數(shù):%s\n",all.isEmpty(),all.size());
all.add("hello");
all.add("hello");
all.add("wolrd");
all.add("MLDN");
all.remove("hello");
all.add(null);
all.forEach(System.out::println);
}
}
??下面可以進(jìn)一步的觀察Vector類實(shí)現(xiàn):
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
Vector類如果使用的是無(wú)參構(gòu)造方法迁酸,則一定會(huì)默認(rèn)開辟一個(gè)10個(gè)長(zhǎng)度的數(shù)組,而后其余的實(shí)現(xiàn)操作與ArrayList是相同的俭正。通過(guò)源代碼分析可以發(fā)現(xiàn)奸鬓,Vector類中的操作方法采用的都是synchronized同步處理,而ArrayList并沒有進(jìn)行同步處理掸读,所以Vector類中的方法在多線程訪問(wèn)的時(shí)候?qū)儆诰€程安全的串远,但是性能不如ArrayList高。