JAVA三種集合LIST、SET、MAP ——詳解

1. 集合框架介紹 

咱們知道,計算機的優點在於處理大量的數據,在編程開發中,爲處理大量的數據,必須具有相應的存儲結構,以前學習的數組能夠用來存儲並處理大量類型相同的數據,可是經過上面的課後練習,會發現數組在應用中的限制:數組長度一旦肯定,就沒法更改;除非採用創建新數組,再將原數組內容拷貝過來;數組中只能存放指定類型的數據,操做不方便。在實際開發中,爲了操做方便,JDK中提供了List集合。java

List集合與數組的用途很是類似,都是用來存儲大量數據的,不一樣處有兩點:web

1. 數組長度在使用前必須肯定,一旦肯定不能改變。而List集合長度可變,無需定義。編程

2. 數組中必須存放同一類型的數據,List集合中能夠存放不一樣類型的數據。數組

List集合是Java集合框架中的一種,另外兩種集合Set和Map會在下面介紹。List集合在JDK中被封裝稱爲接口,針對List接口,有若干種實現,經常使用的有三個子類,即ArrayList、Vector和LinkedList。這三個類的功能與用法相同,但內部實現方式不一樣。下面以ArrayList爲例介紹集合的經常使用操做,Vector和LinkedList的使用方法與ArrayList相似。安全

數組與List集合的常規操做相似,下面經過代碼對比二者的用法:框架

代碼演示:數組的基本操做性能

public class ArrayDemo {學習

public static void main(String[] args) {this

String[] array = new String[3];spa

for (int i = 0; i < 3; i++) {

array[i] = "Hello";

}

String a = array[0];

}

}

代碼演示:List集合的基本操做

import java.util.ArrayList;    

public class ListDemo {

public static void main(String[] args) {

ArrayList list = new ArrayList();    

for (int i = 0; i < 3; i++) {

list.add("Hello");    

}

String a = (String)list.get(0);    

}

}

代碼解析:

① 集合框架在java.util包中,使用前必須使用import語句引入對應的類。

② List集合的定義時不須要指定大小,也不用指定集合中保存的數據類型。

③ List集合中添加數據時不須要制定下標,List集合會自動生成下標。

④ 獲取List集合的元素時使用get方法並傳入下標,而後強制類型轉換爲實際類型。

代碼演示:使用集合記錄學員姓名

public static void main(String[] args) {

System.out.println("請輸入班級學員姓名,輸入OVER結束");

java.util.Scanner scanner = new java.util.Scanner(System.in);

ArrayList list = new ArrayList();

do {

String name = scanner.next();

if (name.equalsIgnoreCase("OVER")) break;

list.add(name);

while (true);

System.out.println(list);    

}

代碼解析:

① List集合重寫了toString方法,能夠將集合中的元素依次輸出。

2. List集合的經常使用方法

下表列出了List集合的經常使用方法:

返回類型

方法名稱

說明

boolean

add(Object obj)

加入元素,返回是否添加成功

boolean

clear()

清除集合中的元素

boolean

contains(Object obj)

查找集合中是否存在傳入的元素

Object

get(int index)

獲取指定位置的元素

boolean

isEmpty()

判斷集合是否爲空

Object

remove(int index)

刪除制定位置的元素,並返回該元素

int

size()

獲取集合大小

Object[]

toArray()

將集合轉換成一個數組

  List集合的經常使用方法

下面經過實例演示各個方法的用途:

代碼演示:List集合的經常使用方法

import java.util.ArrayList;

public class Demo {

public static java.util.Scanner scanner = new java.util.Scanner(System.in);

public static void main(String[] args) {

ArrayList listA = new ArrayList();

ArrayList listB = new ArrayList();

System.out.println("請輸入A班學員姓名,輸入OVER結束");

inputName(listA);

System.out.println("請輸入B班學員姓名,輸入OVER結束");

inputName(listB);

//合併集合listA與listB

listA.addAll(listB);

System.out.println("請輸入要查找的學員姓名");

String name = scanner.next();

int pos = listA.indexOf(name);

if (pos==-1) {

System.out.println("沒有找到");

else {

System.out.println("找到了,位置是:" + pos);

}

System.out.println("請輸入要刪除的學員姓名");

String delName = scanner.next();

if (listA.remove(delName)) {

System.out.println("刪除成功!");

else {

System.out.println("沒有該學員");

}

}

public static void inputName(ArrayList list) {

do {

String name = scanner.next();

if (name.equalsIgnoreCase("OVER")) break;

list.add(name);

while (true);

}

}

3. 使用List集合保存對象

使用List集合保存對象時,主要注意如下幾點:

1. 集合中保存的是對象的引用,觀察如下代碼:

代碼演示:使用集合保存對象

import java.util.ArrayList;

class Student {

String name;

int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public String toString() {

return name + "/" + age;

}

}

public class Demo {

public static void main(String[] args) {

ArrayList list = new ArrayList();

Student stu = new Student("Tom" , 10);

for (int i = 0; i < 3; i++) {

stu.age = 10 + i;

list.add(stu);

}

System.out.println(list);

}

}

上面代碼的原意是在集合中保存三個Student對象,age分別爲十、十一、12,但實際輸出的age值均爲12。這是由於list集合中保存的是stu對象的引用,而在循環中stu的引用並無變化,因此循環結束後集閤中的三個元素都指向stu對象,age的值天然也是最後的12。將代碼「Student stu = new Student("Tom" , 10);」放入循環內能夠解決這一問題。

2. 使用remove、contains、indexOf等方法時,應該重寫類的equals方法,觀察如下代碼:

代碼演示:未重寫equals方法的代碼

//省略了Student類的定義

public class Demo {

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add(new Student("Tom" , 11));

list.add(new Student("Jerry" , 22));

list.add(new Student("Alice" , 33));

System.out.println(list.contains(new Student("Tom" , 11)));

System.out.println(list.indexOf(new Student("Jerry" , 22)));

System.out.println(list.remove(new Student("Alice" , 33))?"成功":"無此項");

}

}

在上例中,咱們但願判斷學員Tom是否存在,查找學員Jerry,刪除學員Alice,可是輸出的結果倒是不存在,找不到,刪不掉。這是由於List集合會調用元素的equals方法來判斷對象是否相等,而Student類沒有重寫equals方法,默認是按引用地址比較,而每一個學員對象的地址又不相同,因此出現這個現象。經過給Student類添加equals方法能夠解決這個問題:

代碼演示:重寫equals方法後的Student類

class Student {

String name;

int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public boolean equals(Object obj) {

if (obj == nullreturn false;

if (!(obj instanceof Student)) return false;

Student stu = (Student) obj;

return stu.name.equals(this.name) && stu.age == this.age;

}

}

4. 三種List集合的比較

咱們說過,ArrayList、Vector與LinkedList的使用方法相同,內部實現方式不一樣。而內部實現方式的不一樣又決定了三種集合的適用範圍,瞭解三種集合的內部實現,才能正確的選擇使用類型。

ArrayList與Vector比較

ArrayList與Vector的內部實現相似,Vector設計爲線程安全,ArrayList設計爲非線程安全。爲了保證線程安全,Vector在性能方面稍遜於ArrayList,目前咱們編寫的都是單線程應用程序,應選擇使用ArrayList。

ArrayList與LinkedList

ArrayList與LinkedList均設計爲非線程安全,ArrayList內部採用數組實現(與Vector相同),LinkedList內部採用鏈表結構實現。

ArrayList採用數組保存元素,意味着當大量添加元素,數組空間不足時,依然須要經過新建數組、內存複製的方式來增長容量,效率較低;而當進行對數組進行插入、刪除操做時,又會進行循環移位操做,效率也較低;只有進行按下標查詢時(get方法),使用數組效率很高。

LinkedList採用鏈表保存元素,在添加元素時只須要進行一次簡單的內存分配便可,效率較高;進行插入、刪除操做時,只需對鏈表中相鄰的元素進行修改便可,效率也很高;但進行按下標查詢時,須要對鏈表進行遍歷,效率較低。下圖演示了鏈表結構的特性:

圖:  鏈表結構,每一個元素引用後面的元素

圖:  向鏈表中插入元素,只需修改兩處引用

圖:  刪除鏈表中的元素,也只須要修改兩處引用

能夠總結出ArrayList在進行數據的新增、插入、刪除時效率較低,按下標對數據進行查找時效率較高;LinkedList正好相反。通常來講ArrayList保存常常進行查詢操做的集合,LinkedList適用於保存經常進行修改操做的集合。

5. 章節概述

1. List集合與數組的區別。

2. List集合實際上包含了3個經常使用的集合類,即ArrayList、Vector和LinkedList。

3. List集合的經常使用操做。

4. ArrayList採用數組保存元素,意味着當大量添加元素,數組空間不足時,依然須要經過新建數組、內存複製的方式來增長容量,效率較低;而當進行對數組進行插入、刪除操做時,又會進行循環移位操做,效率也較低;只有進行按下標查詢時(get方法),使用數組效率很高。

5. ArrayList與Vector的內部實現相似,Vector設計爲線程安全,ArrayList設計爲非線程安全。爲了保證線程安全,Vector在性能方面稍遜於ArrayList,目前咱們編寫的都是單線程應用程序,應選擇使用ArrayList。

6. ArrayList與LinkedList均設計爲非線程安全,ArrayList內部採用數組實現(與Vector相同),LinkedList內部採用鏈表結構實現。

7. LinkedList採用鏈表保存元素,在添加元素時只須要進行一次簡單的內存分配便可,效率較高;進行插入、刪除操做時,只需對鏈表中相鄰的元素進行修改便可,效率也很高;但進行按下標查詢時,須要對鏈表進行遍歷,效率較低。

1. Map集合

在上面講的List集合中,可用經過List集合提供的各類方法來對其中的元素進行操做,從而能夠方便用戶操做,可是若是要從List集合中獲取一個特定的對象,操做是比較繁瑣的。

在類Person中有cardId和name兩個屬性,分別表明編號和姓名,建立兩個Person對象並存儲到ArrayList集合中 ,若是要從集合中獲取指定的對象,則必需要經過迭代整個集合來得到,以下所示:

代碼演示:Person類 

public class Person {

String cardId;

String name;

public Person(String cardId, String name) {

this.cardId = cardId;

this.name = name;

}

}

代碼演示:從ArrayList中獲取特定的對象 

public class ArrayListTest {

public static void main(String[] args) {

Person personA = new Person("001", "Tom");

Person personB = new Person("002", "Jack");

ArrayList list = new ArrayList();

list.add(personA);

list.add(personB);

for (int i = 0; i < list.size(); i++) {

Person person = (Person) list.get(i);

if (person.cardId.equals("002")) { 

System.out.println(person.name);

}

}

}

}

從上面的示例中,咱們看到從list集合中獲取一個對象的繁瑣,有沒有簡單的方法呢?在JDK中專門提供了Map集合來存儲上面這種一對一映射關係的對象。

Map集合用於保存具備映射關係的數據,即以鍵值對(key->value)的方式來存儲數據。所以在Map集合內部有兩個集合,一個集合用於保存Map中的key(鍵),一個集合用於保存Map中的value(值),其中key和value能夠是任意數據類型數據。

圖:  Map集合

Map集合中的經常使用類有Hashtable和HashMap,兩個類的功能和用法類似,下面以HashMap爲例介紹Map集合的用法。

代碼演示:Map集合使用

public class MapTest {

public static void main(String[] args) {

HashMap map = new HashMap();  

map.put("001", "Tom");  

map.put("002", "Jack");

String name = (String) map.get("002");  

System.out.println(name);

}

}

代碼解析:

① 建立HashMap對象。

② 利用HashMap中的put方法將鍵值對形式的對象進行存儲,put方法中的第一個參數爲映射關係中key的值,put方法的第二個參數爲映射關係中value的值。

③ 利用HashMapget方法獲取key對應的value,而後強制類型轉換爲實際類型。get中的參數爲key,返回值爲key對應的value

下表列出了HashMap中經常使用的方法:

返回類型

方法名稱

做用

Object

put(Object key,Object value)

加入元素返回與此key關聯的原有的value,不存在則返回null

void

clear()

從集合中移除全部的元素

boolean

containsKey(Object key)

根據key從集合中判斷key是否存在

boolean

containsValue(Object value)

根據value從集合中判斷value是否存在

Object

get(Object key)

根據key返回key對應的值

Set

keySet()

返回Map集合中包含的鍵集合

Object

remove(Object key)

從集合中刪除key對應的元素,返回與key對應的原有value,不存在則返回null

int 

size()

返回集合中的元素的數量

表:  HashMap經常使用方法

Map集合的綜合示例:

代碼演示:Map集合綜合演示

import java.util.HashMap;

import java.util.Scanner;

public class TestMap {

public static void main(String[] args) {

HashMap map = new HashMap();

Scanner scanner = new Scanner(System.in);

for (int i = 0; i < 5; i++) {  

System.out.println("請輸入身份證號:");

String id = scanner.next();

System.out.println("請輸入姓名:");

String name = scanner.next();

map.put(id, name);  

}

int size = map.size();  

System.out.println("數據輸入完畢!共" + size

+ "條數據!\n---------------------------------");

String answer = "no";

do {

System.out.println("請輸入你要查找的用戶的身份證號:");

String id = scanner.next();

if (map.containsKey(id)) {  

String name = (String) map.get(id);  

System.out.println("您查找的用戶姓名爲:" + name);

else {

System.out.println("您查找的用戶不存在!");

}

System.out.println("您還要繼續查找嗎?(yes/no)");

answer = scanner.next();

while ("yes".equalsIgnoreCase(answer));

System.out.println("請輸入要刪除的用戶的身份證號:");

String id = scanner.next();

if (map.containsKey(id)) {

String name = (String) map.remove(id);  

System.out.println("用戶" + name + "刪除成功!");

else {

System.out.println("您要刪除的用戶不存在!");

}

System.out.println("要格式化系統嗎?(yes/no)");

String format = scanner.next();

if ("yes".equalsIgnoreCase(format)) {

map.clear();  

System.out.println("系統格式化完畢!當前系統中數據爲" 

+ map.size() + "條");

}

System.out.println("程序運行結束!");

}

}

代碼解析:

① 使用put方法將身份證號和姓名存入Map集合中。

② 使用size方法得到集合中的映射關係條數。

③ 使用containsKey方法判斷集合是否存在與key對應的映射關係。

④ 使用get方法得到身份證號對應的姓名。

⑤ 使用remove方法刪除身份證號對應的用戶,返回身份證號對應的姓名。

⑥ 使用clear方法刪除Map集合中全部的映射關係。

來看下面的一個示例: 

代碼演示:Map集合中重複key

import java.util.HashMap;

public class DemoMap {

public static void main(String[] args) {

HashMap map = new HashMap();

map.put("001", "小美");

map.put("002", "阿聰");  

map.put("002", "小莉");  

String name = (String) map.get("002");

System.out.println(name);

}

}

注意①和②處的代碼,在向集合中添加值的時候,使用了重複的key,可是value不一樣,在下面得到key002value爲多少呢?程序運行的結果是小莉。從結果能夠中能夠知道,Map集合中的key不能是重複的,若是重複,那麼後面添加的映射關係會覆蓋前面的映射關係。致使這樣狀況的出現主要是由於Map集合中的key的維護是依靠Set集合(立刻會學習到)完成的。

HashMapHashtable的操做是相同的,他們的區別以下: 

Hashtable是線程安全的,HashMap是非線程安全的。全部HashMapHashtable的性能更高。

Hashtable不容許使用使用null值做爲keyvalue,可是HashMap是能夠的。

2. Set集合

Set集合和List集合的不少的用法是相同的。可是Set集合中的元素是無序的,元素也是不能重複的。Set集合中經常使用類爲HashSet。

HashSet類中經常使用的方法以下:

返回類型

方法名稱

做用

boolean

add(Object obj)

加入元素

void

clear()

移除Set集合中全部元素

boolean

contains(Object obj)

判斷Set集合中是否包含指定元素

boolean

isEmpty()

判斷Set集合是否爲空

Iterator

iterator()

返回Set集合中對元素迭代的迭代器

boolean

remove(Object obj)

從集合中刪除元素

Int

size()

返回集合中的元素數量

表:  HashSet類經常使用方法

經過上面的表,能夠清楚的看到Set集合的用法和List集合是類似的,可是須要注意Set集合的迭代和List集合是不一樣的,List的集合的迭代能夠經過for循環得到索引來進行,可是Set集合的迭代必需要經過迭代器進行。

代碼演示:Set集合的迭代

import java.util.HashSet;

import java.util.Iterator;

public class SetIterator {

public static void main(String[] args) {

HashSet set = new HashSet();

set.add("a");

set.add("b");

set.add("c");

Iterator iter = set.iterator();  

while (iter.hasNext()) {  

String str = (String) iter.next();  

System.out.println(str);

}

}

}

代碼解析:

① 經過Set集合的iterator()方法得到該集合的迭代器,迭代器是Iterator類的實例。

② 根據迭代器的hasNext()方法判斷集合中是否還有元素,若是有就返回true。

③ 根據迭代器的next()方法得到集合中的元素,並強制類型轉換爲目標類型。

從上面例子的運行結果,能夠看出Set中的元素是無序的。經過Set集合的迭代再來學習Map集合的迭代。

代碼演示:Map集合的迭代

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

public class MapIter {

public static void main(String[] args) {

HashMap map = new HashMap();

map.put("001", "小美");

map.put("002", "阿聰");

map.put("003", "小黑");

HashSet keys = (HashSet) map.keySet();  

Iterator iter = keys.iterator();

while (iter.hasNext()) {

String key = (String) iter.next();  

String value = (String) map.get(key);  

System.out.println(key + ":" + value);

}

}

}

代碼解析:

① 調用Map集合的keySet方法得到Map集合中的key的集合。

② 得到key

③ 根據key得到對應的value

下面經過一個示例演示Set集合中不容許元素重複的特性:

代碼演示:向Set集合中添加劇復元素

import java.util.HashSet;

import java.util.Iterator;

public class Demo2 {

public static void main(String[] args) {

HashSet set = new HashSet();

set.add("a");

set.add("a");

set.add("c");

System.out.println("集合長度爲:" + set.size());

Iterator iter = set.iterator();

while (iter.hasNext()) {

String str = (String) iter.next();

System.out.println(str);

}

}

}

上面的代碼輸出集合的長度爲2,並且集合中的元素只有一個a和c,從結果中能夠看出Set集合中的元素是不能重複的。帶着這個結論再來看下面的示例:

代碼演示:向Set集合中添加劇復元素

import java.util.HashSet;

import java.util.Iterator;

public class Demo3 {

public static void main(String[] args) {

Person personA = new Person("001", "Tom");

Person personB = new Person("001", "Tom");

HashSet set = new HashSet();

set.add(personA);

set.add(personB);

System.out.println("集合中元素個數:" + set.size());

Iterator iter = set.iterator();

while (iter.hasNext()) {

Person p = (Person) iter.next();

System.out.println(p.cardId + ":" + p.name);

}

}

}

程序的運行結果以下:

集合中元素個數:2

001:Tom

001:Tom

發現程序的運行結果是有「問題」的,由於Set集合中是不容許存放重複的元素的,可是兩個Person對象的屬性值是徹底相同的,怎麼還都能存放進去呢?要找到問題的答案,須要瞭解下Set集合的存放原理。

圖:  Set集合存儲

從上圖中能夠看到,Set集合中的元素的無序性,可是Set集合是怎麼判斷每一個元素的存放的位置呢?在向Set集合中存放元素時,Set集合根據元素的hashCode()方法來獲取一個int類型的數據,而後根據這個數據來計算元素在集合中的位置。可是在存儲元素時會出現兩個元素的hashCode()方法返回值相同的狀況,好比上圖的對象C和D就出現了這種狀況,致使計算出元素在集合中的位置相同,這種狀況稱之爲「衝突」。若是發生了衝突,Set集合會根據發生衝突元素之間調用equals()方法進行比較,若是equals()返回值爲true,說明兩個元素爲相同的元素,這樣會致使添加操做無效。若是equals()返回值爲false,說明兩個元素不相同,這樣Set集合會將該元素進行偏移存儲。「衝突」發生的頻率越高,Set集合的性能就越低,要儘量的避免衝突的發生, 就要在類中重寫hashCode()方法,而且要儘量的保證hashCode()方法返回值是惟一的。在重寫hashCode()方法時有個技巧,就是讓對象中的數值屬性和一個素數相乘,並將積相加,對於對象類型,調用其hashCode()方法便可。

對於hashCode()和equals()兩個方法有這樣的規律,hashCode()方法返回值相同時,equals()方法比較並不必定相等,可是equals()方法比較相等,hashCode()方法返回值是相同的。

代碼演示:實現Person對象的equals和hashCode方法 

public class Person {

String cardId;

String name;

public Person(String cardId, String name) {

this.cardId = cardId;

this.name = name;

}

public int hashCode() {

return cardId.hashCode() + name.hashCode();

}

public boolean equals(Object obj) {

if (obj == null){

return false;

}

if(obj instanceof Person){

Person other = (Person) obj;

if (cardId == null) {

if (other.cardId != null){

return false;

}

else if (!cardId.equals(other.cardId)){

return false;

}

if (name == null) {

if (other.name != null){

return false;

}

else if (!name.equals(other.name)){

return false;

}

}else{

return false;

}

return true;

}

}

再次運行Demo3的代碼,運行結果以下:

集合中元素個數:1

001:Tom

3. 本章總結

Map集合用於保存具備映射關係的數據,即以鍵值對(key->value)的方式來存儲數據。所以在Map集合內部有兩個集合,一個集合用於保存Map中的key(鍵),一個集合用於保存Map中的value(值),其中key和value能夠是任意數據類型數據。

Set集合的特色。

Set集合的使用及迭代。

Map集合的迭代。

相關文章
相關標籤/搜索