我們在前面學過Java數組,Java數組的長度是固定的,在同一個數組中只能存放相同的類型的數據。數組可以存放基本類型的數據,了可以存入對象引用的數據。
在創建數組時,必須明確指定數組的長度,數組一旦創建,其長度就不能被改變,在許多應用的場合,一組數據的數目是不固定的,比如一個單位的員工數目是變化的,有老的員工跳槽,也有新的員工進來。
爲了使程序能方便地存儲和操縱數目不固定的一組數據,JDK中提供了Java集合,所有Java集合類都位於java.util包中,與Java數組不同,Java集合不能存放基本數據類型數據,而只能存放對象的引用。
java集合類分爲三種:
Set(集合):集合中對象不按特定的方式排序。並且沒有重複對象,但它的有些實現類對集合中的對象按特定方式排序。
List(列表):集合中的對象按照索引位置排序,可以有重複對象,允許按照對象在集合中的索引位置檢索對象,List和數組有些相似。
Map(映射):集合中的每一個元素包含一對鍵對象和值對象,集合中沒有重複的鍵對象,值對象可以重複,它的有些實現類能對集合中的鍵對象進行排序。
Java的主要集合類的框架圖:
Collection和Iterator接口:
在Collection接口中聲明瞭適用於Java集合(只包括Set和List)通用方法。
Collection接口的方法
方法 |
描述 |
boolean add(Object o) |
向集合中加入一個對象的引用 |
void clear() |
刪除中集合中所有對象,即不再對持有對象的引用 |
boolean contains(Object o) |
判斷在集合中是否含有特定對象的引用 |
boolean isEmpty() |
判斷集合是否爲空 |
Iterator iterator() |
返回一個Iterator對象,可用它來遍歷集合中的元素 |
boolean remove(Object o) |
從集合中刪除一個對象的引用 |
int size() |
返回集合中元素的數目 |
Object[] toArray() |
返回一個數組,該數組包含集合中的所有元素 |
Set接口和List接口都繼承了Collection接口,而Map接口有繼承Collection接口,而繼承了因此可以對Set對象和List對象調用以上方法,但是不能對Map對象調用以上方法
Collection接口的iterator()和toArray()方法都用於獲得集合中的所有元素,前者返回一個Iterator對象,後者返回一個包含集合中所有元素的數組。
Iterator隱藏底層集合的數據結構,向客戶程序提供了遍歷各種類型的集合的統一接口中。Iterator接口中聲明瞭如下方法:
l hasNext():判斷集合中的元素是否遍歷完畢,如果沒有,就返回true.
l next():返回下一個元素。
l remove():從集合中刪除上一個由next()方法返回的元素
注意:如果集合中的元素沒有排序,Iterator遍歷集合中元素的順序是任意的,並不一定與向集合中加入元素的順序一致的
Set(集)
Set是最簡單一種集合,集合中的對象不按特定方式排序,並且沒有重複對象。Set接口主要有兩個實現類:HashSet和TreeSet。HashSet類按照哈希算法來存取集合中的對象,存取速度比較快。HashSet類還有一個子類LinkedHashSet類,它不僅實現了哈希算法,而且實現了鏈表數據結構,鏈表數據結構能提高插入和刪除元素的性能。TreeSet類實現了SortedSet接口中,具有排序功能。
List(列表)
List的主要特徵是其元素以線性方式存儲,集合中允許存放重複對象。List接口主要的實現類包括:
l ArrayList-ArrayList代表長度可變的數組。允許對元素進行快速的隨機訪問,但是向ArrayList中插入與刪除元素的速度較慢。
l LinkedList-在實現中採用鏈表數據結構。對順序訪問進行了優化,向List中插入和刪除元素的速度較快,隨機訪問速度則相對較慢,隨機訪問是指檢索位於特定索引位置元素。
Map(映射)
Map(映射)是一種把鍵對象和值對象進行映射的集合。它的每一個元素都包含一對鍵對象和值對象,而值對象仍可以是Map類型。依此類推,這樣就形成了多級映射。向Map集合中加入元素時,必須提供一對鍵對象和值對象,從Map集合上檢索元素只要給出鍵對象,就會返回對應的值對象。
實例1
CollectionAll.java
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
public class CollectionAll {
public static void main(String[] args) {
List list1 = new LinkedList();
list1.add("我");
list1.add("是");
list1.add("誰");
list1.add("我");
traverse(list1);
List list2 = new ArrayList();
list2.add("我");
list2.add("是");
list2.add("誰");
list2.add("我");
traverse(list2);
Set set1 = new HashSet();
set1.add("我");
set1.add("是");
set1.add("誰");
set1.add("我");
traverse(set1);
SortedSet set2 = new TreeSet();
set2.add("我");
set2.add("是");
set2.add("誰");
set2.add("我");
traverse(set2);
LinkedHashSet set3 = new LinkedHashSet();
set3.add("我");
set3.add("是");
set3.add("誰");
set3.add("我");
traverse(set3);
Map m1 = new HashMap();
m1.put("name01", "我");
m1.put("name02", "是");
m1.put("name03", "誰");
m1.put("name04", null);
traverse(m1.keySet());
traverse(m1.values());
SortedMap m2 = new TreeMap();
m2.put("name01", "我");
m2.put("name02", "是");
m2.put("name03", "誰");
m2.put("name04", "我");
traverse(m2.keySet());
traverse(m2.values());
LinkedHashMap m3 = new LinkedHashMap();
m3.put("name01", "我");
m3.put("name02", "是");
m3.put("name03", "誰");
m3.put("name04", "我");
traverse(m3.keySet());
traverse(m3.values());
Hashtable numbers = new Hashtable();
numbers.put("name01", "我");
numbers.put("name02", "是");
numbers.put("name03", "誰");
numbers.put("name04", "我");
traverse(numbers.keySet());
traverse(numbers.values());
}
static void traverse(Collection coll) {
Iterator iter = coll.iterator();
while (iter.hasNext()) {
String elem = (String) iter.next();
System.out.print(elem + " ");
}
System.out.println();
}
}
一、集合框架中的各種實現類
HashSet類
HashSet類按照哈希算法一存取集中的對象,具有很好的存取和查找性能。當向集合中加入一個對象時,HashSet會調用對象的hashCode()方法來獲得哈希碼,然後根據這個哈希碼進一步計算出對象在集合中的存放位置。
實例2 MyHashSet.java
package collection;
import java.util.*;
public class MyHashSet{
public static void main(String[] args)
{
HashSet set = new HashSet(6);
Object[] values={"Tom","Mike","Jack","Mary","Linda","Jone"};
for(int i=0;i<values.length;i++)
set.add(values[i]); //向集合中加入對象
set.remove("Mike"); //從集合中刪除一個對象
System.out.println("size="+set.size());
Iterator iter=set.iterator(); //獲得集合中的所有對象
while (iter.hasNext()) {
String elem = (String) iter.next();
System.out.println(elem + " ");;
}
System.out.println(set.contains("Jack")); //打印true
System.out.println(set.contains("Linda")); //打印true
System.out.println(set.contains("Mike")); //打印false
}
}
TreeSet類
TreeSet類實現了SortedSet接口,能夠對集合中的對象進行排序,以下程序創建了一個TreeSet對象,然後向集合中加入4個Integer對象
實例3
TreeSetTest.java
package collection;
import java.util.*;
public class TreeSetTest {
public static void main(String[] args) {
Set set=new TreeSet();
set.add(new Integer(8));
set.add(new Integer(7));
set.add(new Integer(6));
set.add(new Integer(9));
Iterator it=set.iterator();
while(it.hasNext()){
System.out.println(it.next()+"");
}
}
}
由此我們知道當TreeSet集合中加入一個對象時,會把它插入到有序的對象序列中。
ArrayList類
ArrayList-ArrayList代表長度可變的數組。允許對元素進行快速的隨機訪問,它允許所有元素,包括null。ArrayList是線程不同步的,向ArrayList中插入與刪除元素的速度較慢
LinkedList類
LinkedList-實現了List接口,允許null元素,在實現中採用鏈表數據結構。對順序訪問進行了優化,向List中插入和刪除元素的速度較快,隨機訪問速度則相對較慢,隨機訪問是指檢索位於特定索引位置元素。
注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
實例4
StackL.java
package collection;
import java.util.LinkedList;
public class StackL {
private LinkedList list = new LinkedList();
public void push(Object v) {
list.addFirst(v);
}
public Object top() {
return list.getFirst();
}
public Object pop() {
return list.removeFirst();
}
public static void main(String[] args) {
StackL stack = new StackL();
for (int i = 0; i < 10; i++)
stack.push(new Integer(i));
System.out.println(stack.top());
System.out.println(stack.top());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
}
}
ListIterator接口
List的listIterator()方法返回一個ListIterator對象,ListIterator接口繼承了Iterator接口中,此外還提供了專門操縱列表的方法。
l add():向列表插入一個元素
l hasNext(): 判斷列表中是否還有下一個元素
l hasPrevious(): 判斷列表中是否還有上一個元素
l next(): 返回列表中的下一個元素
l previous(): 返回列表中的上一個元素
實例5
ListInserter.java
import java.util.*;
public class ListInserter {
/** 向List列表中按順序插入一數據 */
public static void insert(List list,int data){
ListIterator it=list.listIterator();
while(it.hasNext()){
Integer in=(Integer)it.next();
if(data<=in.intValue()){
it.previous();
it.add(new Integer(data)); //插入元素
break;
}
}
}
public static void main(String args[]){
List list=new LinkedList(); //創建一個鏈接列表
list.add(new Integer(3));
list.add(new Integer(2));
list.add(new Integer(5));
list.add(new Integer(9));
Collections.sort(list);//爲列表排序
insert(list,6); //向列表中插入一個元素
ListIterator it=list.listIterator();
while(it.hasNext()){
Integer elem = (Integer) it.next();
System.out.println(elem + " ");
}
}
}
實例6 比較Java數組和各種List的性能
PerformanceTester..java
package collection;
import java.util.*;
public class PerformanceTester{
private static final int TIMES=100000;
public static abstract class Tester{
private String operation;
public Tester(String operation){this.operation=operation;}
public abstract void test(List list);
public String getOperation(){return operation;}
}
static Tester iterateTester=new Tester("iterate"){
public void test(List list){ //迭代操作
for(int i=0;i<10;i++){
Iterator it=list.iterator();
while(it.hasNext()){
it.next();
}
}
}
};
static Tester getTester=new Tester("get"){
public void test(List list){ //隨機訪問操作
for(int i=0;i<list.size();i++)
for(int j=0;j<10;j++)
list.get(j);
}
};
static Tester insertTester=new Tester("insert"){
public void test(List list){ //插入操作
ListIterator it=list.listIterator(list.size()/2); //從列表的中間開始
for(int i=0;i<TIMES/2;i++)
it.add("hello");
}
};
static Tester removeTester=new Tester("remove"){ //執行刪除操作的匿名類
public void test(List list){ //刪除操作
ListIterator it=list.listIterator();
while(it.hasNext()){
it.next();
it.remove();
}
}
};
static public void testJavaArray(List list){
Tester[] testers={iterateTester,getTester};
test(testers,list);
}
static public void testList(List list){
Tester[] testers={insertTester,iterateTester,getTester,removeTester};
test(testers,list);
}
static public void test(Tester[] testers,List list){
for(int i=0;i<testers.length;i++){
System.out.print(testers[i].getOperation()+"操作:");
long t1=System.currentTimeMillis();
testers[i].test(list);
long t2=System.currentTimeMillis();
System.out.print(t2-t1+" ms");
System.out.println();
}
}
public static void main(String args[]){
List list=null;
//測試Java數組
System.out.println("----測試Java數組----");
String[] ss=new String[TIMES];
Arrays.fill(ss,"hello");
list=Arrays.asList(ss);
testJavaArray(list);
ss=new String[TIMES/2];
Collection col=Arrays.asList(ss);
//測試Vector
System.out.println("----測試Vector----");
list=new Vector();
list.addAll(col);
testList(list);
//測試LinkedList
System.out.println("----測試LinkedList----");
list=new LinkedList();
list.addAll(col);
testList(list);
//測試ArrayList
System.out.println("----測試ArrayList----");
list=new ArrayList();
list.addAll(col);
testList(list);
}
}
從結果可以看出,對Java數組進行隨機訪問和迭代操作具有最快的速度;對LinkedList進行插入和刪除具有最快的速度;對ArrayList進行隨機訪問也具有較快的速度,Vector類在各方面都沒有突出的性能,屬於歷史集合類,已經不提倡使用它。
Map
Map(映射)是一種把鍵對象和值對象進行映射的集合。它的每一個元素都包含一對鍵對象和值對象,而值對象仍可以是Map類型。依此類推,這樣就形成了多級映射。向Map集合中加入元素時,必須提供一對鍵對象和值對象,從Map集合上檢索元素只要給出鍵對象,就會返回對應的值對象。
HashMap
HashMap類按照哈希算法一存取Map中的對象,允許存儲空對象,而且允許鍵是空
實例7
Statistics.java
package collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
class Counter {
int i = 1;
public String toString() {
return Integer.toString(i);
}
}
public class Statistics {
private static Random rand = new Random();
public static void main(String[] args) {
Map hm = new HashMap();
for (int i = 0; i < 10000; i++) { // Produce a number between 0 and 20:
Integer r = new Integer(rand.nextInt(20));
if (hm.containsKey(r))
((Counter) hm.get(r)).i++;
else
hm.put(r, new Counter());
}
System.out.println(hm);
}
}
Hashtable
Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null)的對象都可作爲key或者value。
實例8
HashTable.java
package collection;
import java.util.Enumeration;
import java.util.Hashtable;
public class HashTable {
public static void main(String args[]) {
String names[] = { "張三", "李四", "王五", "趙六", "陳七",
"孫九"};
float diameters[] = { 4800f, 12103.6f, 12756.3f, 6794f, 142984f,
120536f };
Hashtable hash = new Hashtable();
for (int i = 0, n = names.length; i < n; i++) {
hash.put(names[i], new Float(diameters[i]));
}
Enumeration e = hash.keys();
Object obj;
while (e.hasMoreElements()) {
obj = e.nextElement();
System.out.println(obj + ": " + hash.get(obj));
}
}
}
HashMap和HashTable的區別
Hashtable繼承自Dictionary類,而HashMap是Map interface的一個實現
HashMap允許將null作爲一個的key或者value,而Hashtable不允許
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因爲contains方法容易讓人引起誤解。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己爲它的方法實現同步,而HashMap就必須爲之提供外同步
TreeMap
實例9
SortTreeMap.java
package collection;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class SortTreeMap {
public static void main(String args[]) {
String names[] = { "B", "E", "A", "M", "J",
"C", "H", "D", "G" };
float diameters[] = { 4800f, 12103.6f, 12756.3f, 6794f, 142984f,
120536f, 51118f, 49532f, 2274f };
Map map = new TreeMap();
for (int i = 0, n = names.length; i < n; i++) {
map.put(names[i], new Float(diameters[i]));
}
Iterator it = map.keySet().iterator();
Object obj;
while (it.hasNext()) {
obj = it.next();
System.out.println(obj + ": " + map.get(obj));
}
}
}
二、集合實用類:
Collections
在Java集合中,有一個實用類,即java.util.Collections,它的一部分方法專門用於操縱List類型集合,還有一部分方法可用於操縱所有的Collection類型或Map類型的集合。
List代表長度可變的數組,Collections的以下方法適用於List類型的集合。
以下方法適用於Collection類型或Map類型的集合
實例10
collection.java
package collection;
import java.util.*;
public class collection {
public static void main(String[] args) {
ArrayList arrayList1=new ArrayList();
arrayList1.add(new Integer(1));
arrayList1.add(new Integer(9));
arrayList1.add(new Integer(3));
arrayList1.add(new Integer(7));
arrayList1.add(new Integer(8));
arrayList1.add(new Integer(2));
arrayList1.add(new Integer(5));
arrayList1.add(new Integer(4));
arrayList1.add(new Integer(6));
System.out.print("目前ArrayList1數據內容如下:");
System.out.println(arrayList1);
Collections.sort(arrayList1);
System.out.println("排序後的內容如下:"+arrayList1);
Collections.reverse(arrayList1);
System.out.println("反排序後的內容如下:"+arrayList1);
Collections.shuffle(arrayList1);
System.out.println("重新洗牌後數據內容如下:"+arrayList1);
System.out.println("最大值"+Collections.max(arrayList1));
System.out.println("最小值"+Collections.min(arrayList1));
}
}
三、集合類中排序
在JDK類庫中,有一部分類實現了Comparable接口中,如Integer、Double和String等。Comparable接口有一個compareTo(Object o)方法,它返回整數類型。對於表達式x.compareTo(y),如果返回值爲0,則表示x和y相等地,如果返回值大於0,則表示x大於y,如果返回值小於0,則表示x小於y。
在TreeSet調用對象compareTo()方法比較集合中對象的大小
實例11
ComparatorTest.java
package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Person implements Comparable {
String firstName, lastName;
public Person(String f, String l) {
this.firstName = f;
this.lastName = l;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return "[ name=" + firstName + ",name=" + lastName + "]";
}
public int compareTo(Object obj) {
Person emp = (Person) obj;
int deptComp = firstName.compareTo(emp.getFirstName());
return ((deptComp == 0) ? lastName.compareTo(emp.getLastName())
: deptComp);
}
public boolean equals(Object obj) {
if (!(obj instanceof Person)) {
return false;
}
Person emp = (Person) obj;
return firstName.equals(emp.getFirstName())
&& lastName.equals(emp.getLastName());
}
}
public class ComparatorTest implements Comparator {
public int compare(Object obj1, Object obj2) {
Person emp1 = (Person) obj1;
Person emp2 = (Person) obj2;
int nameComp = emp1.getFirstName().compareTo(emp2.getFirstName());
return ((nameComp == 0) ? emp1.getLastName().compareTo(
emp2.getLastName()) : nameComp);
}
public static void main(String args[]) {
String names[] = { "張三", "李四", "王五", "趙六", "陳七", "孫八",
"周九" };
// Convert to list
List list = new ArrayList(Arrays.asList(names));
// Ensure list sorted
Collections.sort(list);
System.out.println("List排序: [length: " + list.size() + "]");
System.out.println(list);
// Search for element in list
int index = Collections.binarySearch(list, "李四");
System.out.println("發現位置 " + index);
// Search for element not in list
index = Collections.binarySearch(list, "、楚一");
System.out.println("沒有發現楚一" + index);
// Insert
int newIndex = -index - 1;
list.add(newIndex, "馮二");
System.out.println("增加了馮二: [length: " + list.size()
+ "]");
System.out.println(list);
// Min should be Bart
System.out.println(Collections.min(list));
// Max should be Roy
System.out.println(Collections.max(list));
Comparator comp = Collections.reverseOrder();
// Reversed Min should be Roy
System.out.println(Collections.min(list, comp));
// Reversed Max should be Bart
System.out.println(Collections.max(list, comp));
}
}
四、集合類的歷史
四、集合類的歷史
在早期JDK1.0版本中,代表集合的類只有Vector、Stack、Enumeration、Hashtable、Properties。從JDK1.2版本中開始,纔出現了Collection、Set、List和Map接口及各種實現類。它們構成了完整的集合框架。JDK10.版本中的集合類也稱歷史集合類
歷史集合類
歷史集合類 |
描述 |
缺點 |
新集合框架的替代類 |
Vector |
集合中的元素有索引位置,在新的集合框架中把它改爲實現了List接口 |
採用了同步機制,影響操縱集合的性能 |
ArrayList和LinkedList |
Stack |
表示堆棧,支持後進先出的操作 |
採用了同步機制,影響操縱集合的性能:Stack繼承了Vector類,使得Stack不能作爲嚴格的堆棧,還允許隨機訪問 |
LinkedList |
Hashtable |
集合中的每一個元素包含一對鍵與值。在新的集合框架中把它改爲實現了Map接口 |
採用了同步機制,影響操縱集合性能 |
HashMap |
Properties |
集合中的每個元素包含一對鍵與值,繼承了Hashtable |
採用了同步機制,影響了操縱集合的性能 |
無 |
Enumeration |
用於遍歷集合中元素 |
只能與Vector和Hashtable等歷史集合配套使用:Enumeration類的名字較長,沒有Iterator類名字簡短 |
Iterator |
從JDK1.2版本開始,對Vector和Hashtable做了修改,使它們分別實現了List和Map接口中。儘管如此,由於Vector、Stack、Hashtable和Enumeration在實現中都使用了同步機制,併發性能差,因此不提倡使用它們,
Properties類是一種特殊的Map類,它繼承了Hashtable(Object,Object)類。Properties類的load()方法可用來從輸入流中讀取鍵與值
實例12
myapp.properties
color=red
shape=circle
user=Tom
PropertiesTester.java
package collection;
import java.util.*;
import java.io.*;
public class PropertiesTester{
public static void print(Properties ps){
Set keys=ps.keySet();
Iterator it=keys.iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=ps.getProperty(key);
//使用getProperty可以返回myapp.properties文件中的一個屬性
System.out.println(key+"="+value);
}
}
public static void main(String args[])throws IOException{
Properties ps=new Properties();
//myapp.properties文件與PropertiesTester類的.class文件位於同一個目錄下
InputStream in=PropertiesTester.class.getResourceAsStream("myapp.properties");
ps.load(in);//把輸入流中的數據加載到Properties對象中
print(ps);
ps=System.getProperties();//該方法返回一個Properties對象,在這個對象中包含了一系列的系統屬性
print(ps);
}
}
小結:
我們介紹了幾種常用Java集合類的特性和使用方法。爲了保證集合正常工作,有些集合類對存放的對象有特殊的要求:
HashSet和HashMap具有好的性能,是Set和Map首選的實現類,只有在需要排序的場合,才考慮用TreeSet和TreeMap。LinkedList和ArrayList各有優缺點,如果經常對元素執行插入和刪除操作,那麼可用LinkedList,如果經常隨機訪問元素,那麼可用ArrayList.
課後習題:
1. Set和List有哪些區別?
2. Collection與Collections有什麼區別?
3. 比較Java數組、ArrayList和LinkedList在查詢和存取元素方面的性能
4. HashMap和HashTable的區別
5. 編寫一個程序,讀入一系列名字並將它們存儲在LinkedList中,不能存儲重複的名字,並允許用戶查找一個名字。
附錄