Java集合之整體結(jié)構(gòu)
一、Java中集合
Java中集合類(lèi)是Java編程中使用最頻繁、最方便的類(lèi)。集合類(lèi)作為容器類(lèi)可以存儲(chǔ)任何類(lèi)型的數(shù)據(jù),當(dāng)然也可以結(jié)合泛型存儲(chǔ)指定的類(lèi)型(不過(guò)泛型僅僅在編譯期有效,運(yùn)行時(shí)是會(huì)被擦除的)。集合類(lèi)中存儲(chǔ)的僅僅是對(duì)象的引用,并不存儲(chǔ)對(duì)象本身。集合類(lèi)的容量可以在運(yùn)行期間進(jìn)行動(dòng)態(tài)擴(kuò)展,并且還提供很多很方便的方法,如求集合的并集、交集等。
二、集合類(lèi)結(jié)構(gòu)
Java中的集合包含多種數(shù)據(jù)結(jié)構(gòu),如鏈表、隊(duì)列、哈希表等。從類(lèi)的繼承結(jié)構(gòu)來(lái)說(shuō),可以分為兩大類(lèi),一類(lèi)是繼承自Collection接口,這類(lèi)集合包含List、Set和Queue等集合類(lèi)。另一類(lèi)是繼承自Map接口,這主要包含了哈希表相關(guān)的集合類(lèi)。下面我們看一下這兩大類(lèi)的繼承結(jié)構(gòu)圖:
1、List、Set和Queue

圖中的綠色的虛線代表實(shí)現(xiàn),綠色實(shí)線代表接口之間的繼承,藍(lán)色實(shí)線代表類(lèi)之間的繼承。
(1)List:我們用的比較多List包括ArrayList和LinkedList,這兩者的區(qū)別也很明顯,從其名稱(chēng)上就可以看出。ArrayList的底層的通過(guò)數(shù)組實(shí)現(xiàn),所以其隨機(jī)訪問(wèn)的速度比較快,但是對(duì)于需要頻繁的增刪的情況,效率就比較低了。而對(duì)于LinkedList,底層通過(guò)鏈表來(lái)實(shí)現(xiàn),所以增刪操作比較容易完成,但是對(duì)于隨機(jī)訪問(wèn)的效率比較低。
我們先看下兩者的插入效率:
package com.paddx.test.collection;
import java.util.ArrayList;
import java.util.LinkedList;
public class ListTest {
public static void main(String[] args) {
for(int i=;i<;i++){
}
long start = System.currentTimeMillis();
LinkedList<Integer> linkedList = new LinkedList<Integer>();
for(int i=;i<;i++){
linkedList.add(,i);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for(int i=;i<;i++){
arrayList.add(,i);
}
System.out.println(System.currentTimeMillis() - end);
}
}
下面是本地執(zhí)行的結(jié)果:
23
1227
可以看出,在這種情況下,LinkedList的插入效率遠(yuǎn)遠(yuǎn)高于ArrayList,當(dāng)然這是一種比較極端的情況。我們?cè)賮?lái)比較一下兩者隨機(jī)訪問(wèn)的效率:
package com.paddx.test.collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
public class ListTest {
public static void main(String[] args) {
Random random = new Random();
for(int i=;i<;i++){
}
LinkedList<Integer> linkedList = new LinkedList<Integer>();
for(int i=;i<;i++){
linkedList.add(i);
}
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for(int i=;i<;i++){
arrayList.add(i);
}
long start = System.currentTimeMillis();
for(int i=;i<;i++){
int j = random.nextInt(i+);
int k = linkedList.get(j);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
for(int i=;i<;i++){
int j = random.nextInt(i+);
int k = arrayList.get(j);
}
System.out.println(System.currentTimeMillis() - end);
}
}
下面是我本機(jī)執(zhí)行的結(jié)果:
5277
6
很明顯可以看出,ArrayList的隨機(jī)訪問(wèn)效率比LinkedList高出好幾個(gè)數(shù)量級(jí)。通過(guò)這兩段代碼,我們應(yīng)該能夠比較清楚的知道LinkedList和ArrayList的區(qū)別和適應(yīng)的場(chǎng)景。至于Vector,它是ArrayList的線程安全版本,而Stack則對(duì)應(yīng)棧數(shù)據(jù)結(jié)構(gòu),這兩者用的比較少,這里就不舉例了。
(2)Queue:一般可以直接使用LinkedList完成,從上述類(lèi)圖也可以看出,LinkedList繼承自Deque,所以LinkedList具有雙端隊(duì)列的功能。PriorityQueue的特點(diǎn)是為每個(gè)元素提供一個(gè)優(yōu)先級(jí),優(yōu)先級(jí)高的元素會(huì)優(yōu)先出隊(duì)列。
(3)Set:Set與List的主要區(qū)別是Set是不允許元素重復(fù)的,而List則可以允許元素重復(fù)的。判斷元素的重復(fù)需要根據(jù)對(duì)象的hash方法和equals方法來(lái)決定。這也是我們通常要為集合中的元素類(lèi)重寫(xiě)hashCode方法和equals方法的原因。我們還是通過(guò)一個(gè)例子來(lái)看一下Set和List的區(qū)別,以及hashcode方法和equals方法的作用:
package com.paddx.test.collection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class SetTest {
public static void main(String[] args) {
Person p1 = new Person("lxp",10);
Person p2 = new Person("lxp",10);
Person p3 = new Person("lxp",20);
ArrayList<Person> list = new ArrayList<Person>();
list.add(p1);
System.out.println("---------");
list.add(p2);
System.out.println("---------");
list.add(p3);
System.out.println("List size=" + list.size());
System.out.println("----分割線-----");
Set<Person> set = new HashSet<Person>();
set.add(p1);
System.out.println("---------");
set.add(p2);
System.out.println("---------");
set.add(p3);
System.out.println("Set size="+set.size());
}
static class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
System.out.println("Call equals();name="+name);
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return name.equals(person.name);
}
@Override
public int hashCode() {
System.out.println("Call hashCode(),age="+age);
return age;
}
}
}
上述代碼的執(zhí)行結(jié)果如下:
---------
---------
List size=3
----分割線-----
Call hashCode(),age=10
---------
Call hashCode(),age=10
Call equals();name=lxp
---------
Call hashCode(),age=20
Set size=2
從結(jié)果看出,元素加入List的時(shí)候,不執(zhí)行額外的操作,并且可以重復(fù)。而加入Set之前需要先執(zhí)行hashCode方法,如果返回的值在集合中已存在,則要繼續(xù)執(zhí)行equals方法,如果equals方法返回的結(jié)果也為真,則證明該元素已經(jīng)存在,會(huì)將新的元素覆蓋老的元素,如果返回hashCode值不同,則直接加入集合。這里記住一點(diǎn),對(duì)于集合中元素,hashCode值不同的元素一定不相等,但是不相等的元素,hashCode值可能相同。
HashSet和LinkedHashSet的區(qū)別在于后者可以保證元素插入集合的元素順序與輸出順序保持一致。而TresSet的區(qū)別在于其排序是按照Comparator來(lái)進(jìn)行排序的,默認(rèn)情況下按照字符的自然順序進(jìn)行升序排列。
(4)Iterable:從這個(gè)圖里面可以看到Collection類(lèi)繼承自Iterable,該接口的作用是提供元素遍歷的功能,也就是說(shuō)所有的集合類(lèi)(除Map相關(guān)的類(lèi))都提供元素遍歷的功能。Iterable里面包含了Iterator的迭代器,其源碼如下,大家如果熟悉迭代器模式的話,應(yīng)該很容易理解。
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
2、Map:

Map類(lèi)型的集合最大的優(yōu)點(diǎn)在于其查找效率比較高,理想情況下可以實(shí)現(xiàn)O(1)的時(shí)間復(fù)雜度。Map中最常用的是HashMap,LinkedHashMap與HashMap的區(qū)別在于前者能夠保證插入集合的元素順序與輸出順序一致。這兩者與TreeMap的區(qū)別在于TreeMap是根據(jù)鍵值進(jìn)行排序的,當(dāng)然其底層的實(shí)現(xiàn)也有本質(zhì)的區(qū)別,如HashMap底層是一個(gè)哈希表,而TreeMap的底層數(shù)據(jù)結(jié)構(gòu)是一棵樹(shù)。我們現(xiàn)在看下TreeMap與LinkedHashMap的區(qū)別:
package com.paddx.test.collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class MapTest {
public static void main(String[] args) {
Map<String,String> treeMap = new TreeMap<String,String>();
Map<String,String> linkedMap = new LinkedHashMap<String, String>();
treeMap.put("b",null);
treeMap.put("c",null);
treeMap.put("a",null);
for (Iterator<String> iter = treeMap.keySet().iterator();iter.hasNext();){
System.out.println("TreeMap="+iter.next());
}
System.out.println("----------分割線---------");
linkedMap.put("b",null);
linkedMap.put("c",null);
linkedMap.put("a",null);
for (Iterator<String> iter = linkedMap.keySet().iterator();iter.hasNext();){
System.out.println("LinkedHashMap="+iter.next());
}
}
}
運(yùn)行上述代碼,執(zhí)行結(jié)果如下:
TreeMap=a
TreeMap=b
TreeMap=c
----------分割線---------
LinkedHashMap=b
LinkedHashMap=c
LinkedHashMap=a
從運(yùn)行結(jié)果可以很明顯的看出這TreeMap和LinkedHashMap的區(qū)別,前者是按字符串排序進(jìn)行輸出的,而后者是根據(jù)插入順序進(jìn)行輸出的。細(xì)心的讀者可以發(fā)現(xiàn),HashMap與TreeMap的區(qū)別,與之前提到的HashSet與TreeSet的區(qū)別是一致的,在后續(xù)進(jìn)行源碼分析的時(shí)候,我們可以看到HashSet和TreeSet本質(zhì)上分別是通過(guò)HashMap和TreeMap來(lái)實(shí)現(xiàn)的,所以它們的區(qū)別自然也是相同的。HashTable現(xiàn)在已經(jīng)很少使用了,與HashMap的主要區(qū)別是HashTable是線程安全的,不過(guò)由于其效率比較低,所以通常使用HashMap,在多線程環(huán)境下,通常用CurrentHashMap來(lái)代替。
三、總結(jié)
本文只是從整體上介紹了Java集合框架及其繼承關(guān)系。除了上述類(lèi),集合還提供Collections和Arrays兩個(gè)工具類(lèi),此外,集合中排序跟Comparable和Comparator緊密相關(guān)。在之后的文章中將對(duì)上述提的類(lèi)在JDK中實(shí)現(xiàn)源碼進(jìn)行詳細(xì)分析。
相關(guān)文章
idea中啟動(dòng)項(xiàng)目彈出 IDEA out of memory窗口的解決方案
這篇文章主要介紹了idea中啟動(dòng)項(xiàng)目彈出 IDEA out of memory窗口的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作
這篇文章主要介紹了MyBatis 實(shí)現(xiàn)數(shù)據(jù)的批量新增和刪除的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
詳解使用IntelliJ IDEA新建Java Web后端resfulAPI模板
這篇文章主要介紹了詳解使用IntelliJ IDEA新建Java Web后端resfulAPI模板,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Java語(yǔ)言中flush()函數(shù)作用及使用方法詳解
這篇文章主要介紹了Java語(yǔ)言中flush函數(shù)作用及使用方法詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
java實(shí)現(xiàn)操作系統(tǒng)的短進(jìn)程作業(yè)調(diào)度示例分享
java編寫(xiě)的實(shí)現(xiàn)了操作系統(tǒng)中的短作業(yè)進(jìn)程,可以實(shí)現(xiàn)幾道作業(yè)同時(shí)作業(yè)調(diào)度2014-02-02
springboot編程式事務(wù)TransactionTemplate的使用說(shuō)明
這篇文章主要介紹了springboot編程式事務(wù)TransactionTemplate的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06

