Java編程思想——細話Java持有對象(下)

一、Map集合

由於Map集合的每一個元素是由鍵和值組成的,所以他具有將對象映射到對象的能力。

  • containsKey()——查看集合中是否包含某鍵值。
  • containsValue()——查看是否包含某個值。
  • keySet()——獲取所有鍵
  • values()——獲取所有值
  • get()——獲取指定鍵對應的值

Map可以很容易被擴展爲多維:

Map<Person,List<String>> map=new HashMap<Person,List<String>>();//可以存儲每個人擁有多少本書
import java.util.*;

public class Box{	
    public static void main(String[] args){
	Map<String,List<String>> map=new HashMap<String,List<String>>();	
	map.put("Luck",Arrays.asList("A_Book","B_Book","C_Book"));
	map.put("Kate",Arrays.asList("D_Book","E_Book","F_Book"));
	map.put("Dawn",Arrays.asList("Q_Book","X_Book","Y_Book"));
	map.put("Marry",Arrays.asList("G_Book","H_Book","I_Book"));
	System.out.println("map: "+map);
	//判斷集合中是否包含某個鍵
	System.out.println("containsKey: "+map.containsKey("Luck"));
	//判斷集合中是否包含某個值
	System.out.println("containsValue: "+map.containsValue(22));
	//獲取集合中的所有鍵
	for(String people:map.keySet()){
            System.out.println("Name: "+people);
	}
	//獲取集合中的所有值	
        for(List<String> list:map.values()){
	    System.out.println("AllBooks"+list);
	}
	//獲取指定鍵對應的值	
        for(String people:map.keySet()){
	    System.out.println("Name: "+people);
	    System.out.println("---"+map.get(people));
	}		
    }
}


從我們的例子可以發現,Map集合是不能直接使用迭代器的,因爲迭代器是針對單列集合設計的。在實際使用中,可以使用foreach進行遍歷,或者將Map集合轉換成Set集合然後使用迭代器進行遍歷。

eg2:

import java.util.*;

public class Box{	
	public static void main(String[] args){
		for(Map.Entry entry:System.getenv().entrySet()){
			System.out.println(entry.getKey()+"-----"+entry.getValue());
		}
	}
}

運行結果:


     System.getenv()返回的是一個存儲電腦所有環境變量的Map集合。Map.entrySet()返回的是一個Set<Map.Entry<K,V>>,Map.Entry 是Map中的一個接口,他的用途是表示一個映射項(裏面有Key和Value),而Set<Map.Entry<K,V>>表示一個映射項的Set。Map.Entry裏有相應的getKey和getValue方法,即JavaBean,讓我們能夠從一個項中取出Key和Value。Map也是通過這個方法使用迭代器的。

二、Queue(隊列)

——隊列是一種先進先出(FIFO)的容器。即從容器的一端放入,從另一端取出,且存放的順序與取出的順序是一致的。隊列在併發編程中特別重要,因爲它們以可安全的將對象從一個任務傳輸給另一個任務。

      LinkedList集合提供了支持隊列的方法,並實現了Queue接口,因此LinkedList可以作爲Queue的一種實現。在實際使用中一般會將LinkedList向上轉型爲Queue。

  • offer()——在允許的情況下,將一個元素插入到隊尾,或者返回false;
  • peek()——在不移除元素的情況下返回隊頭,當對列爲空時返回null;
  • element()——在不移除元素的情況下返回隊頭,當隊列爲空時拋出NoSuchElementException異常;
  • poll()——移除元素並返回隊頭,在隊列爲空時返回null;
  • remove()——移除元素後返回隊頭,在隊列爲空時拋出NoSuchElementException異常;

注:之所以在使用LinkedList實現隊列時我們採用向上轉型,是爲了窄化LinkedList,以避免有一些不必要的功能。

小例子:

import java.util.*;

public class Box{	
	public static void main(String[] args){
		Queue<Character> queue=new LinkedList<Character>();	
		//向隊列中存儲一組字符
		for(char c:"hellojava!".toCharArray()){
			queue.offer(c);
		}
		System.out.print(queue);
		System.out.println();
		while(queue.peek()!=null){
			System.out.print(queue.peek()+"---");
			//System.out.print(queue.remove());
			System.out.print(queue.poll());
			System.out.println();
		}
		System.out.println();
		System.out.print(queue);
	}
}

運行結果:


從運行結果可以看出peek()每次返回的和remove()刪除的是同一個元素,而且是先進先出的。

——PriorityQueue(優先級隊列)

       聲明下一個彈出元素是最需要的元素,即具有最高優先級的元素。一旦加入優先級這個因素,那麼處理的先後順序將與到達時間無關。所以在PriorityQueue中調用peek()、poll()、remove()方法時,獲取的元素都是列表中優先級最高的元素。PriorityQueue可以很容易的和Integer、String、Character一起使用,因爲這些類都內建了自然排序,如果我們想在PriorityQueue中使用自己的類,需要建立額外的功能以產生自然排序,或者必須提供自己的Comparator。

import java.util.*;

public class Box{	
	public static void main(String[] args){
		String str="  Hello java and j2ee!";
		List<String> list=Arrays.asList(str.split(""));
		//按自然排序進行存儲
		PriorityQueue<String> queue1=new PriorityQueue<String>(list);
        System.out.print(queue1);	
		System.out.println();		
		//按照自然排序的反序進行存儲
		PriorityQueue<String> queue=new PriorityQueue<String>(list.size(),Collections.reverseOrder());	
		queue.addAll(list);
		System.out.print(queue);
	}
}

三、迭代器

      迭代器(一種設計模式)——是一個對象,其可以遍歷並選擇序列中的對象,因爲創建其需付出的代價很小,所以被稱爲輕量級對象。迭代器可以將遍歷序列的操作與序列底層的結構分離,所以其可以統一對容器的訪問方式

*Java中提供了Iterator迭代器——

  • 使用Iterator()方法要求容器返回一個Iterator。Iterator將準備好返回序列的第一個元素。
  • 使用next()獲取序列中的下一個元素。
  • 使用hasNext()檢查序列中是否還有元素。
  • 使用remove()將移除迭代器返回的最後一個元素。
import java.util.*;
public class Box{
	
	public static void main(String[] args){
		Set<String> t_box=new TreeSet<String>();	
		Collections.addAll(t_box,"B,A,C,E,D,F".split(","));
		//創建一個迭代器
		Iterator it=t_box.iterator();
		while(it.hasNext()){
		    //迭代器的remove方法若放在next()方法前會報錯	
		    //it.remove();
		    System.out.print(it.next()+" ");
		    it.remove();
		    break;
		}
		System.out.println();
		//採用for循環同樣可以實現集合的遍歷
		for(String t:t_box){
		    System.out.print(t+" ");
		    //但是在遍歷中不能進行刪除操作,因爲每刪除一個元素,t_box.size()的大小會發生變化	
		    //t_box.remove(t);
		}
	}
}

運行結果:


從運行結果可以看出迭代器在遍歷的同時可以對集合中的元素進行刪除操作,刪除迭代器最後返回的值。但是在使用迭代器的刪除功能時需要注意倆點:一是,要在.next()方法後使用;二是,不能連續多次使用remove()方法。迭代器之所以可以在遍歷中刪除元素,是因爲其在刪除中會維持一個標誌來記錄目前是不是處於可刪除狀態。

*Java中提供了ListIterator迭代器——

     是一個更加強大的Iterator子類型,但是其只能用於各種List類的訪問。要注意的一點是,Iterator只能向前移動,而ListIterator可以雙向移動。它可以產生相對於迭代器在列表中指向的當前位置的前一個和後一個元素的索引,並可以使用set()方法替換它訪問過的最優一個元素。

倆個創建方式:

  1. 調用ListIterator()方法產生一個指向List開始處的迭代器。
  2. 調用ListIterator(index)方法產生一個指向List中索引爲index的元素位置的迭代器。
import java.util.*;
public class Box{	
	public static void main(String[] args){
		List<String> list=new ArrayList<String>();	
		Collections.addAll(list,"A,B,C,D,E,F".split(","));
		//創建遍歷列表所有元素的迭代器
		ListIterator it=list.listIterator();
		while(it.hasNext()){
		    System.out.print(it.next()+" ");
		}
		System.out.println();
		//創建遍歷下表爲3開始的列表元素
		ListIterator it_index=list.listIterator(3);
		while(it_index.hasNext()){
		    System.out.print(it_index.next()+" ");
		    it_index.set("1");//將遍歷的元素替換爲1
		}
		System.out.println();
		
		ListIterator it2=list.listIterator();
		while(it2.hasNext()){
		    System.out.print(it2.next()+" ");
		}
	}
}

運行結果:


從結果可以看出使用迭代器的set()方法,可以將遍歷的元素都替換爲指定值。

*Foreach與迭代器——

       Foreach在遍歷集合時之所以能與迭代器有一樣的效果,是因爲Java SE5引入了新的被稱爲Iterable的接口,該接口包含一個能產生Iterator的iterator()方法,並且Iterable接口被foreach用來在序列中移動(也就是在foreach的底層實際也是迭代器)。也因此,能夠與foreach一起工作成爲了所有Collection對象的特性

*適配器方法慣用法——

      想要使用向前和向後迭代的倆種方法遍歷一個列表,如果直接繼承並覆蓋iterator()方法,只能替換現有方法,而不能實現要求。此時使用適配器方法(設計模式的一種思想)的慣用法可以解決問題。

      這裏不能使用覆蓋,而是添加一個能夠產生Iterable對象的方法,該對象可以用於foreach語句中。這樣我們可以有多種使用foreach的方式:

import java.util.*;

public class Box{	
	public static void main(String[] args){
		List<Integer> list=new ArrayList<Integer>();
		Integer[] a={1,2,3,4,5};
		list=Arrays.asList(a);
		ReversedList<Integer> rl=new ReversedList<Integer>(list);
		for(Integer i:rl){
			System.out.print(i);
		}
		System.out.println();
		for(Integer i:rl.reversed()){
			System.out.print(i);
		}
	}
}
class ReversedList<T> extends ArrayList<T>{
	public ReversedList(Collection<T> c){super(c);}
	public Iterable<T> reversed(){
		return new Iterable<T>(){
			public Iterator<T> iterator(){
				return new Iterator<T>(){
					int current=size()-1;
					public boolean hasNext(){return current>-1;}
					public T next(){return get(current--);}
					public void remove(){throw new UnsupportedOperationException();}
				};
			}
			
		};
	}
	
}

運行結果:


——什麼是適配器模式:

       將一個接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。我的理解是有一些接口我們只需要其中的一部分方法(也許只要一個),有些類我們和其沒有繼承關係但需要其中的一個或多個方法,此時我們沒必要實現接口中的所有方法,更沒必要繼承一個類(也許此時你已經繼承了另一個類),我們要想實現我們的要求就可以通過適配器創建一個新的接口,接口中由我們需要的那些方法,然後創建一個類實現此接口進行方法調配。

總結:

  • 數組將數字和對象聯繫起來。保存類型明確的對象,查詢對象不需對結果做類型轉換。它可以保存多維的基本類型的數據。但是一旦生成,不能改變其容量
  • Collection保存單一元素,而Map存儲鍵值對類型的數據。泛型確定了應該存放的數據類型,從容器中獲取元素時不用進行類型轉換。這類容器可以自動改變存放數據的空間大小不能持有基本類型,但是Java中的自動拆裝箱機制可以彌補這一個缺點。
  • List集合建立了數字索引與對象的關聯,因此數組和List集合都是排好序的容器,但是List可以自動擴充容量,但是數組不可以。List集合包含了倆個實現類ArrayList和LinkedList,前者適合進行大量的隨機訪問,後者則更適合進行有大量插入或刪除元素的操作
  • Map是一種將對象與對象相關聯的設計。HashMap設計用來快速訪問TreeMap保持”鍵“始終處於排序狀態,所以沒有HashMap快;LinkedHashMap保持元素插入的順序,但是也通過散列提供了快速訪問的能力
  • Set集合不可以接受重複元素(依賴於equals()),HashSet提供了快速查詢速度,而TreeSet保持元素處於排序狀態。LinkedHashSet保持元素插入的順序。
  • Vector、Hashtable、Stack已經被淘汰,所以在實際開發中儘量不要使用。
  • 生成Iterator是將隊列與消費隊列的方法連接在一起耦合度最小的方式,並且與實現Collection相比,它在序列類上所施加的約束也少許多。


——點線框表示接口

——實線框表示實現類

——黑色粗線框表示常用容器

——空心箭頭表示實現接口

——實心箭頭表示可以實現指向類的對象

從這張圖中可以很好的看到各個容器之間的關係,需要我們記住他。

----------------------------------------------------------------------

容器的使用在實際開發中至關重要,這裏我們需要熟練使用各個集合和迭代器。另一方面從最後的例子中會發現內部類在開發中的重要性,設計模式也需要我們花大功夫去學習,要深刻理解設計模式的思想。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章