SCJP筆記_章七_泛型與集合

第七章 泛型與集合html

 

 

7.1 重寫hashCode()和equals()方法java

考試目標6.2 區分hashCode()和equals()方法的正確設計和錯誤設計,並解釋 == 和equals()方法的不一樣。程序員

 

toString()方法算法

剛沒有重寫toString方法時,顯示該對象哈希碼的無符號十六進制表示。如:數組

TestObject@a47e0.安全

 

7.1.1 重寫equals()方法框架

 

使用==來判斷兩個引用變量是否引用了同一個對象。ide

使用equals()來判斷兩個對象在乎義上是否等價。函數

 

不重寫equals()意味着什麼?工具

若是不重寫equals(),則對象將不會是有用的哈希鍵。

若是不重寫equals(),則不一樣的對象不能認爲是等價的。

總之,若是不重寫,則equals()只使用==運算符進行比較。

public class TestEquals {

	public static void main(String[] args) {
		TestEquals t1 = new TestEquals();
		TestEquals t2 = new TestEquals();
		System.out.println(t1.toString());
		System.out.println(t2.toString());
		System.out.println(t1==t2);
		System.out.println(t1.equals(t2));
	}

}
//result:
//testGenerics.TestEquals@35ce36
//testGenerics.TestEquals@757aef
//false
//false

 

 

實現equals()方法

public class TestEquals2 {

	private int id;	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}

	public boolean equals(Object o){
		if ((o instanceof TestEquals2)&&(this.id==((TestEquals2) o).getId())) {
			return true;			
		}else{
			return false;
		}
	}
	
	public static void main(String[] args) {
		TestEquals2 t1 = new TestEquals2();
		t1.setId(1);
		TestEquals2 t2 = new TestEquals2();
		t2.setId(1);
		TestEquals2 t3 = new TestEquals2();
		t3.setId(2);
		System.out.println(t1.toString());
		System.out.println(t2.toString());
		System.out.println(t1==t2);
		System.out.println(t1.equals(t2));
		System.out.println(t1.equals(t3));
	}
}
//result:
//testGenerics.TestEquals2@35ce36
//testGenerics.TestEquals2@757aef
//false
//true
//false

 

 

equals()契約

  • 自反的 對於任何引用值x,x.equals(x)都應該返回true。
  • 對稱的 對於任何引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)才返回true。
  • 傳遞的 對於任何引用值x,y,z,若是x.equals(y)返回true時,而且y.equals(z)返回true。
  • 一致的 對於任何引用值x和y,若是在該對象上的相等性比較中所使用的信息沒有作修改,則對x.equals(y)的屢次調用要一致地返回true或者false。
  • 對於變元爲null的都應返回false

  

7.1.2 重寫hashCode()方法

 

理解哈希碼

一個類的許多對象,根據同一算法生成的值分類(分桶),當查找時,先由hashCode()算出值,肯定在哪一個哈希桶裏。

不一樣的對象可能有相同的哈希值;重寫過equals方法的相同對象,應該有同樣的哈希值。

 

實現hashCode()方法

說白了就是怎樣的hashCode算法,都返回一個默認值,好比1932,這樣的作法是「合法的」,可是並不合適。這就至關於把全部的對象都放在了一個桶中,並無達到分類、管理對象的目的。

「適當的」方法是將如id等,能表明對象惟一性等的屬性加入到hashCode的算法中,儘量的將各個桶平衡。

 

hashCode()契約

  • 在Java應用程序的同一個執行期間,若是沒有修改對象的equals()比較內使用的任何信息,則不管何時在相同的對象上屢次調用hashCode()方法時,它必須一致地返回同一個整數。
  • 若是equals()方法兩個對象是相等的,則在這兩個對象的任意一個上調用hashCode()方法,必須產生相同的整數結果。
  • 若是根據equals()方法兩個對象是不相等的,則在這兩個對象的任意一個上調用hashCode()方法,並不要求產生不一樣的整數結果。然而程序員應該知道,爲不相等的對象產生不一樣的整數結果可能提升哈希表的性能。

 

7.2 集合

 

考試目標6.1 給定一個設計場景,判斷應該使用哪些集合類和/或接口來恰當地實現該設計,包括使用Comparable接口。

 

7.2.1 用集合作什麼

  • 將對象添加到集合。
  • 從集合中刪除對象。
  • 找出一個對象(或一組對象)是否位於集合內。
  • 從集合中檢索對象(不刪除它)。
  • 迭代遍歷集合,逐個查看每一個元素(對象)。

集合框架的重點接口和類

集合接口和類 

  • collection      表示概念上的集合。
  • Collection     表示java.util.Collection接口,Set、List和Queue擴展自它。Map並不擴展自它。
  • Collections   表示java.util.Collections類,它擁有大量的靜態實用工具方法,用於集合。

集合的4種基本形式:

List 事物列表(實現List的類)

Set 具備惟一性的事物(實現Set的類)

Map 具備惟一ID的事物(實現Map的類)

Queue 按照被處理的順序排列的事物

 

ordered(有序的,有秩序的,有先來後到的)

表示該集合可以按照特定的順序(而不是隨機的順序)迭代遍歷這個集合。

 

sorted(已排序,有順序的,有順序規則的)

表示集合中的順序是根據某個或某些規則肯定的。

  

 

7.2.2 List接口

List關心的是索引。

 

ArrayList

能夠將它理解成一個可增加的數組。

它不是同步的。

它提供快速迭代和快速隨機訪問的能力。

 

Vector

它就是個同步的ArrayList,線程安全。

 

LinkedList

迭代比ArrayList慢,但適合用來快速插入和刪除。

 

 

7.2.3 Set接口

Set關心惟一性,它不容許重複

 

HashSet

HashSet 是一種 unsorted、unordered 的 Set 。它使用被插入對象的哈希碼,所以,hashCode()實現越有效,將獲得的訪問性能就越好。、

速度訪問,保證沒有重複,不提供任何順序。

 

LinkedHashSet

是HashSet的ordered版本,按照插入順序迭代

 

TreeSet

是sorted的,按照元素的天然順序進行升序排列。或者構造一個帶構造函數的TreeSet,它讓你經過使用Comparable或Comparator爲集合提供本身的規則。

 

7.2.4 Map接口

Map關心惟一的標識符

 

HashMap

最快速地更新鍵/值對。容許一個null鍵和多個null值。

 

Hashtable

HashMap的同步版本,不容許null鍵或null值。

 

LinkedHashMap

迭代更快,按照插入順序或者最後訪問的順序迭代。容許一個null鍵和多個null值。

 

TreeMap

一種排序映射。

 

7.2.5 Queue接口

 

PriortyQueue

按照元素的優先級排序的「待執行任務」的列表。

 

 

Map Set List ordered sorted
HashMap x    
Hashtable x    
TreeMap x     sorted 按照天然順序或自定義比較規則
LinkedHashMap x     按照插入順序或最後的訪問順序
HashSet   x  
TreeSet   x   sorted 按照天然順序或自定義比較規則
LinkedHashSet   x   按照插入順序
ArrayList     x 按照索引
Vector     x 按照索引
LinkedList     x 按照索引
PriorityQueue       sorted 按照「要執行的任務」的順序

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

7.3 使用集合框架

 

考試目標6.3 編寫使用NavigableSet和NavigableMap接口的代碼。

考試目標6.5 利用java.util包中的功能編寫代碼,經過排序、執行折半查找或將數組轉換成列表操做列表。利用java.util包中的功能編寫代碼,經過排序、執行折半查找或將數組轉換成列表來操做數組。利用java.util.Comparator和java.lang.Comparable接口來影響列表或數組的排序。此外,還要認識基本包裝器類和java.lang.String在排序時的「天然順序」的影響。

 

7.3.1 ArrayList基礎

與數組相比,ArrayList創建時不用指定長度,能夠動態增加,提供更增強大的插入和查找機制。

 

7.3.2 用集合進行自動裝箱

Java5中,能夠將集合中的基本類型自動裝箱爲包裝類型。

 

7.3.3 排序集合與數組

 

排序集合

import java.util.ArrayList;
import java.util.Collections;

public class TestSort1 {
	public static void main(String[] args) {
		ArrayList<String> stuff = new ArrayList<String>();
		stuff.add("Denver");
		stuff.add("Boulder");
		stuff.add("Vail");
		stuff.add("Aspen");
		stuff.add("Telluride");
		System.out.println("unsorted " + stuff);
		Collections.sort(stuff);      //天然排序
		System.out.println("sorted   " + stuff);		
	}
}
unsorted [Denver, Boulder, Vail, Aspen, Telluride]
sorted   [Aspen, Boulder, Denver, Telluride, Vail]

 

Comparable接口

Comparable接口由Collections.sort()方法和java.util.Arrays.sort()方法用來分別排序List和對象數組。

要實現java.lang.Comparable,類必須實現一種方法compareTo()。下面是compareTo()的調用:

int x = thisObj.compareTo(anotherObj);

該方法返回以下int結果:

負數,若是thisObj < anotherObj

零,若是thisObj == anotherObj

正數,若是thisObj > anotherObj

總之,實現compareTo()就是制定對象排序的標準。

 

用Comparator排序

Collections.sort()還有一個重載:

public static <T> void sort(List<T> list,
                            Comparator<? super T> c)

 

Comparable接口和Comparator接口的比較

 

java.lang.Comparable java.util.Comparator
int objOne.compareTo(objTwo) int compare(objOne,objTwo)

返回:

負數,若是objOne < objTwo

零,   若是objOne==objTwo

正數,若是objOne > objTwo

相同
必須修改想排序其實例的類 構建一個類,它不一樣於想排序其實例的類
只能夠建立一個排序序列 能夠建立多個排序序列

在API中常常由以下方式實現:

String、包裝器類、Date、Calendar

意味着要實現成排序第三方類的實例

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

用Arrays類排序

Arrays.sort(arrayToSort)

Arrays.sort(arrayToSort,Comparator)

 

查找數組與集合

static int binarySearch(Object [] ar,Object key) 

方法返回兩種結果,若是在數組ar中查到了key,則返回key的索引值;不然返回插入點。

插入點與索引值:

插入點:   -1         -2         -3         -4        -5      -(n+1)      -(n+2)

數組:         ar[0]     ar[1]     ar[2]    ar[3]   ...............ar[n]  

索引值:         0           1           2          3                       n

package testGenerics;

import java.util.Arrays;
import java.util.Comparator;

public class SearchObjArray {
	public static void main(String[] args) {
		String[] sa = {"one","two","three","four"};
		Arrays.sort(sa);		//#1
		for(String s:sa){
			System.out.print(s+" ");
		}
		System.out.println("\none = "+Arrays.binarySearch(sa, "one"));	//#2
		
		System.out.println("now reverse sort");
		ReSortComparator rs = new ReSortComparator();		//#3
		Arrays.sort(sa,rs);
		for(String s : sa){
			System.out.print(s + " ");
		}
		System.out.println("\none = "+Arrays.binarySearch(sa, "one"));	//#4
		System.out.println("one = "+Arrays.binarySearch(sa,"one",rs));	//#5
	}
	
	static class ReSortComparator implements Comparator<String>{	//#6
		public int compare(String a,String b){
			return b.compareTo(a);			//#7
		}
	}
}
//result:
//four one three two 
//one = 1
//now reverse sort
//two three one four 
//one = -1
//one = 2
/*
 * #1:按字母順序(天然順序)排序sa數組。
 * #2:查找元素「one」的位置,該位置爲1.
 * #3:建立一個Comparator實例。下一行使用comparator從新排序數組。
 * #4:嘗試查找數組。咱們沒有將用於排序數組的Comparator傳遞給binarySearch()方法,
 *     所以,得到一個不正確的(不明確的)答案。
 * #5:再次查找,將Comparator傳遞給binarySearch()。此次得到了正確答案2.
 * #6:定義Comparator,這裏讓它稱爲一個內部類是可行的。
 * #7:經過在調用compareTo()中變元的交換使用,獲得反向的排序。
 */

 

 

在數組和List之間進行轉換:

Arrays.asList()

List.toArray()

 

使用List

能夠經過Iterator迭代器來遍歷List,Iterator的兩個方法:

boolean hasNext()

Object next()

  

7.3.4 導航(查找)TreeSet與TreeMap

TreeSet和TreeMap實現了 Java6 的新接口 java.util.NavigableSet 和 java.util.NavigableMap

如今有一個需求是我想查在一個Set裏,比1600大一點的那個元素,下面是在Java5和Java6的不一樣實現

package testGenerics;

import java.util.TreeSet;

public class Ferry {

	public static void main(String[] args) {
		TreeSet<Integer> times = new TreeSet<Integer>();
		times.add(1205);
		times.add(1505);
		times.add(1545);
		times.add(1830);
		times.add(2010);
		times.add(2100);
		
		TreeSet<Integer> subset = new TreeSet<Integer>();
		subset = (TreeSet<Integer>) times.headSet(1600);
		System.out.println("J5 - last before 4pm is:"+subset.last());
		
		TreeSet<Integer> sub2 = new TreeSet<Integer>();
		sub2 = (TreeSet<Integer>) times.tailSet(2000);
		System.out.println("J5 - first after 8pm is:"+sub2.first());
		
		System.out.println("J6 - last before 4pm is:"+times.lower(1600));
		System.out.println("J6 - first after 8pm is:"+times.higher(2000));
	}

}
//result:
//J5 - last before 4pm is:1545
//J5 - first after 8pm is:2010
//J6 - last before 4pm is:1545
//J6 - first after 8pm is:2010

 

7.3.5 其餘導航方法

輪詢polling

 

TreeSet

 

 E ceiling(E e)
          返回此 set 中大於等於給定元素的最小元素;若是不存在這樣的元素,則返回 null

 

 E higher(E e)
          返回此 set 中嚴格大於給定元素的最小元素;若是不存在這樣的元素,則返回 null

 

 E floor(E e)
          返回此 set 中小於等於給定元素的最大元素;若是不存在這樣的元素,則返回 null

 

 E lower(E e)
          返回此 set 中嚴格小於給定元素的最大元素;若是不存在這樣的元素,則返回 null
 E pollFirst()
          獲取並移除第一個(最低)元素;若是此 set 爲空,則返回 null

 

 E pollLast()
          獲取並移除最後一個(最高)元素;若是此 set 爲空,則返回 null

 

TreeMap

 

 K ceilingKey(K key)
          返回大於等於給定鍵的最小鍵;若是不存在這樣的鍵,則返回 null

 

 K higherKey(K key)
          返回嚴格大於給定鍵的最小鍵;若是不存在這樣的鍵,則返回 null

 

 K floorKey(K key)
          返回小於等於給定鍵的最大鍵;若是不存在這樣的鍵,則返回 null

 

 K lastKey()
          返回映射中當前最後一個(最高)鍵。

 

TreeMap還能夠返回鍵值對:

TreeMap的firstEntry()方法返回一個與此映射中的最小鍵關聯的鍵-值映射關係;若是映射爲空,則返回 null

TreeMap的lastEntry()方法返回與此映射中的最大鍵關聯的鍵-值映射關係;若是映射爲空,則返回 null

 

降序

TreeSet

 

NavigableSet<E> descendingSet()
          返回此 set 中所包含元素的逆序視圖。

TreeMap

 

NavigableMap<K,V> descendingMap()
          返回此映射中所包含映射關係的逆序視圖。

 

7.3.6 後備集合

下面以Set爲例,好比headSet(),在Map中有相應的方法headMap()

 

NavigableSet<E> headSet(E toElement, boolean inclusive)
          返回此 set 的部分視圖,其元素小於(或等於,若是 inclusive 爲 true)toElement

 

NavigableSet<E> tailSet(E fromElement, boolean inclusive)
          返回此 set 的部分視圖,其元素大於(或等於,若是 inclusive 爲 true)fromElement

 

 NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
          返回此 set 的部分視圖,其元素範圍從 fromElementtoElement

 

使用PriortyQueue類

 

 E poll()
          獲取並移除此隊列的頭,若是此隊列爲空,則返回 null。
 E peek()
          獲取但不移除此隊列的頭;若是此隊列爲空,則返回 null。

 

boolean offer(E e)
          將指定的元素插入此優先級隊列。

 

Arrays和Collections中的方法概述

java.util.Arrays中的主要方法:

 

static
<T> List<T>
asList(T... a)
          返回一個受指定數組支持的固定大小的列表。

 

static int binarySearch(Object[] a, Object key)
          使用二分搜索法來搜索指定數組,以得到指定對象。

 

static
<T> int
binarySearch(T[] a, T key, Comparator<? super T> c)
          使用二分搜索法來搜索指定數組,以得到指定對象。

 

static boolean equals(Object[] a, Object[] a2)
          若是兩個指定的 Objects 數組彼此相等,則返回 true。

 

static void sort(Object[] a)
          根據元素的天然順序對指定對象數組按升序進行排序。

 

static
<T> void
sort(T[] a, Comparator<? super T> c)
          根據指定比較器產生的順序對指定對象數組進行排序。

 

static String toString(Object[] a)
          返回指定數組內容的字符串表示形式。

 

java.util.Collections中的主要方法:

 

static
<T> int
binarySearch(List<? extends Comparable<? super T>> list, T key)
          使用二分搜索法搜索指定列表,以得到指定對象。
static
<T> int
binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
          使用二分搜索法搜索指定列表,以得到指定對象。

 

static void reverse(List<?> list)
          反轉指定列表中元素的順序。
static
<T> Comparator<T>
reverseOrder()
          返回一個比較器,它強行逆轉實現了 Comparable 接口的對象 collection 的天然順序
static
<T> Comparator<T>
reverseOrder(Comparator<T> cmp)
          返回一個比較器,它強行逆轉指定比較器的順序。

 

static
<T extends Comparable<? super T>>
void
sort(List<T> list)
          根據元素的天然順序 對指定列表按升序進行排序。
static
<T> void
sort(List<T> list, Comparator<? super T> c)
          根據指定比較器產生的順序對指定列表進行排序。

 

 

List、Set、Map和Queue的方法概述

 

主要接口方法 List Set Map 描述

boolean add(element)

boolean add(index,element)

X

X

X

 

 

添加一個元素。對於List,能夠在

索引點有選擇地添加元素

boolean contains(object)

boolean containsKey(object key)

boolean containsValue(object value)

X

 

 

X

 

 

 

X

X

在集合中查找一個對象(或者有選

擇地在Map中查找一個鍵),將結

果做爲boolean返回

object get(index)

object get(key)

X

 

 

 

X

經過索引或鍵從集合得到一個對象

 

int indexOf(object) X     得到對象在List中的位置 
Iterator iterator()   得到List或Set的迭代器
Set keySet()     返回包含Map的鍵的Set
put(key,value)     添加一個鍵/值對到Map 

remove(index)

remove(object)

remove(key)

X

X

 

 

X

 

 

 

X

經過索引、元素的值或鍵刪除元素

 

 

int size() X 返回集合中元素的數量 

Object[] toArray()

T[] toArray(T[])

 

X

 

 

返回包含集合元素的數組

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

7.4 泛型類型

 

考試目標6.3 編寫代碼,使用Collections API的泛型版本,特別是Set、List和Map接口以及實現類的泛型版本。瞭解非泛型的Collection API的限制以及如何重構代碼來使用泛型版本。

考試目標6.4 編寫代碼,正確使用類/接口聲明、實例變量、方法變元以及返回類型中的類型參數。編寫泛型方法或使用通配符類型的方法,並理解這兩種方法間的類似及不一樣之處。編寫使用NavigableSet和NavigableMap接口的代碼。

 

處理集合的遺留方式

使用泛型以下聲明一個List:

List<String> myList = new ArrayList<String>();

使用了泛型後,myList裏的全部元素都必須是String類型的;若是咱們不使用泛型,雖然你能夠往這個myList裏插入各類類型的元素,可是你要本身保證代碼的安全性。讀出的時候要強制轉型,而且要保證確實可以轉型。

 

7.4.1 泛型與遺留代碼

把非泛型代碼升級成泛型代碼:

在集合類的關鍵字後面加<類型>。

 

7.4.2 混合泛型和非泛型集合

能夠將泛型集合傳遞到帶有非泛型集合的方法,但結果可能很是糟糕。編譯器不能阻止方法將錯誤的類型插入到之前是類型安全的集合。

若是編譯器可以認識到,非類型安全的代碼可能會危害原來聲明爲類型安全的東西,就會給出一個編譯器警告。例如,若是將一個List<String>傳遞到聲明爲

void foo(List aList){aList.add(anInteger);} 的方法,則會獲得一個警告,由於add()有多是「不安全的」。

「編譯不帶錯誤」與「編譯不帶警告」是不一樣的。編譯時的警告不被認爲是一個編譯錯誤或失敗。

泛型類型信息在運行時不存在——它只用於編譯時安全。混合泛型與遺留代碼所獲得的編譯後代碼,在運行時可能拋出異常。

 

7.4.3 多態與泛型

規則:變量聲明的類型必須匹配傳遞給實際對象的類型。若是聲明瞭List<Foo> foo,那麼賦予foo引用的必須是泛型類型<Foo>,而不是<Foo>的一個子類型,也不是它的一個超類型。

 

7.4.4 泛型方法

前面介紹了一個規則,但若是我如今須要聲明泛型爲XX的子類型時,該怎麼聲明才能讓全部子類型泛型都使用父類型泛型呢?

通配符語法容許泛型方法,接受方法變元所聲明的類型的子類型(或超類型):

void addD(List<? extends Dog> list){}

通配符關鍵字extends用於表示「擴展」或「實現」。所以,在<? extends Dog>中,Dog能夠是一個類,也能夠是一個接口。

 

當使用通配符時,List<? extends Dog>表示能夠訪問集合但不能修改它。

當使用通配符時,List<?>表示任何泛型類型均可以賦給引用,但只能訪問,不能修改。 

 

7.4.5 泛型聲明

泛型的聲明約定用T表明類型,E表明元素:具體使用的時候只要記住集合用E,非集合用T就行了。

 

建立本身的泛型類

建立泛型方法 

相關文章
相關標籤/搜索