在介紹Java容器以前,先看一個簡單的容器分類,以作到能夠大致瞭解Java容器分類:java
Java容器類類庫的用途是保存對象
,並將其劃分爲兩個不一樣的概念:程序員
棧 是 後進先出 的容器。LinkedList具備可以實現棧的全部功能的方法,能夠直接將LinkedList做爲棧使用。棧操做:數組
隊列是典型的先進先出的容器。LinkedList 實現了Queueu接口,從而支持隊列的行爲。安全
先進先出 描述了最典型的 隊列規則 。所謂 隊列規則 是指在給定一組隊列中的元素的狀況下,肯定下一個彈出隊列的元素的規則。 先進先出 聲明的是下一個元素應該是等待時間最長的元素。數據結構
PriorityQueue是優先級隊列,其聲明下一個彈出的元素是最須要的元素(具備最高優先級)。ide
當你在PriorityQueue 上調用 offer() 方法來插入一個對象時,這個對象會在隊列中排序。默認的排序將使用對象在隊列隊列中的天然順序,可是你能夠經過提供本身的Comparator接口實現來修改這個順序。PriorityQueue 能夠確保當你調用peek()、poll()和remove()方法時,獲取的元素將是隊列中優先級最高的元素。函數
使用示例:性能
public class TestQueueElement {
public int getAge() {
return age;
}
public int getSex() {
return sex;
}
public String getName() {
return name;
}
private int age;
private int sex;
private String name;
public TestQueueElement(int age, int sex, String name) {
this.age = age;
this.sex = sex;
this.name = name;
}
@Override
public String toString() {
return getClass().getSimpleName() + " { name:" + name + " ,age:" + age + ",sex:" + sex + "}";
}
}
/** * 1.以age字段的升序進行排序, * 2.在age相同時,以name字段的長度的升序進行排序 */
public class TestQueueElementComparator implements Comparator<TestQueueElement> {
@Override
public int compare(TestQueueElement o1, TestQueueElement o2) {
int result = 0;
if (o1.getName().length() > o2.getName().length()) {
result += 1;
} else if (o1.getName().length() < o2.getName().length()) {
result -= 1;
}
if (o1.getAge() > o2.getAge()) {
result += 2;
} else if (o1.getAge() < o2.getAge()) {
result -= 2;
}
return result;
}
}
public class Main {
public static void main(String[] args) {
PriorityQueue<TestQueueElement> pq = new PriorityQueue<>(10, new TestQueueElementComparator());
pq.offer(new TestQueueElement(10, 0, "name1"));
pq.offer(new TestQueueElement(9, 0, "name1"));
pq.offer(new TestQueueElement(11, 0, "name1"));
pq.offer(new TestQueueElement(10, 0, "name11"));
pq.offer(new TestQueueElement(10, 0, "name111"));
pq.offer(new TestQueueElement(8, 0, "name"));
pq.offer(new TestQueueElement(8, 0, "name111"));
while (!pq.isEmpty()) {
System.out.println(pq.poll());
}
}
}
輸出結果:
TestQueueElement { name:name ,age:8,sex:0}
TestQueueElement { name:name111 ,age:8,sex:0}
TestQueueElement { name:name1 ,age:9,sex:0}
TestQueueElement { name:name1 ,age:10,sex:0}
TestQueueElement { name:name11 ,age:10,sex:0}
TestQueueElement { name:name111 ,age:10,sex:0}
TestQueueElement { name:name1 ,age:11,sex:0}
複製代碼
存入 Set 的每一個元素都必須是惟一的,由於 Set 不保存重複元素。加入 Set 的元素必須定義 equals() 方法以確保對象的惟一性。Set 具備和 Collection 徹底同樣的接口,Set接口不保證維護元素的次序。優化
SortedSet中的元素能夠確保處於排序狀態。this
對Map中使用的鍵的要求與對 Set 中的元素的要求是同樣的。任何鍵都必須具備一個 equals() 方法,若是建被用於散列Map,那麼他還必須具備恰當的 hashCode() 方法,若是鍵被用於 TreeMap ,那麼它必須實現 Comparable。
使用SortedMap(TreeMap 是其現階段的惟一實現),能夠確保鍵處於排序狀態。
只是使用容器,不知道或者說不關心容器的類型,僅僅是獲取容器中的元素。迭代器統一了對容器的訪問方式。
迭代器是一個對象,它的工做是遍歷並選擇序列中的對象,而客戶端程序員沒必要知道或關心該序列底層的結構。此外,迭代器一般被稱爲 輕量級對象:建立它的代價小。可是對迭代器的使用是由限制的:
迭代器使用示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) list.add(i);
System.out.println(list.toString());
Iterator<Integer> it = list.iterator();
//遍歷迭代器對象,循環結束,it中不在具備元素
while (it.hasNext()) {
Integer integer = it.next();
System.out.println(integer.toString());
}
it = list.iterator();
//刪除it迭代器中的元素,同時list中對應的元素也被刪除
for (int i = 0; i < 5; i++) {
it.next();
it.remove();
}
}
複製代碼
ListIterator 是一個更增強大的 Iterator 的子類型,它只能用於各類 List 類的訪問。 儘管 Iterator 只能向前移動,可是 ListIterator 能夠雙向移動 )。它還能夠產生相對於迭代器在列表中指向的當前位置的前一個和後一個元素的索引,而且可使用 set() 方法替換它訪問過的最後一個元素。你能夠經過調用 listIterator() 方法產生一個指向 List 開始處的 ListIterator,而且還能夠經過調用 listiterator(n) 方法建立一個一開始就指向列表索引爲n的元素處的 ListIterator。
使用示例:
public class Test {
private String id;
public Test(int id) {
this.id = "test:" + id;
}
@Override
public String toString() {
return id;
}
}
public static void main(String[] args) {
List<Test> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Test test = new Test(i);
list.add(test);
}
System.out.println(list.toString());
ListIterator<Test> it = list.listIterator();
while (it.hasNext()) {
//it.next() 獲取下一個元素
//it.nextIndex() 獲取下一個元素的索引
//it.previousIndex() 獲取上一個元素的索引
System.out.println(it.next() + "," + it.nextIndex() + "," + it.previousIndex() + ";");
}
System.out.println();
// 是否有上一個元素
while (it.hasPrevious()) {
//it.previous() 獲取上一個元素
System.out.println(it.previous() + " ");
}
it = list.listIterator(3);
while (it.hasNext()) {
it.next();
it.set(new Test(20));
}
System.out.println(list.toString());
}
複製代碼
散列碼是 相對惟一 的,用以表明對象的int值,它是經過將對象的某些信息進行轉換而生成的。hashCode() 是根類Object中的方法,所以全部的Java 對象均可以產生散列碼。
首先,使用散列的目的在於:想要使用一個對象來查找另外一個對象。
其次,散列的價值在於速度:散列將鍵保存在某處,以便可以很快找到。存儲一組元素最快的數據結構是數組,因此使用它來表示鍵的信息 (注意;所說的是鍵的信息,而不是鍵自己)。因爲數組不保存鍵自己,而是經過鍵對象生成一個數字,將其做爲數組的下標,這個數字就是散列碼,由定義在Object中的、且可能由你的類覆蓋的hashCode()方法生成。
爲解決數組容量被固定的問題,不一樣的鍵能夠產生相同的下標。也就是說,可能會有衝突。所以,數組多大就不重要了,任何鍵總能在數組中找到它的位置。
因而查詢一個值得過程就是計算散列碼,而後使用散列碼查詢數組。若是可以保證沒有衝突,那可就有了一個完美的散列函數,可是這種狀況只是特例。一般,衝突由外部連接處理:數組並不直接保存值,而是保存值的list。而後對list中的值使用equals()方法進行線性查詢。這部分的查詢天然會比較慢(由於線性查詢是最慢的查詢方式),可是,若是散列函數好的話,數組的每一個位置就只有較少的值。所以,不是查詢整個list,而是快速的跳到數組的某個位置,只對不多的元素驚醒比較。從而使HashMap的查詢速度加快。
當用本身建立用做 HashMap 的鍵的類,必須在其中放置必須的方法:equals() 和 hashCode() 兩個方法。HashMap 使用 equals() 判斷當前的鍵是否與表中存在的鍵相同。hashCode()並不須要老是可以返回惟一的標識碼,可是equals()方法必須嚴格地判斷兩個對象是否相同。
正確的 equals() 方法必須知足下列5個條件:
強調:默認的Object.equals() 只是比較對象的地址。
在明白瞭如何散列以後,編寫本身的hashCode()就更具備意義了。所以設計hashCode()是最重要的因素就是:不管什麼時候,對同一個對象調用hashCode()都應該生成一樣的值。因此,若是你的hashCode()方法依賴於對象中易變的數據,此時用戶就要小心,由於數據發生變化時,hashCode() 就會生成一個不一樣的散列碼,至關於產生了一個不一樣的鍵。此外,也不該該是hashCode() 依賴於具備惟一性的對象信息,尤爲是使用this的值,這樣將沒法生成一個新的鍵,只能產生很糟糕的hashCode()。應該使用對象內有意義的識別信息。
所以,要想使hashCode()實用,它必須速度快,而且必須有意義。也就是說,它必須基於對象的內容生成散列碼。因此,散列碼沒必要是獨一無二的(應該更關注生成速度,而不是惟一性),可是經過hashCode()和 equals(),必須可以徹底肯定對象的身份。
由於在生成桶的下標前,hashCode() 還須要作進一步的處理,因此散列碼的生成範圍並不重要,只要是int便可。
好的hashCode()應該產生分佈均勻的散列碼。若是散列碼都幾種在一塊,那麼HashMap或者HashSet 在某些區域的負載會很重,這樣就不如分佈均勻的散列函數。
所以一個好的hashCode() 應該遵循的指導是:
域類型 | 計算 |
---|---|
boolean | c=(f?0:1) |
byte、char、short或int | c=(int)f |
long | c=(int)(f^(f>>>32)) |
float | c=Float.floatToIntBits(f) |
doble | long L = Double.doubleToLongBits(f); c=(int)(L^(L>>>32)) |
Object,其equals()調用這個域的 equals() | c=f.hashCode() |
數組 | 對每一個元素應用上述規則 |
result = 37 * result + c
;下面即是遵循這個指導的一個示例:
public class CountedString {
private static List<String> created = new ArrayList<>();
public String getString() {
return mString;
}
public void setString(String string) {
mString = string;
}
private String mString;
private int id;
public CountedString(String string) {
mString = string;
created.add(string);
for (String s2 : created) {
if (s2.equals(mString)) id++;
}
}
@Override public String toString() {
return "String:" + mString + " id:" + id + " hashCode():" + hashCode();
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + mString.hashCode();
result = 37 * result + id;
return result;
}
@Override public boolean equals(Object obj) {
return obj instanceof CountedString && mString
.equals(((CountedString) obj).mString) && id == ((CountedString) obj).id;
}
public static void main() {
Map<CountedString, Integer> map = new HashMap<>();
CountedString[] cs = new CountedString[5];
for (int i = 0; i < cs.length; i++) {
cs[i] = new CountedString("hi");
map.put(cs[i], i);
}
System.out.println(map);
for (CountedString cst : cs) {
System.out.println("Looking up:" + cst);
System.out.println(map.get(cst));
}
}
}
複製代碼