在編寫JAVA程序中,我們經常會遇到需要保存一組數據對象,此時,我們可以采用對象數組來進行多個對象的保存,但對象數組存在一個最大的問題即在於長度上的限制,如果說我們現在要保存一組對象,但是我們並知道數組對象到底有多少個的時候,那么此時就遇到了困難,因此為了解決此問題,在JDK1.2中,提出了類集框架的概念,並在JDK1.5中對此框架進行了修改,加入了泛型的支持,從而保證了操作的安全性。而在整個集合中,提供了幾個集合核心操作的接口,分別為:Collection、Set、List、Enumeration、Iterator、ListIterator等。
1)單值保存的最大父接口:Collection
所謂的單值保存指的是每次操作只保存一個對象,每次增加只增加一個對象,而在Collection接口之中定義了如下幾個常用的方法:
package com.njupt.study.collection; import java.util.Iterator; public interface Collection<E> extends Iterable<E>{ /** * 增加數據 * @param e * @return */ boolean add(E e); /** * 刪除指定的元素 * @param o * @return */ boolean remove(Object o); /** * 清除數據 */ void clear(); /** * 判斷集合是否為空 * @return */ boolean isEmpty(); /** * 獲取元素的個數 * @return */ int size(); /** * 查找一個數據是否存在 * @param o * @return */ boolean contains(Object o); /** * 將集合變為對象數組后返回 * @return */ Object[] toArray(); /** * 將集合變為指定類型的對象數組 * @param <T> * @param a * @return */ <T> T[] toArray(T[] a); /** * 為Iterator接口實例化,來源於父類接口Iterable */ Iterator<E> iterator(); }
Collection接口本身在開發之中並不會直接的去使用,而在開發之中往往會使用兩個子接口:List、Set。
2)允許重復的子接口:List
List接口是Collection接口的子接口,是有序的 collection(也稱為序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。
public interface List<E>extends Collection<E>
List接口對Collection接口進行了大量的擴充操作,而主要的擴充方法有如下幾個:
public interface List<E> extends Collection<E> { /** * 返回指定位置上的數據 * @param index * @return */ E get(int index); /** * 修改指定位置上的數據 * @param index * @param element * @return */ E set(int index, E element); /** * 為ListIterator接口實例化 * @return */ ListIterator<E> listIterator(); }
List本身也是一個接口,所以如果要想使用這個接口就必須有子類,實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
- ArrayList:
在List接口里面ArrayList子類的使用幾率是最高的,一般List接口實例化,一般想到的為ArrayList。
1 package com.njupt.study.collection; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 class Student 7 { 8 private String name; 9 10 private int age; 11 12 public Student(){}; 13 14 public Student(String n,int a) 15 { 16 this.name = n; 17 this.age = a; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 public int getAge() { 29 return age; 30 } 31 32 public void setAge(int age) { 33 this.age = age; 34 } 35 36 } 37 38 39 public class Demo02 { 40 41 public static void main(String[] args) { 42 List<Student> list = new ArrayList<Student>(); 43 44 list.add(new Student("zhangsan",18)); 45 46 list.add(new Student("lisi",19)); 47 48 list.add(new Student("wangwu",17)); 49 50 System.out.println(list); 51 52 System.out.println("******************"); 53 54 for(int i=0;i<list.size();i++) 55 { 56 System.out.println(list.get(i)); 57 } 58 } 59 }
輸出結果為:
[com.njupt.study.collection.Student@1fc4bec, com.njupt.study.collection.Student@dc8569, com.njupt.study.collection.Student@1bab50a]
******************
com.njupt.study.collection.Student@1fc4bec
com.njupt.study.collection.Student@dc8569
com.njupt.study.collection.Student@1bab50a
為了顯示更加明顯的信息,因此增加重寫toString()方法,
public String toString()
{
return this.name+"---->"+this.age;
}
顯示結果為:
[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17
可以看見ArrayList采用的為數組方式保存元素對象。
add 為添加元素,那么remove 可以刪除元素,那么我們試試刪除元素:
1 package com.njupt.study.collection; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 class Student 7 { 8 private String name; 9 10 private int age; 11 12 public Student(){}; 13 14 public Student(String n,int a) 15 { 16 this.name = n; 17 this.age = a; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 public int getAge() { 29 return age; 30 } 31 32 public void setAge(int age) { 33 this.age = age; 34 } 35 36 public String toString() 37 { 38 return this.name+"---->"+this.age; 39 } 40 } 41 42 43 public class Demo02 { 44 45 public static void main(String[] args) { 46 List<Student> list = new ArrayList<Student>(); 47 48 list.add(new Student("zhangsan",18)); 49 50 list.add(new Student("lisi",19)); 51 52 list.add(new Student("wangwu",17)); 53 54 System.out.println(list); 55 56 System.out.println("******************"); 57 58 for(int i=0;i<list.size();i++) 59 { 60 System.out.println(list.get(i)); 61 } 62 63 System.out.println("*********刪除元素*************"); 64 65 list.remove(new Student("wangwu",17)); 66 for(int i=0;i<list.size();i++) 67 { 68 System.out.println(list.get(i)); 69 } 70 71 } 72 }
輸出結果為:
[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17
*********刪除元素*************
zhangsan---->18
lisi---->19
wangwu---->17
可以看見元素並沒有被刪除,為什么呢? 那我們試試非自定義對象是否可以刪除呢?
1 package com.njupt.study.collection; 2 3 import java.util.ArrayList; 4 5 public class Demo01 { 6 7 /** 8 * @param args 9 */ 10 public static void main(String[] args) { 11 java.util.List<String> list = new ArrayList<String>(); 12 13 list.add("a"); 14 15 list.add("b"); 16 17 list.add("c"); 18 19 System.out.println(list); 20 21 for (int x = 0; x < list.size(); x++) { 22 System.out.println(list.get(x)); 23 } 24 25 list.remove("c"); 26 27 System.out.println("**********************"); 28 29 for (int x = 0; x < list.size(); x++) { 30 System.out.println(list.get(x)); 31 } 32 } 33 34 }
顯示結果為:
[a, b, c]
a
b
c
**********************
a
b
我們發現是可以刪除的,為什么我們自己定義的不能刪除呢?我們看下String的源碼有什么不同?
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
原因應該就是上面的,因為對象在刪除的時候,需要判斷集合中的對象和要刪除的對象是否一致,然后才能刪除,但我們自定義對象中,並沒有重寫equals方法所以,刪除的時候沒有成功,為此,我們增加equals方法試試如何?
1 public boolean equals(Object obj) 2 { 3 if(this == obj) 4 { 5 return true; 6 } 7 8 if(obj == null) 9 { 10 return false; 11 } 12 13 if( ! (obj instanceof Student) ) 14 { 15 return false; 16 } 17 18 Student other = (Student) obj; 19 20 if(this.name.equals(other.name) && this.age == other.age) 21 { 22 return true; 23 } 24 return false; 25 }
顯示結果為:
[zhangsan---->18, lisi---->19, wangwu---->17]
******************
zhangsan---->18
lisi---->19
wangwu---->17
*********刪除元素*************
zhangsan---->18
lisi---->19
已成功刪除。
注意:
既然ArrayList類可以為List接口實例化,那么也就可以為Collection接口實例化,但是這個時候已經不可以繼續使用get()方法操作了,所以此時只能夠將Collection變為對象數組后返回。
package com.njupt.study.collection; import java.util.ArrayList; import java.util.Collection; import java.util.List; class Student { private String name; private int age; public Student(){}; public Student(String n,int a) { this.name = n; this.age = a; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean equals(Object obj) { if(this == obj) { return true; } if(obj == null) { return false; } if( ! (obj instanceof Student) ) { return false; } Student other = (Student) obj; if(this.name.equals(other.name) && this.age == other.age) { return true; } return false; } /*public boolean equals(Object obj) { if (this == obj) { return true ; } if (obj == null) { return false ; } if (! (obj instanceof Student)) { return false ; } Student t = (Student) obj ; if(this.name.equals(t.name ) && this.age == t.age) { return true ; } return false ; } */ public String toString() { return this.name+"---->"+this.age; } } public class Demo02 { public static void main(String[] args) { Collection<Student> list = new ArrayList<Student>(); list.add(new Student("zhangsan",18)); list.add(new Student("lisi",19)); list.add(new Student("wangwu",17)); System.out.println(list); System.out.println("******************"); Student[] all = list.toArray(new Student[]{}); for(int i=0;i<all.length;i++) { System.out.println(all[i]); } } }
2. LinkedList
同樣實現List接口的LinkedList與ArrayList不同,ArrayList是一個動態數組,而LinkedList是一個雙向鏈表。所以它除了有ArrayList的基本操作方法外還額外提供了insert方法在LinkedList的首部或尾部。
由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重鏈表的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。
與ArrayList一樣,LinkedList也是非同步的。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
ArrayList擅長於隨機訪問。同時ArrayList是非同步的。
3. Vector
Vector類是在JDK 1.0的時候所推出的最早的實現數據結構支持的類,其最早翻譯為向量,但是到了JDK 1.2之后,為了使其可以繼續使用,所以讓這個類多實現了一個List接口,這樣一來,就造成了Vector子類的操作方法比ArrayList更多,但是一般這些多的方法很少考慮。
與ArrayList相似,但是Vector是同步的。所以說Vector是線程安全的動態數組。它的操作與ArrayList幾乎一樣。
4.Stack
Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop 方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。
ArrayList和Vector的區別:
ArrayList和Vector都屬於List接口的常用子類,這兩者在操作形式以及概念上的區別如下:
| No. |
區別點 |
ArrayList |
Vector |
| 1 |
推出時間 |
JDK 1.2時推出,屬於新的類 |
JDK 1.0時推出,屬於舊的類 |
| 2 |
性能 |
使用異步處理方式,性能較高 |
采用同步處理操作,性能相對較低 |
| 3 |
安全性 |
非線程安全 |
線程安全 |
| 4 |
輸出 |
|
|
因為Java主要從事於網絡的開發,所以使用異步的處理操作形式要比使用同步(Synchronized)更多。
3)不允許重復的子接口:Set
Set接口與List接口最大的不同在於里面的數據不允許有重復,那么首先觀察一下Set接口的繼承結構:
public interface Set<E> extends Collection<E> |
Set是一種不包括重復元素的Collection,與List一樣,它同樣運行null的存在但是僅有一個。由於Set接口的特殊性,所有傳入Set集合中的元素都必須不同,同時要注意任何可變對象,如果在對集合中元素進行操作時,導致e1.equals(e2)==true,則必定會產生某些問題。實現了Set接口的集合有:EnumSet、HashSet、TreeSet。
1.EnumSet
是枚舉的專用Set。所有的元素都是枚舉類型。
2.HashSet
哈希(Hash)是一種數據的排列算法,這種算法的典型操作就是加塞算法,那塊有地就保存,所以只要帶有hash都是無序的。HashSet堪稱查詢速度最快的集合,因為其內部是以HashCode來實現的。它內部元素的順序是由哈希碼來決定的,所以它不保證set 的迭代順序;特別是它不保證該順序恆久不變。
1 package com.njupt.study.collection; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Demo03 { 7 8 /** 9 * @param args 10 */ 11 public static void main(String[] args) { 12 Set<String> all = new HashSet<String>(); 13 all.add("Hello"); 14 all.add("World"); 15 all.add("Hello"); // 重復數據 16 System.out.println(all); 17 } 18 19 }
輸出結果為:
[World, Hello]
通過本程序可以發現,里面所保存的順序改變,而且如果有重復的數據也不能夠保存。
3.TreeSet
在TreeSet子類里面所有保存的數據是沒有重復的,而且可以為用戶自動的進行排序。基於TreeMap,生成一個總是處於排序狀態的set,內部以TreeMap來實現。它是使用元素的自然順序對元素進行排序,或者根據創建Set 時提供的 Comparator 進行排序,具體取決於使用的構造方法。
關於排序的說明:既然在TreeSet中的所有數據都可以排序,而且里面設置的數據也都是對象,那么下面就使用自定義類完成。
但是這個時候必須注意到一點:對於現在的TreeSet子類而言,由於其要對一組對象進行排序,那么這個對象所在的類就一定要實現Comparable接口,用於指定排序規則;
1 package com.njupt.study.collection; 2 3 import java.util.Set; 4 import java.util.TreeSet; 5 6 7 class Student implements Comparable<Student> 8 { 9 private String name; 10 11 private int age; 12 13 public Student(){}; 14 15 public Student(String n,int a) 16 { 17 this.name = n; 18 this.age = a; 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 public int getAge() { 30 return age; 31 } 32 33 public void setAge(int age) { 34 this.age = age; 35 } 36 37 38 public boolean equals(Object obj) 39 { 40 if(this == obj) 41 { 42 return true; 43 } 44 45 if(obj == null) 46 { 47 return false; 48 } 49 50 if( ! (obj instanceof Student) ) 51 { 52 return false; 53 } 54 55 Student other = (Student) obj; 56 57 if(this.name.equals(other.name) && this.age == other.age) 58 { 59 return true; 60 } 61 return false; 62 } 63 64 65 public String toString() 66 { 67 return this.name+"---->"+this.age; 68 } 69 70 @Override 71 public int compareTo(Student o) { 72 if (this.age > o.age) { 73 return 1; 74 } else if (this.age < o.age) { 75 return -1; 76 } else { 77 return this.name.compareTo(o.name); 78 } 79 } 80 } 81 82 public class Demo04 { 83 84 /** 85 * @param args 86 */ 87 public static void main(String[] args) { 88 Set<Student> all = new TreeSet<Student>(); 89 all.add(new Student("張三",20)) ; 90 all.add(new Student("李四",19)) ; 91 all.add(new Student("王五",22)) ; // 年齡一樣 92 all.add(new Student("趙六",22)) ; // 年齡一樣 93 all.add(new Student("孫七",25)) ; 94 all.add(new Student("孫七",25)) ; // 插入了重復的數據 95 all.remove(new Student("李四",19)) ; // 刪除一個數據 96 System.out.println(all); 97 } 98 99 }
顯示結果為:[張三---->20, 王五---->22, 趙六---->22, 孫七---->25]
需要注意:
實現了對自定義類對象的排序,並且可以判斷重復元素,但是TreeSet類只是利用了Comparable完成了重復元素的判斷,可是這種判斷並不是真正意義上的重復元素判斷。
如果說現在要想判斷一個對象是否重復,嚴格來講,這個操作是由Object類所提供的,在任何一個子類里面需要覆寫Object類中的以下兩個方法;
· 對象比較:public boolean equals(Object obj);
· 對象編碼:public int hashCode();
hashCode()方法是用於進行對象編碼計算的操作方法,如果要想進行對象的編碼,那么肯定需要一些數學上的支持,這里就不詳細講解了。
