存儲對象的容器,面嚮對象語言對事物的體現都是以對象的形式,因此爲了方便對多個對象的操做,存儲對象,集合是存儲對象最經常使用的一種方式。html
集合的出現就是爲了持有對象。集合中能夠存儲任意類型的對象, 並且長度可變。在程序中有可能沒法預先知道須要多少個對象, 那麼用數組來裝對象的話, 長度很差定義, 而集合解決了這樣的問題。java
數組和集合類都是容器程序員
數組長度是固定的,集合長度是可變的。數組中能夠存儲基本數據類型,集合只能存儲對象數組中存儲數據類型是單一的,集合中能夠存儲任意類型的對象。算法
集合類的特色編程
用於存儲對象,長度是可變的,能夠存儲不一樣類型的對象。api
存儲類型單一的數據容器,操做複雜(數組一旦聲明好不可變)CRUD數組
集合作什麼安全
1:將對象添加到集合網絡
2:從集合中刪除對象數據結構
3: 從集合中查找一個對象
4:從集合中修改一個對象就是增刪改查
注意:集合和數組中存放的都是對象的引用而非對象自己
Java工程師對不一樣的容器進行了定義,雖然容器不一樣,可是仍是有一些共性能夠抽取最後抽取了一個頂層接口,那麼就造成了一個集合框架。如何學習呢?固然是從頂層學起,頂層裏邊具備最共性,最基本的行爲。具體的使用,就要選擇具體的容器了。爲何? 由於不斷向上抽取的東西有多是不能建立對象的.抽象的可能性很大,而且子類對象的方法更多一些. 因此是看頂層,建立底層。那麼集合的頂層是什麼呢 叫作Collection
集合框架體系
---|Collection: 單列集合 ---|List: 有存儲順序, 可重複 ---|ArrayList: 數組實現, 查找快, 增刪慢 因爲是數組實現, 在增和刪的時候會牽扯到數組 增容, 以及拷貝元素. 因此慢。數組是能夠直接 按索引查找, 因此查找時較快 ---|LinkedList: 鏈表實現, 增刪快, 查找慢 因爲鏈表實現, 增長時只要讓前一個元素記住自 己就能夠, 刪除時讓前一個元素記住後一個元 素, 後一個元素記住前一個元素. 這樣的增刪效 率較高但查詢時須要一個一個的遍歷, 因此效率 較低 ---|Vector: 和ArrayList原理相同, 但線程安全, 效率略低 和ArrayList實現方式相同, 但考慮了線程安全問 題, 因此效率略低 ---|Set: 無存儲順序, 不可重複 ---|HashSet ---|TreeSet ---|LinkedHashSet ---| Map: 鍵值對 ---|HashMap ---|TreeMap ---|HashTable ---|LinkedHashMap |
爲何出現這麼多集合容器,由於每個容器對數據的存儲方式不一樣,這種存儲方式稱之爲數據結構(data structure)
注意 集合和數組中存放的都是對象的引用。
Collection |
咱們須要保存若干個對象的時候使用集合。 |
List
|
若是咱們須要保留存儲順序, 而且保留重複元素, 使用List. 若是查詢較多, 那麼使用ArrayList 若是存取較多, 那麼使用LinkedList 若是須要線程安全, 那麼使用Vector |
Set
|
若是咱們不須要保留存儲順序, 而且須要去掉重複元素, 使用Set. 若是咱們須要將元素排序, 那麼使用TreeSet 若是咱們不須要排序, 使用HashSet, HashSet比 TreeSet效率高. 若是咱們須要保留存儲順序, 又要過濾重複元素, 那麼使用LinkedHashSet |
Collection接口有兩個子接口:
List(鏈表|線性表)
Set(集)
特色:
Collection中描述的是集合共有的功能(CRUD)
List可存放重複元素,元素存取是有序的
Set不能夠存放重複元素,元素存取是無序的
java.util.Collection ---| Collection 描述全部接口的共性 ----| List接口 能夠有重複元素的集合 ----| Set 接口 不能夠有重複元素的集合
|
2:學習集合對象
學習Collection中的共性方法,多個容器在不斷向上抽取就出現了該體系。發現Collection接口中具備全部容器都具有的共性方法。查閱API時,就能夠直接看該接口中的方法。並建立其子類對象對集合進行基本應用。當要使用集合對象中特有的方法,在查看子類具體內容。
查看api 文檔Collection在在java.util 中(注意是大寫Collection)
注意在現階段遇到的 E T 之類的類型,須要暫時理解爲object 由於涉及到了泛型.
3:建立集合對象,使用Collection中的List的具體實現類ArrayList
1:Collection coll=new Arraylist();
增長: 1:add() 將指定對象存儲到容器中 add 方法的參數類型是Object 便於接收任意對象 2:addAll() 將指定集合中的元素添加到調用該方法和集合中 刪除: 3:remove() 將指定的對象從集合中刪除 4:removeAll() 將指定集合中的元素刪除 修改 5:clear() 清空集合中的全部元素 判斷 6:isEmpty() 判斷集合是否爲空 7:contains() 判斷集合何中是否包含指定對象
8:containsAll() 判斷集合中是否包含指定集合 使用equals()判斷兩個對象是否相等 獲取: 9:int size() 返回集合容器的大小
轉成數組10: toArray() 集合轉換數組 |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); System.out.println(list); // [計算機網絡, 現代操做系統, java編程思想]
// 增長2 將list容器元素添加到list2容器中 Collection list2 = new ArrayList(); list2.add("java核心技術"); list2.addAll(list); list2.add("java語言程序設計"); System.out.println(list2); // [java核心技術, 計算機網絡, 現代操做系統, java編程思想, java語言程序設計] } |
// 刪除1 remove boolean remove = list2.remove("java核心技術"); System.out.println(remove); // true System.out.println(list2); // //刪除2 removeAll() 將list中的元素刪除 boolean removeAll = list2.removeAll(list); System.out.println(removeAll);//true System.out.println(list2);//[java語言程序設計] |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 修改 clear() 清空集合中的全部元素 list.clear(); System.out.println(list); //[] } |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
boolean empty = list.isEmpty(); System.out.println(empty);// false boolean contains = list.contains("java編程思想"); System.out.println(contains);// true Collection list2 = new ArrayList(); list2.add("水許傳"); boolean containsAll = list.containsAll(list2); System.out.println(containsAll);// false
} |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取 集合容器的大小 int size = list.size(); System.out.println(size); } |
該案例要求完成使用集合:
public static void main(String[] args) {
// 建立集合對象 Collection coll = new ArrayList();
// 建立Person對象 Person p1 = new Person("jack", 25); Person p2 = new Person("rose", 22); Person p3 = new Person("lucy", 20); Person p4 = new Person("jack", 25);
// 集合中添加一些Perosn
// 刪除指定Person
// 刪除全部Person
// 判斷容器中是否還有Person
// 判斷容器中是否包含指定Person
// 獲取容器中Person的個數
// 將容器變爲數組,遍歷除全部Person
} |
分析:
1:Person類
1:姓名和年齡
2:重寫hashCode和equals方法
1:若是不重寫,調用Object類的equals方法,判斷內存地址,爲false
1:若是是Person類對象,而且姓名和年齡相同就返回true
2:若是不重寫,調用父類hashCode方法
1:若是equals方法相同,那麼hashCode也要相同,須要重寫hashCode方法
3:重寫toString方法
1:不重寫,直接調用Object類的toString方法,打印該對象的內存地址
Person類
class Person { private String name; private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
@Override public int hashCode() { return this.name.hashCode() + age; }
@Override public boolean equals(Object obj) { if (!(obj instanceof Person)) { return false; } Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; }
@Override public String toString() { return "Person :name=" + name + ", age=" + age; }
}
|
public static void main(String[] args) { Person p1 = new Person("張三", 19); Person p2 = new Person("李四", 20); Person p3 = new Person("王五", 18); Collection list = new ArrayList(); list.add(p1); list.add(p2); list.add(p3); // isEmpty() 判斷集合是否爲空 boolean empty = list.isEmpty(); System.out.println(empty); // 返回集合容器的大小 int size = list.size(); System.out.println(size); // contains()判斷集合何中是否包含指定對象 boolean contains = list.contains(p1); System.out.println(contains);
// remove(); 將指定的對象從集合中刪除 list.remove(p1);
// clear() 清空集合中的全部元素 list.clear(); System.out.println(list);
}
|
//使用集合存儲自定義對象2 class Book { private String name; private double price;
public Book() {
}
public Book(String name, double price) { this.name = name; this.price = price; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override public int hashCode() { return (int) (this.name.hashCode() + price); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Book)) { return false; } Book book = (Book) obj; return this.name.equals(book.name) && this.price == book.price; }
@Override public String toString() { return "book:@ name:" + this.name + ", price:" + this.price; } } public class Demo1 { public static void main(String[] args) { Collection col = new ArrayList(); col.add(new Book("think in java", 100)); col.add(new Book("core java", 200)); System.out.println(col); } } |
---| Iterable 接口 Iterator iterator() ----| Collection 接口 ------| List 接口 元素能夠重複,容許在指定位置插入元素,並經過索 引來訪問元素 |
1:增長 void add(int index, E element) 指定位置添加元素 boolean addAll(int index, Collection c) 指定位置添加集合 2:刪除 E remove(int index) 刪除指定位置元素
3:修改 E set(int index, E element) 返回的是須要替換的集合中的元素 4:查找: E get(int index) 注意: IndexOutOfBoundsException int indexOf(Object o) // 找不到返回-1 lastIndexOf(Object o) 5:求子集合 List<E> subList(int fromIndex, int toIndex) // 不包含toIndex |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // add,在0角標位置添加一本書 list.add(0, "舒克和貝塔"); System.out.println(list); // 在list2集合的1角標位置添加list集合元素 List list2 = new ArrayList(); list2.add("史記"); list2.add("資治通鑑"); list2.add("全球通史"); boolean addAll = list2.addAll(1, list); System.out.println(addAll); //true System.out.println(list2); } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 刪除0角標元素 Object remove = list.remove(0); System.out.println(remove); } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 修改2角標位置的書,返回的原來2角標位置的書 Object set = list.set(2, "邊城"); System.out.println(set); //java編程思想 System.out.println(list);
} |
查找
List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 查找: E get(int index) 注意角標越界 Object set = list.get(list.size() - 1); System.out.println(set); // java語言程序設計 System.out.println(list); list.get(list.size()); //IndexOutOfBoundsException } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); list.add("java編程思想"); System.out.println(list); // 查找: E get(int index) 注意角標越界 Object set = list.get(list.size() - 1); System.out.println(set); // java語言程序設計 System.out.println(list); // list.get(list.size()); //IndexOutOfBoundsException
// indexOf(Object o) 返回第一次出現的指定元素的角標 int indexOf = list.indexOf("java編程思想"); System.out.println(indexOf); // 2 // 沒有找到,返回-1 int indexOf2 = list.indexOf("三國志"); System.out.println(indexOf2); // -1
// lastIndexOf 返回最後出現的指定元素的角標 int lastIndexOf = list.lastIndexOf("java編程思想"); System.out.println(lastIndexOf); // 5 } |
--| Iterable ----| Collection ------| List ---------| ArrayList 底層採用數組實現,默認10。每次增加 60%,((oldCapacity * 3)/2 + 1) 查詢快,增刪慢。 ---------| LinkedList |
ArrayList:實現原理:
數組實現, 查找快, 增刪慢
數組爲何是查詢快?由於數組的內存空間地址是連續的.
ArrayList底層維護了一個Object[] 用於存儲對象,默認數組的長度是10。能夠經過 new ArrayList(20)顯式的指定用於存儲對象的數組的長度。
當默認的或者指定的容量不夠存儲對象的時候,容量自動增加爲原來的容量的1.5倍。
因爲ArrayList是數組實現, 在增和刪的時候會牽扯到數組增容, 以及拷貝元素. 因此慢。數組是能夠直接按索引查找, 因此查找時較快
能夠考慮,假設向數組的0角標未知添加元素,那麼原來的角標位置的元素須要總體日後移,而且數組可能還要增容,一旦增容,就須要要將老數組的內容拷貝到新數組中.因此數組的增刪的效率是很低的.
練習:去除ArrayList集合中重複元素
1:存入字符串元素
2:存入自定義對象元素(如Perosn對象)
原理:
循環遍歷該集合,每取出一個放置在新的集合中,放置以前先判斷新的集合是否以包含了新的元素。
思路: 存入人的對象. 1先定義person 類 2將該類的實例存入集合 3 將對象元素進行操做. 注意:自定義對象要進行復寫toString 和 equals 方法. 爲何? 由於object 是person 的父類,object 中的toString 返回的是哈希值,object 類中equals 方法比較的是對象的地址值. 思路 1存入字符串對象 2存入自定義對象 如person 2建立容器,用於存儲非重複元素 3對原容器進行遍歷,在遍歷過程當中進行判斷遍歷到的元素是否在容器中存在.(contains) 4若是存在,就不存入,不然存入. 5 返回新容器 |
public class Demo6 { public static void main(String[] args) { ArrayList arr = new ArrayList(); Person p1 = new Person("jack", 20); Person p2 = new Person("rose", 18); Person p3 = new Person("rose", 18); arr.add(p1); arr.add(p2); arr.add(p3); System.out.println(arr); ArrayList arr2 = new ArrayList(); for (int i = 0; i < arr.size(); i++) { Object obj = arr.get(i); Person p = (Person) obj; if (!(arr2.contains(p))) { arr2.add(p); } } System.out.println(arr2); } }
class Person { private String name; private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() { return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (!(obj instanceof Person)) { return false; } Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age; }
@Override public String toString() { return "Person@name:" + this.name + " age:" + this.age; }
} |
在實際的開發中ArrayList是使用頻率最高的一個集合。
--| Iterable ----| Collection ------| List ---------| ArrayList 底層採用數組實現,默認10。每次增加 60%,((oldCapacity * 3)/2 + 1) 查詢快,增刪慢。 ---------| LinkedList 底層採用鏈表實現,增刪快,查詢慢。 |
LinkedList:鏈表實現, 增刪快, 查找慢
因爲LinkedList:在內存中的地址不連續,須要讓上一個元素記住下一個元素.因此每一個元素中保存的有下一個元素的位置.雖然也有角標,可是查找的時候,須要從頭往下找,顯然是沒有數組查找快的.可是,鏈表在插入新元素的時候,只須要讓前一個元素記住新元素,讓新元素記住下一個元素就能夠了.因此插入很快.
因爲鏈表實現, 增長時只要讓前一個元素記住本身就能夠, 刪除時讓前一個元素記住後一個元素, 後一個元素記住前一個元素. 這樣的增刪效率較高。
但查詢時須要一個一個的遍歷, 因此效率較低。
特有方法
1:方法介紹 addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast() 若是集合中沒有元素,獲取或者刪除元 素拋:NoSuchElementException 2:數據結構 1:棧 (1.6) 先進後出 push() pop() 2:隊列(雙端隊列1.5) 先進先出 offer() poll() 3:返回逆序的迭代器對象 descendingIterator() 返回逆序的迭代器對象
|
基本方法
import java.util.Iterator; import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("西遊記"); list.add("三國演義"); list.add("石頭記"); list.add("水滸傳"); list.add("全球通史"); list.addFirst("史記"); list.addLast("吶喊"); // list.addFirst(null); // list.addLast(null); System.out.println(list); // 獲取指定位置處的元素。 String str = (String) list.get(0); // 返回此列表的第一個元素。 String str2 = (String) list.getFirst(); System.out.println(str.equals(str2));
// 獲取指定位置處的元素。 String str3 = (String) list.get(list.size() - 1); // 返回此列表的最後一個元素。 String str4 = (String) list.getLast(); System.out.println(str3.equals(str4));
// 獲取但不移除此列表的頭(第一個元素)。 Object element = list.element(); System.out.println(element);
int size = list.size(); System.out.println(size); }
|
迭代
mport java.util.Iterator; import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("西遊記"); list.add("三國演義"); list.add("石頭記"); list.add("水滸傳"); list.add("全球通史"); Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } } } |
逆序迭代
import java.util.Iterator; import java.util.LinkedList;
public class Demo6 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("aa"); list.add("bb"); list.add("cc"); Iterator dit = list.descendingIterator(); while (dit.hasNext()) { System.out.println(dit.next()); } } }
|
注意:可使用該集合去模擬出隊列(先進先出) 或者堆棧(後進先出) 數據結構。
堆棧(後進先出)
//堆棧(後進先出) 數據結構 public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); // 壓棧,先進後出 list.push("西遊記"); list.push("三國演義"); list.push("石頭記"); list.push("水滸傳"); System.out.println(list); // 彈棧 String str1 = (String) list.pop(); System.out.println(str1); String str2 = (String) list.pop(); System.out.println(str2); String str3 = (String) list.pop(); System.out.println(str3); String str4 = (String) list.pop(); System.out.println(str4); System.out.println(list.size());// 0 System.out.println(list); //[] } }
|
隊列,先進先出
import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); // 隊列,先進先出 list.offer("西遊記"); list.offer("三國演義"); list.offer("石頭記"); list.offer("水滸傳"); System.out.println(list); // 出隊列 System.out.println(list.poll()); System.out.println(list.poll()); System.out.println(list.poll()); System.out.println(list.poll());
System.out.println(list.size());
System.out.println(list.peek()); // 獲取隊列的頭元素,可是不刪除 System.out.println(list.peekFirst()); // 獲取隊列的頭元素,可是不刪除 System.out.println(list.peekLast()); // 獲取隊列的最後一個元素可是不刪除
} }
|
ArrayList 和 LinkedList的存儲查找的優缺點:
1、ArrayList 是採用動態數組來存儲元素的,它容許直接用下標號來直接查找對應的元素。可是,可是插入元素要涉及數組元素移動及內存的操做。總結:查找速度快,插入操做慢。
2、LinkedList 是採用雙向鏈表實現存儲,按序號索引數據須要進行前向或後向遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入速度較快
問題:有一批數據要存儲,要求存儲這批數據不能出現重複數據,ArrayList、LinkedList都無法知足需求。解決辦法:使用 set集合。
Vector: 描述的是一個線程安全的ArrayList。
ArrayList: 單線程效率高
Vector : 多線程安全的,因此效率低
特有的方法:
void addElement(E obj) 在集合末尾添加元素 E elementAt( int index) 返回指定角標的元素 Enumeration elements() 返回集合中的全部元素,封裝到Enumeration對象中
Enumeration 接口:
boolean hasMoreElements() 測試此枚舉是否包含更多的元素。 E nextElement() 若是此枚舉對象至少還有一個可提供的元素,則返回此枚舉的下一個元素。 |
public static void main(String[] args) { Vector v = new Vector(); v.addElement("aaa"); v.addElement("bbb"); v.addElement("ccc"); System.out.println( v ); System.out.println( v.elementAt(2) ); // ccc // 遍歷Vector遍歷 Enumeration ens = v.elements(); while ( ens.hasMoreElements() ) { System.out.println( ens.nextElement() ); } } |
爲了方便的處理集合中的元素,Java中出現了一個對象,該對象提供了一些方法專門處理集合中的元素.例如刪除和獲取集合中的元素.該對象就叫作迭代器(Iterator).
對 Collection 進行迭代的類,稱其爲迭代器。仍是面向對象的思想,專業對象作專業的事情,迭代器就是專門取出集合元素的對象。可是該對象比較特殊,不能直接建立對象(經過new),該對象是之內部類的形式存在於每一個集合類的內部。
如何獲取迭代器?Collection接口中定義了獲取集合類迭代器的方法(iterator()),因此全部的Collection體系集合均可以獲取自身的迭代器。
正是因爲每個容器都有取出元素的功能。這些功能定義都同樣,只不過實現的具體方式不一樣(由於每個容器的數據結構不同)因此對共性的取出功能進行了抽取,從而出現了Iterator接口。而每個容器都在其內部對該接口進行了內部類的實現。也就是將取出方式的細節進行封裝。
Jdk1.5以後添加的新接口, Collection的父接口. 實現了Iterable的類就是可迭代的.而且支持加強for循環。該接口只有一個方法即獲取迭代器的方法iterator()能夠獲取每一個容器自身的迭代器Iterator。(Collection)集合容器都須要獲取迭代器(Iterator)因而在5.0後又進行了抽取將獲取容器迭代器的iterator()方法放入到了Iterable接口中。Collection接口進程了Iterable,因此Collection體系都具有獲取自身迭代器的方法,只不過每一個子類集合都進行了重寫(由於數據結構不一樣)
Iterator iterator() 返回該集合的迭代器對象
該類主要用於遍歷集合對象,該類描述了遍歷集合的常見方法 1:java.lang. Itreable ---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| Set接口 不能夠有重複元素的集合 |
public interface Iterable<T>
Itreable 該接口僅有一個方法,用於返回集合迭代器對象。
1: Iterator<T> iterator() 返回集合的迭代器對象
Iterator接口定義的方法
Itreator 該接口是集合的迭代器接口類,定義了常見的迭代方法 1:boolean hasNext() 判斷集合中是否有元素,若是有元素能夠迭代,就返回true。 2: E next() 返回迭代的下一個元素,注意: 若是沒有下一個元素時,調用 next元素會拋出NoSuchElementException
3: void remove() 從迭代器指向的集合中移除迭代器返回的最後一個元素(可選操 做)。 |
思考:爲何next方法的返回類型是Object的呢? 爲了能夠接收任意類型的對象,那麼返回的時候,不知道是什麼類型的就定義爲object
1:while循環
public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } } |
2:for循環
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
for (Iterator it = list.iterator(); it.hasNext();) { //迭代器的next方法返回值類型是Object,因此要記得類型轉換。 String next = (String) it.next(); System.out.println(next); } } }
|
須要取出全部元素時,能夠經過循環,java 建議使用for 循環。由於能夠對內存進行一下優化。
3:使用迭代器清空集合
public class Demo1 { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); coll.add("ddd"); System.out.println(coll); Iterator it = coll.iterator(); while (it.hasNext()) { it.next(); it.remove(); } System.out.println(coll); } } |
細節一:
若是迭代器的指針已經指向了集合的末尾,那麼若是再調用next()會返回NoSuchElementException異常
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } // 迭代器的指針已經指向了集合的末尾 // String next = (String) it.next(); // java.util.NoSuchElementException } } |
細節二:
若是調用remove以前沒有調用next是不合法的,會拋出IllegalStateException
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
Iterator it = list.iterator(); while (it.hasNext()) { // 調用remove以前沒有調用next是不合法的 // it.remove(); // java.lang.IllegalStateException String next = (String) it.next(); System.out.println(next); }
} }
|
4:迭代器原理
查看ArrayList源碼
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() { return cursor != size(); }
public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }
public void remove() { if (lastRet == -1) throw new IllegalStateException(); checkForComodification();
try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
} |
5:注意在對集合進行迭代過程當中,不容許出現迭代器之外的對元素的操做,由於這樣會產生安全隱患,java會拋出異常併發修改異常(ConcurrentModificationException),普通迭代器只支持在迭代過程當中的刪除動做。
注意: ConcurrentModificationException: 當一個集合在循環中即便用引用變量操做集合又使用迭代器操做集合對象, 會拋出該異常。
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Demo1 { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); coll.add("ddd"); System.out.println(coll); Iterator it = coll.iterator(); while (it.hasNext()) { it.next(); it.remove(); coll.add("abc"); // 出現了迭代器之外的對元素的操做 } System.out.println(coll); } } |
若是是List集合,想要在迭代中操做元素可使用List集合的特有迭代器ListIterator,該迭代器支持在迭代過程當中,添加元素和修改元素。
public interface ListIterator extends Iterator
ListIterator<E> listIterator()
---| Iterator hasNext() next() remove() ------| ListIterator Iterator子接口 List專屬的迭代器 add(E e) 將指定的元素插入列表(可選操做)。該元素直接插入到 next 返回的下一個元素的前面(若是有) void set(E o) 用指定元素替換 next 或 previous 返回的最後一個元素 hasPrevious() 逆向遍歷列表,列表迭代器有多個元素,則返回 true。 previous() 返回列表中的前一個元素。 |
Iterator在迭代時,只能對元素進行獲取(next())和刪除(remove())的操做。
對於 Iterator 的子接口ListIterator 在迭代list 集合時,還能夠對元素進行添加
(add(obj)),修改set(obj)的操做。
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取List專屬的迭代器 ListIterator lit = list.listIterator();
while (lit.hasNext()) { String next = (String) lit.next(); System.out.println(next); }
} }
|
倒序遍歷
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取List專屬的迭代器 ListIterator lit = list.listIterator(); while (lit.hasNext()) { String next = (String) lit.next(); System.out.println(next); } System.out.println("***************"); while (lit.hasPrevious()) { String next = (String) lit.previous(); System.out.println(next); }
} }
|
Set方法:用指定元素替換 next 或 previous 返回的最後一個元素
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
ListIterator lit = list.listIterator(); lit.next(); // 計算機網絡 lit.next(); // 現代操做系統 System.out.println(lit.next()); // java編程思想 //用指定元素替換 next 或 previous 返回的最後一個元素 lit.set("平凡的世界");// 將java編程思想替換爲平凡的世界 System.out.println(list);
} }
|
add方法將指定的元素插入列表,該元素直接插入到 next 返回的元素的後
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
ListIterator lit = list.listIterator(); lit.next(); // 計算機網絡 lit.next(); // 現代操做系統 System.out.println(lit.next()); // java編程思想 // 將指定的元素插入列表,該元素直接插入到 next 返回的元素的後 lit.add("平凡的世界");// 在java編程思想後添加平凡的世界 System.out.println(list);
} } |
Set:注重獨一無二的性質,該體系集合能夠知道某物是否已近存在於集合中,不會存儲重複的元素
用於存儲無序(存入和取出的順序不必定相同)元素,值不能重複。
對象的相等性
引用到堆上同一個對象的兩個引用是相等的。若是對兩個引用調用hashCode方法,會獲得相同的結果,若是對象所屬的類沒有覆蓋Object的hashCode方法的話,hashCode會返回每一個對象特有的序號(java是依據對象的內存地址計算出的此序號),因此兩個不一樣的對象的hashCode值是不可能相等的。
若是想要讓兩個不一樣的Person對象視爲相等的,就必須覆蓋Object繼下來的hashCode方法和equals方法,由於Object hashCode方法返回的是該對象的內存地址,因此必須重寫hashCode方法,才能保證兩個不一樣的對象具備相同的hashCode,同時也須要兩個不一樣對象比較equals方法會返回true
該集合中沒有特有的方法,直接繼承自Collection。
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| ArrayList ---| LinkedList ---| Set接口 不能夠有重複元素的集合 |
案例:set集合添加元素並使用迭代器迭代元素。
import java.util.HashSet; import java.util.Iterator; import java.util.Set;
public class Demo4 { public static void main(String[] args) { //Set 集合存和取的順序不一致。 Set hs = new HashSet(); hs.add("世界軍事"); hs.add("兵器知識"); hs.add("艦船知識"); hs.add("漢和防務"); System.out.println(hs); // [艦船知識, 世界軍事, 兵器知識, 漢和防務] Iterator it = hs.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } |
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| ArrayList ---| LinkedList ---| Set接口 不能夠有重複元素的集合 ---| HashSet 線程不安全,存取速度快。底層是以哈希表實現的。 |
HashSet
哈希表邊存放的是哈希值。HashSet存儲元素的順序並非按照存入時的順序(和List顯然不一樣) 是按照哈希值來存的因此取數據也是按照哈希值取得。
HashSet不存入重複元素的規則.使用hashcode和equals
因爲Set集合是不能存入重複元素的集合。那麼HashSet也是具有這一特性的。HashSet如何檢查重複?HashSet會經過元素的hashcode()和equals方法進行判斷元素師否重複。
當你試圖把對象加入HashSet時,HashSet會使用對象的hashCode來判斷對象加入的位置。同時也會與其餘已經加入的對象的hashCode進行比較,若是沒有相等的hashCode,HashSet就會假設對象沒有重複出現。
簡單一句話,若是對象的hashCode值是不一樣的,那麼HashSet會認爲對象是不可能相等的。
所以咱們自定義類的時候須要重寫hashCode,來確保對象具備相同的hashCode值。
若是元素(對象)的hashCode值相同,是否是就沒法存入HashSet中了? 固然不是,會繼續使用equals 進行比較.若是 equals爲true 那麼HashSet認爲新加入的對象重複了,因此加入失敗。若是equals 爲false那麼HashSet 認爲新加入的對象沒有重複.新元素能夠存入.
總結:
元素的哈希值是經過元素的hashcode方法 來獲取的, HashSet首先判斷兩個元素的哈希值,若是哈希值同樣,接着會比較equals方法 若是 equls結果爲true ,HashSet就視爲同一個元素。若是equals 爲false就不是同一個元素。
哈希值相同equals爲false的元素是怎麼存儲呢,就是在一樣的哈希值下順延(能夠認爲哈希值相同的元素放在一個哈希桶中)。也就是哈希同樣的存一列。
hashtable
圖1:hashCode值不相同的狀況
圖2:hashCode值相同,但equals不相同的狀況。
HashSet:經過hashCode值來肯定元素在內存中的位置。一個hashCode位置上能夠存放多個元素。
當hashcode() 值相同equals() 返回爲true 時,hashset 集合認爲這兩個元素是相同的元素.只存儲一個(重複元素沒法放入)。調用原理:先判斷hashcode 方法的值,若是相同纔會去判斷equals 若是不相同,是不會調用equals方法的。
HashSet究竟是如何判斷兩個元素重複。
經過hashCode方法和equals方法來保證元素的惟一性,add()返回的是boolean類型
判斷兩個元素是否相同,先要判斷元素的hashCode值是否一致,只有在該值一致的狀況下,纔會判斷equals方法,若是存儲在HashSet中的兩個對象hashCode方法的值相同equals方法返回的結果是true,那麼HashSet認爲這兩個元素是相同元素,只存儲一個(重複元素沒法存入)。
注意:HashSet集合在判斷元素是否相同先判斷hashCode方法,若是相同纔會判斷equals。若是不相同,是不會調用equals方法的。
HashSet 和ArrayList集合都有判斷元素是否相同的方法,
boolean contains(Object o)
HashSet使用hashCode和equals方法,ArrayList使用了equals方法
練習:使用HashSet存儲字符串,並嘗試添加劇復字符串
回顧String類的equals()、hashCode()兩個方法。
import java.util.HashSet; import java.util.Iterator; import java.util.Set;
public class Demo4 { public static void main(String[] args) { // Set 集合存和取的順序不一致。 Set hs = new HashSet(); hs.add("世界軍事"); hs.add("兵器知識"); hs.add("艦船知識"); hs.add("漢和防務");
// 返回此 set 中的元素的數量 System.out.println(hs.size()); // 4
// 若是此 set 還沒有包含指定元素,則返回 true boolean add = hs.add("世界軍事"); // false System.out.println(add);
// 返回此 set 中的元素的數量 System.out.println(hs.size());// 4 Iterator it = hs.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } }
|
使用HashSet存儲自定義對象,並嘗試添加劇復對象(對象的重複的斷定)
package cn.itcast.gz.map;
import java.util.HashSet; import java.util.Iterator;
public class Demo4 { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Person("jack", 20)); hs.add(new Person("rose", 20)); hs.add(new Person("hmm", 20)); hs.add(new Person("lilei", 20)); hs.add(new Person("jack", 20));
Iterator it = hs.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } }
class Person { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() { System.out.println("hashCode:" + this.name); return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { System.out.println(this + "---equals---" + obj); if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
}
|
問題:如今有一批數據,要求不能重複存儲元素,並且要排序。ArrayList 、 LinkedList不能去除重複數據。HashSet能夠去除重複,可是是無序。
案例:使用TreeSet集合存儲字符串元素,並遍歷
import java.util.TreeSet;
public class Demo5 { public static void main(String[] args) { TreeSet ts = new TreeSet(); ts.add("ccc"); ts.add("aaa"); ts.add("ddd"); ts.add("bbb");
System.out.println(ts); // [aaa, bbb, ccc, ddd]
} } |
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 有序,能夠重複,有角標的集合 ---| ArrayList ---| LinkedList ---| Set接口 無序,不能夠重複的集合 ---| HashSet 線程不安全,存取速度快。底層是以hash表實現的。 ---| TreeSet 紅-黑樹的數據結構,默認對元素進行天然排序(String)。若是在比較的時候兩個對象返回值爲0,那麼元素重複。 |
紅-黑樹
紅黑樹是一種特定類型的二叉樹
紅黑樹算法的規則: 左小右大。
既然TreeSet能夠天然排序,那麼TreeSet一定是有排序規則的。
1:讓存入的元素自定義比較規則。
2:給TreeSet指定排序規則。
方式一:元素自身具有比較性
元素自身具有比較性,須要元素實現Comparable接口,重寫compareTo方法,也就是讓元素自身具有比較性,這種方式叫作元素的天然排序也叫作默認排序。
方式二:容器具有比較性
當元素自身不具有比較性,或者自身具有的比較性不是所須要的。那麼此時可讓容器自身具有。須要定義一個類實現接口Comparator,重寫compare方法,並將該接口的子類實例對象做爲參數傳遞給TreeMap集合的構造方法。
注意:當Comparable比較方式和Comparator比較方式同時存在時,以Comparator的比較方式爲主;
注意:在重寫compareTo或者compare方法時,必需要明確比較的主要條件相等時要比較次要條件。(假設姓名和年齡一直的人爲相同的人,若是想要對人按照年齡的大小來排序,若是年齡相同的人,須要如何處理?不能直接return 0,由於可能姓名不一樣(年齡相同姓名不一樣的人是不一樣的人)。此時就須要進行次要條件判斷(須要判斷姓名),只有姓名和年齡同時相等的才能夠返回0.)
經過return 0來判斷惟一性。
問題:爲何使用TreeSet存入字符串,字符串默認輸出是按升序排列的?由於字符串實現了一個接口,叫作Comparable 接口.字符串重寫了該接口的compareTo 方法,因此String對象具有了比較性.那麼一樣道理,個人自定義元素(例如Person類,Book類)想要存入TreeSet集合,就須要實現該接口,也就是要讓自定義對象具有比較性.
存入TreeSet集合中的元素要具有比較性.
比較性要實現Comparable接口,重寫該接口的compareTo方法
TreeSet屬於Set集合,該集合的元素是不能重複的,TreeSet如何保證元素的惟一性
經過compareTo或者compare方法中的來保證元素的惟一性。
添加的元素必需要實現Comparable接口。當compareTo()函數返回值爲0時,說明兩個對象相等,此時該對象不會添加進來。
比較器接口
----| Comparable compareTo(Object o) 元素自身具有比較性 ----| Comparator compare( Object o1, Object o2 ) 給容器傳入比較器 |
TreeSet集合排序的兩種方式:
一,讓元素自身具有比較性。
也就是元素須要實現Comparable接口,覆蓋compareTo 方法。
這種方式也做爲元素的天然排序,也可稱爲默認排序。
年齡按照搜要條件,年齡相同再比姓名。
import java.util.TreeSet;
public class Demo4 { public static void main(String[] args) { TreeSet ts = new TreeSet(); ts.add(new Person("aa", 20, "男")); ts.add(new Person("bb", 18, "女")); ts.add(new Person("cc", 17, "男")); ts.add(new Person("dd", 17, "女")); ts.add(new Person("dd", 15, "女")); ts.add(new Person("dd", 15, "女"));
System.out.println(ts); System.out.println(ts.size()); // 5
} }
class Person implements Comparable { private String name; private int age; private String gender;
public Person() {
}
public Person(String name, int age, String gender) {
this.name = name; this.age = age; this.gender = gender; }
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 String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
@Override public int hashCode() { return name.hashCode() + age * 37; }
public boolean equals(Object obj) { System.err.println(this + "equals :" + obj); if (!(obj instanceof Person)) { return false; } Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age;
}
public String toString() { return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]"; }
@Override public int compareTo(Object obj) {
Person p = (Person) obj; System.out.println(this+" compareTo:"+p); if (this.age > p.age) { return 1; } if (this.age < p.age) { return -1; } return this.name.compareTo(p.name); }
}
|
二,讓容器自身具有比較性,自定義比較器。
需求:當元素自身不具有比較性,或者元素自身具有的比較性不是所需的。
那麼這時只能讓容器自身具有。
定義一個類實現Comparator 接口,覆蓋compare方法。
並將該接口的子類對象做爲參數傳遞給TreeSet集合的構造函數。
當Comparable比較方式,及Comparator比較方式同時存在,以Comparator
比較方式爲主。
import java.util.Comparator; import java.util.TreeSet;
public class Demo5 { public static void main(String[] args) { TreeSet ts = new TreeSet(new MyComparator()); ts.add(new Book("think in java", 100)); ts.add(new Book("java 核心技術", 75)); ts.add(new Book("現代操做系統", 50)); ts.add(new Book("java就業教程", 35)); ts.add(new Book("think in java", 100)); ts.add(new Book("ccc in java", 100));
System.out.println(ts); } }
class MyComparator implements Comparator {
public int compare(Object o1, Object o2) { Book b1 = (Book) o1; Book b2 = (Book) o2; System.out.println(b1+" comparator "+b2); if (b1.getPrice() > b2.getPrice()) { return 1; } if (b1.getPrice() < b2.getPrice()) { return -1; } return b1.getName().compareTo(b2.getName()); }
}
class Book { private String name; private double price;
public Book() {
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public Book(String name, double price) {
this.name = name; this.price = price; }
@Override public String toString() { return "Book [name=" + name + ", price=" + price + "]"; }
} |
TreeSet練習
將字符串中的數值進行排序。
例如String str="8 10 15 5 2 7"; 2,5,7,8,10,15
使用 TreeSet完成。
思路:1,將字符串切割。
2,能夠將這些對象存入TreeSet集合。
由於TreeSet自身具有排序功能。
public class Demo5 { public static void main(String[] args) { String str = "8 10 15 5 2 7"; String[] strs = str.split(" "); TreeSet ts = new TreeSet(); for (int x = 0; x < strs.length; x++) { int y = Integer.parseInt(strs[x]); ts.add(y); } System.out.println(ts); } }
|
會保存插入的順序。
看到array,就要想到角標。
看到link,就要想到first,last。
看到hash,就要想到hashCode,equals.
看到tree,就要想到兩個接口。Comparable,Comparator。
練習:去除數組中的重複元素.
若是程序中存儲了幾百萬個學生,並且常常須要使用學號來搜索某個學生,那麼這個需求有效的數據結構就是Map。Map是一種依照鍵(key)存儲元素的容器,鍵(key)很像下標,在List中下標是整數。在Map中鍵(key)可使任意類型的對象。Map中不能有重複的鍵(Key),每一個鍵(key)都有一個對應的值(value)。一個鍵(key)和它對應的值構成map集合中的一個元素。
Map中的元素是兩個對象,一個對象做爲鍵,一個對象做爲值。鍵不能夠重複,可是值能夠重複。
看頂層共性方法找子類特有對象.
Map與Collection在集合框架中屬並列存在
Map存儲的是鍵值對
Map存儲元素使用put方法,Collection使用add方法
Map集合沒有直接取出元素的方法,而是先轉成Set集合,在經過迭代獲取元素
Map集合中鍵要保證惟一性
也就是Collection是單列集合, Map 是雙列集合。
總結:
Map一次存一對元素, Collection 一次存一個。Map 的鍵不能重複,保證惟一。
Map 一次存入一對元素,是以鍵值對的形式存在.鍵與值存在映射關係.必定要保證鍵的惟一性.
查看api文檔:
interface Map<K,V>
K - 此映射所維護的鍵的類型
V - 映射值的類型
概念
將鍵映射到值的對象。一個映射不能包含重複的鍵;每一個鍵最多隻能映射到一個值。
特色
Key和Value是1對1的關係,如:門牌號 :家 老公:老婆
雙列集合
Map學習體系: ---| Map 接口 將鍵映射到值的對象。一個映射不能包含重複的鍵;每一個鍵最多隻能映射到一個值。 ---| HashMap 採用哈希表實現,因此無序 ---| TreeMap 能夠對健進行排序 |
---|Hashtable: 底層是哈希表數據結構,線程是同步的,不能夠存入null鍵,null值。 效率較低,被HashMap 替代。 ---|HashMap: 底層是哈希表數據結構,線程是不一樣步的,能夠存入null鍵,null值。 要保證鍵的惟一性,須要覆蓋hashCode方法,和equals方法。 ---| LinkedHashMap: 該子類基於哈希表又融入了鏈表。能夠Map集合進行增刪提升效率。 ---|TreeMap: 底層是二叉樹數據結構。能夠對map集合中的鍵進行排序。須要使用Comparable或者Comparator 進行比較排序。return 0,來判斷鍵的惟一性。 |
常見方法
一、添加: 一、V put(K key, V value) (能夠相同的key值,可是添加的value值會覆 蓋前面的,返回值是前一個,若是沒有就返回null) 二、putAll(Map<? extends K,? extends V> m) 從指定映射中將全部映射關 系複製到此映射中(可選操做)。 二、刪除 一、remove() 刪除關聯對象,指定key對象 二、clear() 清空集合對象 3、獲取 1:value get(key); 能夠用於判斷鍵是否存在的狀況。當指定的鍵不存在的時候,返 回的是null。
三、判斷: 一、boolean isEmpty() 長度爲0返回true不然false 二、boolean containsKey(Object key) 判斷集合中是否包含指定的key 三、boolean containsValue(Object value) 判斷集合中是否包含指定的value 4、長度: Int size()
|
添加:
該案例使用了HashMap,創建了學生姓名和年齡之間的映射關係。並試圖添加劇復的鍵。
import java.util.HashMap; import java.util.Map;
public class Demo1 { public static void main(String[] args) { // 定義一個Map的容器對象 Map<String, Integer > map1 = new HashMap<String, Integer >(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // 添加劇復的鍵值(值不一樣),會返回集合中原有(重複鍵)的值, System.out.println(map1.put("jack", 30)); //20
Map<String, Integer> map2 = new HashMap<String, Integer>(); map2.put("張三丰", 100); map2.put("虛竹", 20); System.out.println("map2:" + map2); // 從指定映射中將全部映射關係複製到此映射中。 map1.putAll(map2); System.out.println("map1:" + map1); // } }
|
刪除:
// 刪除: // remove() 刪除關聯對象,指定key對象 // clear() 清空集合對象
Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // 指定key,返回刪除的鍵值對映射的值。 System.out.println("value:" + map1.remove("java")); map1.clear(); System.out.println("map1:" + map1); |
獲取:
// 獲取: // V get(Object key) 經過指定的key對象獲取value對象 // int size() 獲取容器的大小 Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // V get(Object key) 經過指定的key對象獲取value對象 // int size() 獲取容器的大小 System.out.println("value:" + map1.get("jack")); System.out.println("map.size:" + map1.size()); |
判斷:
// 判斷: // boolean isEmpty() 長度爲0返回true不然false // boolean containsKey(Object key) 判斷集合中是否包含指定的key // boolean containsValue(Object value)
Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); System.out.println("isEmpty:" + map1.isEmpty()); System.out.println("containskey:" + map1.containsKey("jack")); System.out.println("containsvalues:" + map1.containsValue(100)); |
遍歷Map的方式:
1、將map 集合中全部的鍵取出存入set集合。 Set<K> keySet() 返回全部的key對象的Set集合 再經過get方法獲取鍵對應的值。 2、 values() ,獲取全部的值. Collection<V> values()不能獲取到key對象 3、 Map.Entry對象 推薦使用 重點 Set<Map.Entry<k,v>> entrySet() 將map 集合中的鍵值映射關係打包成一個對象 Map.Entry對象經過Map.Entry 對象的getKey, getValue獲取其鍵和值。 |
|
第一種方式:使用keySet
將Map轉成Set集合(keySet()),經過Set的迭代器取出Set集合中的每個元素(Iterator)就是Map集合中的全部的鍵,再經過get方法獲取鍵對應的值。
import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set;
public class Demo2 { public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map);
// // 獲取方法: // 第一種方式: 使用keySet // 須要分別獲取key和value,沒有面向對象的思想 // Set<K> keySet() 返回全部的key對象的Set集合
Set<Integer> ks = map.keySet(); Iterator<Integer> it = ks.iterator(); while (it.hasNext()) { Integer key = it.next(); String value = map.get(key); System.out.println("key=" + key + " value=" + value); } } }
|
第二種方式: 經過values 獲取全部值,不能獲取到key對象
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map); // 第二種方式: // 經過values 獲取全部值,不能獲取到key對象 // Collection<V> values()
Collection<String> vs = map.values(); Iterator<String> it = vs.iterator(); while (it.hasNext()) { String value = it.next(); System.out.println(" value=" + value); } } |
第三種方式: Map.Entry
public static interface Map.Entry<K,V>
經過Map中的entrySet()方法獲取存放Map.Entry<K,V>對象的Set集合。
Set<Map.Entry<K,V>> entrySet()
面向對象的思想將map集合中的鍵和值映射關係打包爲一個對象,就是Map.Entry
,將該對象存入Set集合,Map.Entry是一個對象,那麼該對象具有的getKey,getValue得到鍵和值。
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map); // 第三種方式: Map.Entry對象 推薦使用 重點 // Set<Map.Entry<K,V>> entrySet()
// 返回的Map.Entry對象的Set集合 Map.Entry包含了key和value對象 Set<Map.Entry<Integer, String>> es = map.entrySet();
Iterator<Map.Entry<Integer, String>> it = es.iterator();
while (it.hasNext()) {
// 返回的是封裝了key和value對象的Map.Entry對象 Map.Entry<Integer, String> en = it.next();
// 獲取Map.Entry對象中封裝的key和value對象 Integer key = en.getKey(); String value = en.getValue();
System.out.println("key=" + key + " value=" + value); } } |
底層是哈希表數據結構,線程是不一樣步的,能夠存入null鍵,null值。要保證鍵的惟一性,須要覆蓋hashCode方法,和equals方法。
案例:自定義對象做爲Map的鍵。
package cn.itcast.gz.map;
import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set;
public class Demo3 { public static void main(String[] args) { HashMap<Person, String> hm = new HashMap<Person, String>(); hm.put(new Person("jack", 20), "1001"); hm.put(new Person("rose", 18), "1002"); hm.put(new Person("lucy", 19), "1003"); hm.put(new Person("hmm", 17), "1004"); hm.put(new Person("ll", 25), "1005"); System.out.println(hm); System.out.println(hm.put(new Person("rose", 18), "1006"));
Set<Entry<Person, String>> entrySet = hm.entrySet(); Iterator<Entry<Person, String>> it = entrySet.iterator(); while (it.hasNext()) { Entry<Person, String> next = it.next(); Person key = next.getKey(); String value = next.getValue(); System.out.println(key + " = " + value); } } }
class Person { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() {
return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
} }
|
TreeMap的排序,TreeMap能夠對集合中的鍵進行排序。如何實現鍵的排序?
方式一:元素自身具有比較性
和TreeSet同樣原理,須要讓存儲在鍵位置的對象實現Comparable接口,重寫compareTo方法,也就是讓元素自身具有比較性,這種方式叫作元素的天然排序也叫作默認排序。
方式二:容器具有比較性
當元素自身不具有比較性,或者自身具有的比較性不是所須要的。那麼此時可讓容器自身具有。須要定義一個類實現接口Comparator,重寫compare方法,並將該接口的子類實例對象做爲參數傳遞給TreeMap集合的構造方法。
注意:當Comparable比較方式和Comparator比較方式同時存在時,以Comparator的比較方式爲主;
注意:在重寫compareTo或者compare方法時,必需要明確比較的主要條件相等時要比較次要條件。(假設姓名和年齡一直的人爲相同的人,若是想要對人按照年齡的大小來排序,若是年齡相同的人,須要如何處理?不能直接return 0,覺得可能姓名不一樣(年齡相同姓名不一樣的人是不一樣的人)。此時就須要進行次要條件判斷(須要判斷姓名),只有姓名和年齡同時相等的才能夠返回0.)
經過return 0來判斷惟一性。
import java.util.TreeMap;
public class Demo4 { public static void main(String[] args) { TreeMap<String, Integer> tree = new TreeMap<String, Integer>(); tree.put("張三", 19); tree.put("李四", 20); tree.put("王五", 21); tree.put("趙六", 22); tree.put("周七", 23); tree.put("張三", 24); System.out.println(tree); System.out.println("張三".compareTo("李四"));//-2094 } } |
自定義元素排序
package cn.itcast.gz.map;
import java.util.Comparator; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap;
public class Demo3 { public static void main(String[] args) { TreeMap<Person, String> hm = new TreeMap<Person, String>( new MyComparator()); hm.put(new Person("jack", 20), "1001"); hm.put(new Person("rose", 18), "1002"); hm.put(new Person("lucy", 19), "1003"); hm.put(new Person("hmm", 17), "1004"); hm.put(new Person("ll", 25), "1005"); System.out.println(hm); System.out.println(hm.put(new Person("rose", 18), "1006"));
Set<Entry<Person, String>> entrySet = hm.entrySet(); Iterator<Entry<Person, String>> it = entrySet.iterator(); while (it.hasNext()) { Entry<Person, String> next = it.next(); Person key = next.getKey(); String value = next.getValue(); System.out.println(key + " = " + value); } } }
class MyComparator implements Comparator<Person> {
@Override public int compare(Person p1, Person p2) { if (p1.getAge() > p2.getAge()) { return -1; } else if (p1.getAge() < p2.getAge()) { return 1; } return p1.getName().compareTo(p2.getName()); }
}
class Person implements Comparable<Person> { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() {
return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
@Override public int compareTo(Person p) {
if (this.age > p.age) { return 1; } else if (this.age < p.age) { return -1; } return this.name.compareTo(p.name); }
} |
注意:Set的元素不可重複,Map的鍵不可重複,若是存入重複元素如何處理
Set元素重複元素不能存入add方法返回false
Map的重複健將覆蓋舊鍵,將舊值返回。
集合框架中的工具類:特色:該工具類中的方法都是靜態的。
Collections:常見方法: 1, 對list進行二分查找: 前提該集合必定要有序。 int binarySearch(list,key); //必須根據元素天然順序對列表進行升級排序 //要求list 集合中的元素都是Comparable 的子類。 int binarySearch(list,key,Comparator); 2,對list集合進行排序。 sort(list); //對list進行排序,其實使用的事list容器中的對象的compareTo方法 sort(list,comaprator); //按照指定比較器進行排序 3,對集合取最大值或者最小值。 max(Collection) max(Collection,comparator) min(Collection) min(Collection,comparator) reverse(list); 5,對比較方式進行強行逆轉。 Comparator reverseOrder(); Comparator reverseOrder(Comparator); 6,對list集合中的元素進行位置的置換。 swap(list,x,y); 7,對list集合進行元素的替換。若是被替換的元素不存在,那麼原集合不變。 replaceAll(list,old,new); 8,能夠將不一樣步的集合變成同步的集合。 Set synchronizedSet(Set<T> s) Map synchronizedMap(Map<K,V> m) List synchronizedList(List<T> list) 9. 若是想要將集合變數組: 可使用Collection 中的toArray 方法。注意:是Collection不是Collections工具類 傳入指定的類型數組便可,該數組的長度最好爲集合的size。 |
Arrays:用於對數組操做的工具類
1,二分查找,數組須要有序 binarySearch(int[]) binarySearch(double[])
2,數組排序 sort(int[]) sort(char[])…… 2, 將數組變成字符串。 toString(int[]) 3, 複製數組。 4, 複製部分數組。 copyOfRange(): 5, 比較兩個數組是否相同。 equals(int[],int[]); 6, 將數組變成集合。 List asList(T[]); 這樣能夠經過集合的操做來操做數組中元素, 可是不可使用增刪方法,add,remove。由於數組長度是固定的,會出現 UnsupportOperationExcetion。 可使用的方法:contains,indexOf。。。 若是數組中存入的基本數據類型,那麼asList會將數組實體做爲集合中的元素。 若是數組中的存入的引用數據類型,那麼asList會將數組中的元素做爲集合中 的元素。 |
import java.util.ArrayList; import java.util.Collections; import java.util.Arrays; import java.util.List; class Demo1 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(4); list.add(3); list.add(1); list.add(2); list.add(3); // 排序 Collections.sort(list); // 折半查找的前提是排序好的元素 System.out.println( Collections.binarySearch( list , 8 ) ); // 找不到返回-插入點-1 // 反序集合輸出 Collections.reverse( list ); System.out.println( list ); // 求最值 System.out.println( Collections.max( list ) ); // 4 // fill() 使用指定的元素替換指定集合中的全部元素 // Collections.fill( list, 5 ); System.out.println( list );
// 將數組轉換爲集合 Integer is[] = new Integer[]{6,7,8}; List<Integer> list2 = Arrays.asList(is); list.addAll( list2 ); System.out.println( list );
// 將List轉換爲數組 Object [] ins = list.toArray(); System.out.println( Arrays.toString( ins ) );
} } |
集合的練習
問題: 定義一個Person數組,將Person數組中的重複對象剔除?
思路:
1. 描述一個Person類
2. 將數組轉換爲Arrays.asList() List
3. Set addAll( list )
4. hashCode()且equals()
import java.util.Arrays; import java.util.Set; import java.util.List; import java.util.HashSet;
// 1. 描述Person類 class Person { public String name; public int age;
public Person() { }
public Person(String name, int age) { this.name = name; this.age = age; }
public String toString() {
return getClass().getName() + " : name=" + this.name + " age=" + this.age;
}
// 4. 重寫hashCode和equals() public int hashCode() {
return this.age; }
public boolean equals(Object o) { Person p = null; if (o instanceof Person) p = (Person) o; return this.name.equals(p.name) && (this.age == p.age); } }
class Demo2 { public static void main(String[] args) { Person[] ps = new Person[] { new Person("jack", 34), new Person("lucy", 20), new Person("lili", 10), new Person("jack", 34) }; // 遍歷數組 System.out.println(Arrays.toString(ps)); // 2. 將自定義對象數組轉換爲List集合 List<Person> list = Arrays.asList(ps); // 3. 將List轉換爲Set Set<Person> set = new HashSet<Person>(); set.addAll(list); System.out.println(set);
} } |
當集合中存儲的對象類型不一樣時,那麼會致使程序在運行的時候的轉型異常
import java.util.ArrayList; import java.util.Iterator;
public class Demo5 { public static void main(String[] args) { ArrayList arr = new ArrayList(); arr.add(new Tiger("華南虎")); arr.add(new Tiger("東北虎")); arr.add(new Sheep("喜羊羊")); System.out.println(arr); Iterator it = arr.iterator(); while (it.hasNext()) { Object next = it.next(); Tiger t = (Tiger) next; t.eat(); }
} } class Tiger { String name;
public Tiger() {
}
public Tiger(String name) { this.name = name; }
@Override public String toString() {
return "Tiger@name:" + this.name; }
public void eat() { System.out.println(this.name + "吃羊"); } }
class Sheep { String name;
public Sheep() {
}
public Sheep(String name) { this.name = name; }
@Override public String toString() { return "Sheep@name:" + this.name; }
public void eat() { System.out.println(this.name + "吃青草"); } } |
緣由 :發現雖然集合能夠存儲任意對象,可是若是須要使用對象的特有方法,那麼就須要類型轉換,若是集合中存入的對象不一樣,可能引起類型轉換異常.
[Tiger@name:華南虎, Tiger@name:東北虎, Sheep@name:喜羊羊] 華南虎吃羊 東北虎吃羊 Exception in thread "main" java.lang.ClassCastException: cn.itcast.gz.map.Sheep cannot be cast to cn.itcast.gz.map.Tiger at cn.itcast.gz.map.Demo5.main(Demo5.java:17) |
出現問題:
存入的是特定的對象,取出的時候是Object對象,須要強制類型轉換,可能誘發類型轉換異常.
沒法控制存入的是什麼類型的對象,取出對象的時候進行強轉時可能誘發異常.並且在編譯時期沒法發現問題.
雖然能夠再類型轉換的時候經過if語句進行類型檢查(instanceof),可是效率較低.(例如吃飯的時候,還須要判斷米飯裏有沒有沙子,吃飯效率低).能夠經過給容器加限定的形式規定容器只能存儲一種類型的對象.
就像給容器貼標籤說明該容器中只能存儲什麼樣類型的對象。
因此在jdk5.0後出現了泛型
泛型應用:
格式
public class Demo5 { public static void main(String[] args) { // 使用泛型後,規定該集合只能放羊,老虎就進不來了. ArrayList<Sheep> arr = new ArrayList<Sheep>(); arr.add(new Sheep("美羊羊")); arr.add(new Sheep("懶洋洋")); arr.add(new Sheep("喜羊羊")); // 編譯失敗 // arr.add(new Tiger("東北虎")); System.out.println(arr); Iterator<Sheep> it = arr.iterator(); while (it.hasNext()) { // 使用泛型後,不須要強制類型轉換了 Sheep next = it.next(); next.eat(); }
} } |
1. 將運行時的異常提早至編譯時發生。
2. 獲取元素的時候無需強轉類型,就避免了類型轉換的異常問題
格式 經過<> 來指定容器中元素的類型.
何時使用泛型:當類中操做的引用數據類型不肯定的時候,就可使用泛型類.
JDK5.0以前的Comparable
package java.lang; public interface Comparable {
public int compareTo(Object o); } |
JDK5.0以後的Comparable
package java.lang; public interface Comparable<T> {
public int compareTo(T o); } |
這裏的<T>表示泛型類型,隨後能夠傳入具體的類型來替換它.
細節一
聲明好泛型類型以後,集合中只能存放特定類型元素
public class Demo6 { public static void main(String[] args) { //建立一個存儲字符串的list ArrayList<String> arr=new ArrayList<String>(); arr.add("gz"); arr.add("itcast"); //存儲非字符串編譯報錯. arr.add(1); } } |
細節二:
泛型類型必須是引用類型
public class Demo6 { public static void main(String[] args) { // 泛型類型必須是引用類型,也就是說集合不能存儲基本數據類型 // ArrayList<int> arr2=new ArrayList<int>();
// 使用基本數據類型的包裝類 ArrayList<Integer> arr2 = new ArrayList<Integer>();
} }
|
細節三: 使用泛型後取出元素不須要類型轉換.
public class Demo6 { public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>(); arr.add("gzitcast"); arr.add("cditcast"); arr.add("bjitcast"); //使用泛型後取出元素不須要類型轉換. String str=arr.get(0); System.out.println(); } } |
需求:寫一個函數,調用者傳遞什麼類型的變量,該函數就返回什麼類型的變量?
實現一:
因爲沒法肯定具體傳遞什麼類型的數據.那麼方法的形參就定義爲Object類型.返回值也就是Object類型.可是使用該函數時須要強制類型轉換.
private Object getDate(Object obj) { return obj; } |
當不進行強制類型轉換可否寫出該功能.?
目前所學的知識沒法解決該問題
就須要使用泛型類解決
使用的泛型的自定義來解決以上問題。
泛型: 就是將類型看成變量處理。規範泛型的定義通常是一個大寫的任意字母。
1. 函數上的泛型定義
當函數中使用了一個不明確的數據類型,那麼在函數上就能夠進行泛型的定義。
public <泛型的聲明> 返回值類型 函數名( 泛型 變量名 ){
}
|
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 };
new Demo6().getData(5);
}
public <T> T getData(T data) { return data; } |
細節:
使用泛型方法前須要進行泛型聲明,使用一對尖括號 <泛型>,聲明的位置在static後返回值類型前。
當一個類中有多個函數聲明瞭泛型,那麼該泛型的聲明能夠聲明在類上。
格式
2. 類上的泛型聲明
修飾符 class 類名<泛型>{
} |
import java.util.Arrays;
public class Demo6<T> { public static void main(String[] args) { // 使用泛型類,建立對象的時候須要指定具體的類型 new Demo6<Integer>().getData(5); }
public T getData(T data) { return data; }
// 反序任意類型數組 public void reverse(T[] arr) { int start = 0; int end = arr.length - 1; for (int i = 0; i < arr.length; i++) { if (start < end) { T temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } }
}
|
在泛型類中定義一個靜態方法
public class Demo6<T> { public static void main(String[] args) { System.out.println(getData2(100)); }
public T getData(T data) { return data; }
//靜態方法 public static T getData2(T data) { return data; }
} |
注意:靜態方法不可使用類中定義的泛型
由於類中的泛型須要在對象初始化時指定具體的類型,而靜態優先於對象存在。那麼類中的靜態方法就須要單獨進行泛型聲明,聲明泛型必定要寫在static後,返回值類型以前
泛型類細節:
1、建立對象的時候要指定泛型的具體類型 2、建立對象時能夠不指定泛型的具體類型(和建立集合對象一眼)。默認是Object,例如咱們使用集合存儲元素的時候沒有使用泛型就是那麼參數的類型就是Object 3、類上面聲明的泛型只能應用於非靜態成員函數,若是靜態函數須要使用泛型,那麼 須要在函數上獨立聲明。 4、若是創建對象後指定了泛型的具體類型,那麼該對象操做方法時,這些方法只能操做一種數據類型。 5、因此既能夠在類上的泛型聲明,也能夠在同時在該類的方法中聲明泛型。 |
泛型練習:
定義泛型成員
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} |
若是Father類有子類,子類該如何實現
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} //子類指定了具體的類型 class Son extends Father<String>{
} //子類也須要使用泛型 class Son3<T> extends Father<T>{
} //錯誤寫法,父類上定義有泛型須要進行處理 class Son2 extends Father<T>{
} |
public class Demo8 { public static void main(String[] args) { MyInter<String> my = new MyInter<String>(); my.print("泛型");
MyInter2 my2 = new MyInter2(); my.print("只能傳字符串"); } }
interface Inter<T> { void print(T t); }
// 實現不知爲什麼類型時能夠這樣定義 class MyInter<T> implements Inter<T> { public void print(T t) { System.out.println("myprint:" + t); } } //使用接口時明確具體類型。 class MyInter2 implements Inter<String> {
@Override public void print(String t) { System.out.println("myprint:" + t);
}
}
|
需求:
定義一個方法,接收一個集合對象(該集合有泛型),並打印出集合中的全部元素。
例如集合對象以下格式:
Collection<Person> coll = new ArrayList<Person>(); coll.add(new Person("jack", 20)); coll.add(new Person("rose", 18)); Collection<Object> coll2 = new ArrayList<Object>(); coll2.add(new Object()); coll2.add(new Object()); coll2.add(new Object());
Collection<String> coll3 = new ArrayList<String>(); coll3.add("abc"); coll3.add("ddd"); coll3.add("eee"); |
分析,集合對象中的元素的類型是變化的,方法的形參的那麼泛型類型就只能定義爲Object類型.
import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator;
public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr);
//將集合的泛型設置類String類型,是Object子類 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); //因爲print方法接收的集合進行了元素限定,只接受限定爲Object類型的集合,編譯不經過 //print(hs); }
public static void print(Collection<Object> coll) { Iterator<Object> it = coll.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } }
|
可是,因爲print方法接收的集合進行了元素限定,只接受限定爲Object類型的集合,編譯不經過該問題如何解決?
能夠把方法的形參的泛型去掉,那麼方法中就把集合中的元素當作Object類型處理.
也可使用使用泛型通配符
public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr);
// 將集合的泛型設置類String類型,是Object子類 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); // 使用泛型通配符,編譯經過。 print(hs); }
public static void print(Collection<?> coll) { Iterator<?> it = coll.iterator(); while (it.hasNext()) {
Object next = it.next(); System.out.println(next); } } }
|
上述就使用了泛型通配符
通配符:? public void show(List<?> list) { } 能夠對類型進行限定範圍。 ?extends E: 接收E類型或者E的子類型。 ? super E: 接收E類型或者E的父類型。 |
限定泛型通配符的邊界
限定通配符的上邊界:
extends
接收Number 類型或者Number的子類型
正確:Vector<? extends Number> x = new Vector<Integer>(); 錯誤:Vector<? extends Number> x = new Vector<String>();
|
限定通配符的下邊界
super
接收Integer 或者Integer的父類型
正確:Vector<? super Integer> x = new Vector<Number>(); 錯誤:Vector<? super Integer> x = new Vector<Byte>(); |
總結:
JDK5中的泛型容許程序員在編寫集合代碼時,就限制集合的處理類型,從而把原來程序運行時可能發生問題,轉變爲編譯時的問題,以此提升程序的可讀性和穩定
注意:泛型是提供給javac編譯器使用的,它用於限定集合的輸入類型,讓編譯器在源代碼級別上,即擋住向集合中插入非法數據。但編譯器編譯完帶有泛形的java程序後,生成的class文件中將再也不帶有泛形信息,以此使程序運行效率不受到影響,這個過程稱之爲「擦除」。
泛型的基本術語,以ArrayList<E>爲例:<>念着typeof
ArrayList<E>中的E稱爲類型參數變量
ArrayList<Integer>中的Integer稱爲實際類型參數
整個稱爲ArrayList<E>泛型類型
整個ArrayList<Integer>稱爲參數化的類型ParameterizedType
最後:
關於數據結構能夠查看以下網站:
http://www.cs.armstrong.edu/liang/animation/index.html 數據結構
存儲對象的容器,面嚮對象語言對事物的體現都是以對象的形式,因此爲了方便對多個對象的操做,存儲對象,集合是存儲對象最經常使用的一種方式。
集合的出現就是爲了持有對象。集合中能夠存儲任意類型的對象, 並且長度可變。在程序中有可能沒法預先知道須要多少個對象, 那麼用數組來裝對象的話, 長度很差定義, 而集合解決了這樣的問題。
數組和集合類都是容器
數組長度是固定的,集合長度是可變的。數組中能夠存儲基本數據類型,集合只能存儲對象數組中存儲數據類型是單一的,集合中能夠存儲任意類型的對象。
集合類的特色
用於存儲對象,長度是可變的,能夠存儲不一樣類型的對象。
存儲類型單一的數據容器,操做複雜(數組一旦聲明好不可變)CRUD
集合作什麼
1:將對象添加到集合
2:從集合中刪除對象
3: 從集合中查找一個對象
4:從集合中修改一個對象就是增刪改查
注意:集合和數組中存放的都是對象的引用而非對象自己
Java工程師對不一樣的容器進行了定義,雖然容器不一樣,可是仍是有一些共性能夠抽取最後抽取了一個頂層接口,那麼就造成了一個集合框架。如何學習呢?固然是從頂層學起,頂層裏邊具備最共性,最基本的行爲。具體的使用,就要選擇具體的容器了。爲何? 由於不斷向上抽取的東西有多是不能建立對象的.抽象的可能性很大,而且子類對象的方法更多一些. 因此是看頂層,建立底層。那麼集合的頂層是什麼呢 叫作Collection
集合框架體系
---|Collection: 單列集合 ---|List: 有存儲順序, 可重複 ---|ArrayList: 數組實現, 查找快, 增刪慢 因爲是數組實現, 在增和刪的時候會牽扯到數組 增容, 以及拷貝元素. 因此慢。數組是能夠直接 按索引查找, 因此查找時較快 ---|LinkedList: 鏈表實現, 增刪快, 查找慢 因爲鏈表實現, 增長時只要讓前一個元素記住自 己就能夠, 刪除時讓前一個元素記住後一個元 素, 後一個元素記住前一個元素. 這樣的增刪效 率較高但查詢時須要一個一個的遍歷, 因此效率 較低 ---|Vector: 和ArrayList原理相同, 但線程安全, 效率略低 和ArrayList實現方式相同, 但考慮了線程安全問 題, 因此效率略低 ---|Set: 無存儲順序, 不可重複 ---|HashSet ---|TreeSet ---|LinkedHashSet ---| Map: 鍵值對 ---|HashMap ---|TreeMap ---|HashTable ---|LinkedHashMap |
爲何出現這麼多集合容器,由於每個容器對數據的存儲方式不一樣,這種存儲方式稱之爲數據結構(data structure)
注意 集合和數組中存放的都是對象的引用。
Collection |
咱們須要保存若干個對象的時候使用集合。 |
List
|
若是咱們須要保留存儲順序, 而且保留重複元素, 使用List. 若是查詢較多, 那麼使用ArrayList 若是存取較多, 那麼使用LinkedList 若是須要線程安全, 那麼使用Vector |
Set
|
若是咱們不須要保留存儲順序, 而且須要去掉重複元素, 使用Set. 若是咱們須要將元素排序, 那麼使用TreeSet 若是咱們不須要排序, 使用HashSet, HashSet比 TreeSet效率高. 若是咱們須要保留存儲順序, 又要過濾重複元素, 那麼使用LinkedHashSet |
Collection接口有兩個子接口:
List(鏈表|線性表)
Set(集)
特色:
Collection中描述的是集合共有的功能(CRUD)
List可存放重複元素,元素存取是有序的
Set不能夠存放重複元素,元素存取是無序的
java.util.Collection ---| Collection 描述全部接口的共性 ----| List接口 能夠有重複元素的集合 ----| Set 接口 不能夠有重複元素的集合
|
2:學習集合對象
學習Collection中的共性方法,多個容器在不斷向上抽取就出現了該體系。發現Collection接口中具備全部容器都具有的共性方法。查閱API時,就能夠直接看該接口中的方法。並建立其子類對象對集合進行基本應用。當要使用集合對象中特有的方法,在查看子類具體內容。
查看api 文檔Collection在在java.util 中(注意是大寫Collection)
注意在現階段遇到的 E T 之類的類型,須要暫時理解爲object 由於涉及到了泛型.
3:建立集合對象,使用Collection中的List的具體實現類ArrayList
1:Collection coll=new Arraylist();
增長: 1:add() 將指定對象存儲到容器中 add 方法的參數類型是Object 便於接收任意對象 2:addAll() 將指定集合中的元素添加到調用該方法和集合中 刪除: 3:remove() 將指定的對象從集合中刪除 4:removeAll() 將指定集合中的元素刪除 修改 5:clear() 清空集合中的全部元素 判斷 6:isEmpty() 判斷集合是否爲空 7:contains() 判斷集合何中是否包含指定對象
8:containsAll() 判斷集合中是否包含指定集合 使用equals()判斷兩個對象是否相等 獲取: 9:int size() 返回集合容器的大小
轉成數組10: toArray() 集合轉換數組 |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); System.out.println(list); // [計算機網絡, 現代操做系統, java編程思想]
// 增長2 將list容器元素添加到list2容器中 Collection list2 = new ArrayList(); list2.add("java核心技術"); list2.addAll(list); list2.add("java語言程序設計"); System.out.println(list2); // [java核心技術, 計算機網絡, 現代操做系統, java編程思想, java語言程序設計] } |
// 刪除1 remove boolean remove = list2.remove("java核心技術"); System.out.println(remove); // true System.out.println(list2); // //刪除2 removeAll() 將list中的元素刪除 boolean removeAll = list2.removeAll(list); System.out.println(removeAll);//true System.out.println(list2);//[java語言程序設計] |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 修改 clear() 清空集合中的全部元素 list.clear(); System.out.println(list); //[] } |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
boolean empty = list.isEmpty(); System.out.println(empty);// false boolean contains = list.contains("java編程思想"); System.out.println(contains);// true Collection list2 = new ArrayList(); list2.add("水許傳"); boolean containsAll = list.containsAll(list2); System.out.println(containsAll);// false
} |
public static void main(String[] args) { Collection list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取 集合容器的大小 int size = list.size(); System.out.println(size); } |
該案例要求完成使用集合:
public static void main(String[] args) {
// 建立集合對象 Collection coll = new ArrayList();
// 建立Person對象 Person p1 = new Person("jack", 25); Person p2 = new Person("rose", 22); Person p3 = new Person("lucy", 20); Person p4 = new Person("jack", 25);
// 集合中添加一些Perosn
// 刪除指定Person
// 刪除全部Person
// 判斷容器中是否還有Person
// 判斷容器中是否包含指定Person
// 獲取容器中Person的個數
// 將容器變爲數組,遍歷除全部Person
} |
分析:
1:Person類
1:姓名和年齡
2:重寫hashCode和equals方法
1:若是不重寫,調用Object類的equals方法,判斷內存地址,爲false
1:若是是Person類對象,而且姓名和年齡相同就返回true
2:若是不重寫,調用父類hashCode方法
1:若是equals方法相同,那麼hashCode也要相同,須要重寫hashCode方法
3:重寫toString方法
1:不重寫,直接調用Object類的toString方法,打印該對象的內存地址
Person類
class Person { private String name; private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
@Override public int hashCode() { return this.name.hashCode() + age; }
@Override public boolean equals(Object obj) { if (!(obj instanceof Person)) { return false; } Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; }
@Override public String toString() { return "Person :name=" + name + ", age=" + age; }
}
|
public static void main(String[] args) { Person p1 = new Person("張三", 19); Person p2 = new Person("李四", 20); Person p3 = new Person("王五", 18); Collection list = new ArrayList(); list.add(p1); list.add(p2); list.add(p3); // isEmpty() 判斷集合是否爲空 boolean empty = list.isEmpty(); System.out.println(empty); // 返回集合容器的大小 int size = list.size(); System.out.println(size); // contains()判斷集合何中是否包含指定對象 boolean contains = list.contains(p1); System.out.println(contains);
// remove(); 將指定的對象從集合中刪除 list.remove(p1);
// clear() 清空集合中的全部元素 list.clear(); System.out.println(list);
}
|
//使用集合存儲自定義對象2 class Book { private String name; private double price;
public Book() {
}
public Book(String name, double price) { this.name = name; this.price = price; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
@Override public int hashCode() { return (int) (this.name.hashCode() + price); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Book)) { return false; } Book book = (Book) obj; return this.name.equals(book.name) && this.price == book.price; }
@Override public String toString() { return "book:@ name:" + this.name + ", price:" + this.price; } } public class Demo1 { public static void main(String[] args) { Collection col = new ArrayList(); col.add(new Book("think in java", 100)); col.add(new Book("core java", 200)); System.out.println(col); } } |
---| Iterable 接口 Iterator iterator() ----| Collection 接口 ------| List 接口 元素能夠重複,容許在指定位置插入元素,並經過索 引來訪問元素 |
1:增長 void add(int index, E element) 指定位置添加元素 boolean addAll(int index, Collection c) 指定位置添加集合 2:刪除 E remove(int index) 刪除指定位置元素
3:修改 E set(int index, E element) 返回的是須要替換的集合中的元素 4:查找: E get(int index) 注意: IndexOutOfBoundsException int indexOf(Object o) // 找不到返回-1 lastIndexOf(Object o) 5:求子集合 List<E> subList(int fromIndex, int toIndex) // 不包含toIndex |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // add,在0角標位置添加一本書 list.add(0, "舒克和貝塔"); System.out.println(list); // 在list2集合的1角標位置添加list集合元素 List list2 = new ArrayList(); list2.add("史記"); list2.add("資治通鑑"); list2.add("全球通史"); boolean addAll = list2.addAll(1, list); System.out.println(addAll); //true System.out.println(list2); } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 刪除0角標元素 Object remove = list.remove(0); System.out.println(remove); } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 修改2角標位置的書,返回的原來2角標位置的書 Object set = list.set(2, "邊城"); System.out.println(set); //java編程思想 System.out.println(list);
} |
查找
List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 查找: E get(int index) 注意角標越界 Object set = list.get(list.size() - 1); System.out.println(set); // java語言程序設計 System.out.println(list); list.get(list.size()); //IndexOutOfBoundsException } |
public static void main(String[] args) { List list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); list.add("java編程思想"); System.out.println(list); // 查找: E get(int index) 注意角標越界 Object set = list.get(list.size() - 1); System.out.println(set); // java語言程序設計 System.out.println(list); // list.get(list.size()); //IndexOutOfBoundsException
// indexOf(Object o) 返回第一次出現的指定元素的角標 int indexOf = list.indexOf("java編程思想"); System.out.println(indexOf); // 2 // 沒有找到,返回-1 int indexOf2 = list.indexOf("三國志"); System.out.println(indexOf2); // -1
// lastIndexOf 返回最後出現的指定元素的角標 int lastIndexOf = list.lastIndexOf("java編程思想"); System.out.println(lastIndexOf); // 5 } |
--| Iterable ----| Collection ------| List ---------| ArrayList 底層採用數組實現,默認10。每次增加 60%,((oldCapacity * 3)/2 + 1) 查詢快,增刪慢。 ---------| LinkedList |
ArrayList:實現原理:
數組實現, 查找快, 增刪慢
數組爲何是查詢快?由於數組的內存空間地址是連續的.
ArrayList底層維護了一個Object[] 用於存儲對象,默認數組的長度是10。能夠經過 new ArrayList(20)顯式的指定用於存儲對象的數組的長度。
當默認的或者指定的容量不夠存儲對象的時候,容量自動增加爲原來的容量的1.5倍。
因爲ArrayList是數組實現, 在增和刪的時候會牽扯到數組增容, 以及拷貝元素. 因此慢。數組是能夠直接按索引查找, 因此查找時較快
能夠考慮,假設向數組的0角標未知添加元素,那麼原來的角標位置的元素須要總體日後移,而且數組可能還要增容,一旦增容,就須要要將老數組的內容拷貝到新數組中.因此數組的增刪的效率是很低的.
練習:去除ArrayList集合中重複元素
1:存入字符串元素
2:存入自定義對象元素(如Perosn對象)
原理:
循環遍歷該集合,每取出一個放置在新的集合中,放置以前先判斷新的集合是否以包含了新的元素。
思路: 存入人的對象. 1先定義person 類 2將該類的實例存入集合 3 將對象元素進行操做. 注意:自定義對象要進行復寫toString 和 equals 方法. 爲何? 由於object 是person 的父類,object 中的toString 返回的是哈希值,object 類中equals 方法比較的是對象的地址值. 思路 1存入字符串對象 2存入自定義對象 如person 2建立容器,用於存儲非重複元素 3對原容器進行遍歷,在遍歷過程當中進行判斷遍歷到的元素是否在容器中存在.(contains) 4若是存在,就不存入,不然存入. 5 返回新容器 |
public class Demo6 { public static void main(String[] args) { ArrayList arr = new ArrayList(); Person p1 = new Person("jack", 20); Person p2 = new Person("rose", 18); Person p3 = new Person("rose", 18); arr.add(p1); arr.add(p2); arr.add(p3); System.out.println(arr); ArrayList arr2 = new ArrayList(); for (int i = 0; i < arr.size(); i++) { Object obj = arr.get(i); Person p = (Person) obj; if (!(arr2.contains(p))) { arr2.add(p); } } System.out.println(arr2); } }
class Person { private String name; private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() { return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (!(obj instanceof Person)) { return false; } Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age; }
@Override public String toString() { return "Person@name:" + this.name + " age:" + this.age; }
} |
在實際的開發中ArrayList是使用頻率最高的一個集合。
--| Iterable ----| Collection ------| List ---------| ArrayList 底層採用數組實現,默認10。每次增加 60%,((oldCapacity * 3)/2 + 1) 查詢快,增刪慢。 ---------| LinkedList 底層採用鏈表實現,增刪快,查詢慢。 |
LinkedList:鏈表實現, 增刪快, 查找慢
因爲LinkedList:在內存中的地址不連續,須要讓上一個元素記住下一個元素.因此每一個元素中保存的有下一個元素的位置.雖然也有角標,可是查找的時候,須要從頭往下找,顯然是沒有數組查找快的.可是,鏈表在插入新元素的時候,只須要讓前一個元素記住新元素,讓新元素記住下一個元素就能夠了.因此插入很快.
因爲鏈表實現, 增長時只要讓前一個元素記住本身就能夠, 刪除時讓前一個元素記住後一個元素, 後一個元素記住前一個元素. 這樣的增刪效率較高。
但查詢時須要一個一個的遍歷, 因此效率較低。
特有方法
1:方法介紹 addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast() 若是集合中沒有元素,獲取或者刪除元 素拋:NoSuchElementException 2:數據結構 1:棧 (1.6) 先進後出 push() pop() 2:隊列(雙端隊列1.5) 先進先出 offer() poll() 3:返回逆序的迭代器對象 descendingIterator() 返回逆序的迭代器對象
|
基本方法
import java.util.Iterator; import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("西遊記"); list.add("三國演義"); list.add("石頭記"); list.add("水滸傳"); list.add("全球通史"); list.addFirst("史記"); list.addLast("吶喊"); // list.addFirst(null); // list.addLast(null); System.out.println(list); // 獲取指定位置處的元素。 String str = (String) list.get(0); // 返回此列表的第一個元素。 String str2 = (String) list.getFirst(); System.out.println(str.equals(str2));
// 獲取指定位置處的元素。 String str3 = (String) list.get(list.size() - 1); // 返回此列表的最後一個元素。 String str4 = (String) list.getLast(); System.out.println(str3.equals(str4));
// 獲取但不移除此列表的頭(第一個元素)。 Object element = list.element(); System.out.println(element);
int size = list.size(); System.out.println(size); }
|
迭代
mport java.util.Iterator; import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("西遊記"); list.add("三國演義"); list.add("石頭記"); list.add("水滸傳"); list.add("全球通史"); Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } } } |
逆序迭代
import java.util.Iterator; import java.util.LinkedList;
public class Demo6 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.add("aa"); list.add("bb"); list.add("cc"); Iterator dit = list.descendingIterator(); while (dit.hasNext()) { System.out.println(dit.next()); } } }
|
注意:可使用該集合去模擬出隊列(先進先出) 或者堆棧(後進先出) 數據結構。
堆棧(後進先出)
//堆棧(後進先出) 數據結構 public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); // 壓棧,先進後出 list.push("西遊記"); list.push("三國演義"); list.push("石頭記"); list.push("水滸傳"); System.out.println(list); // 彈棧 String str1 = (String) list.pop(); System.out.println(str1); String str2 = (String) list.pop(); System.out.println(str2); String str3 = (String) list.pop(); System.out.println(str3); String str4 = (String) list.pop(); System.out.println(str4); System.out.println(list.size());// 0 System.out.println(list); //[] } }
|
隊列,先進先出
import java.util.LinkedList;
public class Demo3 { public static void main(String[] args) { LinkedList list = new LinkedList(); // 隊列,先進先出 list.offer("西遊記"); list.offer("三國演義"); list.offer("石頭記"); list.offer("水滸傳"); System.out.println(list); // 出隊列 System.out.println(list.poll()); System.out.println(list.poll()); System.out.println(list.poll()); System.out.println(list.poll());
System.out.println(list.size());
System.out.println(list.peek()); // 獲取隊列的頭元素,可是不刪除 System.out.println(list.peekFirst()); // 獲取隊列的頭元素,可是不刪除 System.out.println(list.peekLast()); // 獲取隊列的最後一個元素可是不刪除
} }
|
ArrayList 和 LinkedList的存儲查找的優缺點:
1、ArrayList 是採用動態數組來存儲元素的,它容許直接用下標號來直接查找對應的元素。可是,可是插入元素要涉及數組元素移動及內存的操做。總結:查找速度快,插入操做慢。
2、LinkedList 是採用雙向鏈表實現存儲,按序號索引數據須要進行前向或後向遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入速度較快
問題:有一批數據要存儲,要求存儲這批數據不能出現重複數據,ArrayList、LinkedList都無法知足需求。解決辦法:使用 set集合。
Vector: 描述的是一個線程安全的ArrayList。
ArrayList: 單線程效率高
Vector : 多線程安全的,因此效率低
特有的方法:
void addElement(E obj) 在集合末尾添加元素 E elementAt( int index) 返回指定角標的元素 Enumeration elements() 返回集合中的全部元素,封裝到Enumeration對象中
Enumeration 接口:
boolean hasMoreElements() 測試此枚舉是否包含更多的元素。 E nextElement() 若是此枚舉對象至少還有一個可提供的元素,則返回此枚舉的下一個元素。 |
public static void main(String[] args) { Vector v = new Vector(); v.addElement("aaa"); v.addElement("bbb"); v.addElement("ccc"); System.out.println( v ); System.out.println( v.elementAt(2) ); // ccc // 遍歷Vector遍歷 Enumeration ens = v.elements(); while ( ens.hasMoreElements() ) { System.out.println( ens.nextElement() ); } } |
爲了方便的處理集合中的元素,Java中出現了一個對象,該對象提供了一些方法專門處理集合中的元素.例如刪除和獲取集合中的元素.該對象就叫作迭代器(Iterator).
對 Collection 進行迭代的類,稱其爲迭代器。仍是面向對象的思想,專業對象作專業的事情,迭代器就是專門取出集合元素的對象。可是該對象比較特殊,不能直接建立對象(經過new),該對象是之內部類的形式存在於每一個集合類的內部。
如何獲取迭代器?Collection接口中定義了獲取集合類迭代器的方法(iterator()),因此全部的Collection體系集合均可以獲取自身的迭代器。
正是因爲每個容器都有取出元素的功能。這些功能定義都同樣,只不過實現的具體方式不一樣(由於每個容器的數據結構不同)因此對共性的取出功能進行了抽取,從而出現了Iterator接口。而每個容器都在其內部對該接口進行了內部類的實現。也就是將取出方式的細節進行封裝。
Jdk1.5以後添加的新接口, Collection的父接口. 實現了Iterable的類就是可迭代的.而且支持加強for循環。該接口只有一個方法即獲取迭代器的方法iterator()能夠獲取每一個容器自身的迭代器Iterator。(Collection)集合容器都須要獲取迭代器(Iterator)因而在5.0後又進行了抽取將獲取容器迭代器的iterator()方法放入到了Iterable接口中。Collection接口進程了Iterable,因此Collection體系都具有獲取自身迭代器的方法,只不過每一個子類集合都進行了重寫(由於數據結構不一樣)
Iterator iterator() 返回該集合的迭代器對象
該類主要用於遍歷集合對象,該類描述了遍歷集合的常見方法 1:java.lang. Itreable ---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| Set接口 不能夠有重複元素的集合 |
public interface Iterable<T>
Itreable 該接口僅有一個方法,用於返回集合迭代器對象。
1: Iterator<T> iterator() 返回集合的迭代器對象
Iterator接口定義的方法
Itreator 該接口是集合的迭代器接口類,定義了常見的迭代方法 1:boolean hasNext() 判斷集合中是否有元素,若是有元素能夠迭代,就返回true。 2: E next() 返回迭代的下一個元素,注意: 若是沒有下一個元素時,調用 next元素會拋出NoSuchElementException
3: void remove() 從迭代器指向的集合中移除迭代器返回的最後一個元素(可選操 做)。 |
思考:爲何next方法的返回類型是Object的呢? 爲了能夠接收任意類型的對象,那麼返回的時候,不知道是什麼類型的就定義爲object
1:while循環
public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } } |
2:for循環
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
for (Iterator it = list.iterator(); it.hasNext();) { //迭代器的next方法返回值類型是Object,因此要記得類型轉換。 String next = (String) it.next(); System.out.println(next); } } }
|
須要取出全部元素時,能夠經過循環,java 建議使用for 循環。由於能夠對內存進行一下優化。
3:使用迭代器清空集合
public class Demo1 { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); coll.add("ddd"); System.out.println(coll); Iterator it = coll.iterator(); while (it.hasNext()) { it.next(); it.remove(); } System.out.println(coll); } } |
細節一:
若是迭代器的指針已經指向了集合的末尾,那麼若是再調用next()會返回NoSuchElementException異常
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
Iterator it = list.iterator(); while (it.hasNext()) { String next = (String) it.next(); System.out.println(next); } // 迭代器的指針已經指向了集合的末尾 // String next = (String) it.next(); // java.util.NoSuchElementException } } |
細節二:
若是調用remove以前沒有調用next是不合法的,會拋出IllegalStateException
import java.util.ArrayList; import java.util.Iterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
Iterator it = list.iterator(); while (it.hasNext()) { // 調用remove以前沒有調用next是不合法的 // it.remove(); // java.lang.IllegalStateException String next = (String) it.next(); System.out.println(next); }
} }
|
4:迭代器原理
查看ArrayList源碼
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() { return cursor != size(); }
public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }
public void remove() { if (lastRet == -1) throw new IllegalStateException(); checkForComodification();
try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
} |
5:注意在對集合進行迭代過程當中,不容許出現迭代器之外的對元素的操做,由於這樣會產生安全隱患,java會拋出異常併發修改異常(ConcurrentModificationException),普通迭代器只支持在迭代過程當中的刪除動做。
注意: ConcurrentModificationException: 當一個集合在循環中即便用引用變量操做集合又使用迭代器操做集合對象, 會拋出該異常。
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Demo1 { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("aaa"); coll.add("bbb"); coll.add("ccc"); coll.add("ddd"); System.out.println(coll); Iterator it = coll.iterator(); while (it.hasNext()) { it.next(); it.remove(); coll.add("abc"); // 出現了迭代器之外的對元素的操做 } System.out.println(coll); } } |
若是是List集合,想要在迭代中操做元素可使用List集合的特有迭代器ListIterator,該迭代器支持在迭代過程當中,添加元素和修改元素。
public interface ListIterator extends Iterator
ListIterator<E> listIterator()
---| Iterator hasNext() next() remove() ------| ListIterator Iterator子接口 List專屬的迭代器 add(E e) 將指定的元素插入列表(可選操做)。該元素直接插入到 next 返回的下一個元素的前面(若是有) void set(E o) 用指定元素替換 next 或 previous 返回的最後一個元素 hasPrevious() 逆向遍歷列表,列表迭代器有多個元素,則返回 true。 previous() 返回列表中的前一個元素。 |
Iterator在迭代時,只能對元素進行獲取(next())和刪除(remove())的操做。
對於 Iterator 的子接口ListIterator 在迭代list 集合時,還能夠對元素進行添加
(add(obj)),修改set(obj)的操做。
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取List專屬的迭代器 ListIterator lit = list.listIterator();
while (lit.hasNext()) { String next = (String) lit.next(); System.out.println(next); }
} }
|
倒序遍歷
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list); // 獲取List專屬的迭代器 ListIterator lit = list.listIterator(); while (lit.hasNext()) { String next = (String) lit.next(); System.out.println(next); } System.out.println("***************"); while (lit.hasPrevious()) { String next = (String) lit.previous(); System.out.println(next); }
} }
|
Set方法:用指定元素替換 next 或 previous 返回的最後一個元素
import java.util.ArrayList; import java.util.ListIterator;
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
ListIterator lit = list.listIterator(); lit.next(); // 計算機網絡 lit.next(); // 現代操做系統 System.out.println(lit.next()); // java編程思想 //用指定元素替換 next 或 previous 返回的最後一個元素 lit.set("平凡的世界");// 將java編程思想替換爲平凡的世界 System.out.println(list);
} }
|
add方法將指定的元素插入列表,該元素直接插入到 next 返回的元素的後
public class Demo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 增長:add() 將指定對象存儲到容器中 list.add("計算機網絡"); list.add("現代操做系統"); list.add("java編程思想"); list.add("java核心技術"); list.add("java語言程序設計"); System.out.println(list);
ListIterator lit = list.listIterator(); lit.next(); // 計算機網絡 lit.next(); // 現代操做系統 System.out.println(lit.next()); // java編程思想 // 將指定的元素插入列表,該元素直接插入到 next 返回的元素的後 lit.add("平凡的世界");// 在java編程思想後添加平凡的世界 System.out.println(list);
} } |
Set:注重獨一無二的性質,該體系集合能夠知道某物是否已近存在於集合中,不會存儲重複的元素
用於存儲無序(存入和取出的順序不必定相同)元素,值不能重複。
對象的相等性
引用到堆上同一個對象的兩個引用是相等的。若是對兩個引用調用hashCode方法,會獲得相同的結果,若是對象所屬的類沒有覆蓋Object的hashCode方法的話,hashCode會返回每一個對象特有的序號(java是依據對象的內存地址計算出的此序號),因此兩個不一樣的對象的hashCode值是不可能相等的。
若是想要讓兩個不一樣的Person對象視爲相等的,就必須覆蓋Object繼下來的hashCode方法和equals方法,由於Object hashCode方法返回的是該對象的內存地址,因此必須重寫hashCode方法,才能保證兩個不一樣的對象具備相同的hashCode,同時也須要兩個不一樣對象比較equals方法會返回true
該集合中沒有特有的方法,直接繼承自Collection。
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| ArrayList ---| LinkedList ---| Set接口 不能夠有重複元素的集合 |
案例:set集合添加元素並使用迭代器迭代元素。
import java.util.HashSet; import java.util.Iterator; import java.util.Set;
public class Demo4 { public static void main(String[] args) { //Set 集合存和取的順序不一致。 Set hs = new HashSet(); hs.add("世界軍事"); hs.add("兵器知識"); hs.add("艦船知識"); hs.add("漢和防務"); System.out.println(hs); // [艦船知識, 世界軍事, 兵器知識, 漢和防務] Iterator it = hs.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } |
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 能夠有重複元素的集合 ---| ArrayList ---| LinkedList ---| Set接口 不能夠有重複元素的集合 ---| HashSet 線程不安全,存取速度快。底層是以哈希表實現的。 |
HashSet
哈希表邊存放的是哈希值。HashSet存儲元素的順序並非按照存入時的順序(和List顯然不一樣) 是按照哈希值來存的因此取數據也是按照哈希值取得。
HashSet不存入重複元素的規則.使用hashcode和equals
因爲Set集合是不能存入重複元素的集合。那麼HashSet也是具有這一特性的。HashSet如何檢查重複?HashSet會經過元素的hashcode()和equals方法進行判斷元素師否重複。
當你試圖把對象加入HashSet時,HashSet會使用對象的hashCode來判斷對象加入的位置。同時也會與其餘已經加入的對象的hashCode進行比較,若是沒有相等的hashCode,HashSet就會假設對象沒有重複出現。
簡單一句話,若是對象的hashCode值是不一樣的,那麼HashSet會認爲對象是不可能相等的。
所以咱們自定義類的時候須要重寫hashCode,來確保對象具備相同的hashCode值。
若是元素(對象)的hashCode值相同,是否是就沒法存入HashSet中了? 固然不是,會繼續使用equals 進行比較.若是 equals爲true 那麼HashSet認爲新加入的對象重複了,因此加入失敗。若是equals 爲false那麼HashSet 認爲新加入的對象沒有重複.新元素能夠存入.
總結:
元素的哈希值是經過元素的hashcode方法 來獲取的, HashSet首先判斷兩個元素的哈希值,若是哈希值同樣,接着會比較equals方法 若是 equls結果爲true ,HashSet就視爲同一個元素。若是equals 爲false就不是同一個元素。
哈希值相同equals爲false的元素是怎麼存儲呢,就是在一樣的哈希值下順延(能夠認爲哈希值相同的元素放在一個哈希桶中)。也就是哈希同樣的存一列。
hashtable
圖1:hashCode值不相同的狀況
圖2:hashCode值相同,但equals不相同的狀況。
HashSet:經過hashCode值來肯定元素在內存中的位置。一個hashCode位置上能夠存放多個元素。
當hashcode() 值相同equals() 返回爲true 時,hashset 集合認爲這兩個元素是相同的元素.只存儲一個(重複元素沒法放入)。調用原理:先判斷hashcode 方法的值,若是相同纔會去判斷equals 若是不相同,是不會調用equals方法的。
HashSet究竟是如何判斷兩個元素重複。
經過hashCode方法和equals方法來保證元素的惟一性,add()返回的是boolean類型
判斷兩個元素是否相同,先要判斷元素的hashCode值是否一致,只有在該值一致的狀況下,纔會判斷equals方法,若是存儲在HashSet中的兩個對象hashCode方法的值相同equals方法返回的結果是true,那麼HashSet認爲這兩個元素是相同元素,只存儲一個(重複元素沒法存入)。
注意:HashSet集合在判斷元素是否相同先判斷hashCode方法,若是相同纔會判斷equals。若是不相同,是不會調用equals方法的。
HashSet 和ArrayList集合都有判斷元素是否相同的方法,
boolean contains(Object o)
HashSet使用hashCode和equals方法,ArrayList使用了equals方法
練習:使用HashSet存儲字符串,並嘗試添加劇復字符串
回顧String類的equals()、hashCode()兩個方法。
import java.util.HashSet; import java.util.Iterator; import java.util.Set;
public class Demo4 { public static void main(String[] args) { // Set 集合存和取的順序不一致。 Set hs = new HashSet(); hs.add("世界軍事"); hs.add("兵器知識"); hs.add("艦船知識"); hs.add("漢和防務");
// 返回此 set 中的元素的數量 System.out.println(hs.size()); // 4
// 若是此 set 還沒有包含指定元素,則返回 true boolean add = hs.add("世界軍事"); // false System.out.println(add);
// 返回此 set 中的元素的數量 System.out.println(hs.size());// 4 Iterator it = hs.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } }
|
使用HashSet存儲自定義對象,並嘗試添加劇復對象(對象的重複的斷定)
package cn.itcast.gz.map;
import java.util.HashSet; import java.util.Iterator;
public class Demo4 { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Person("jack", 20)); hs.add(new Person("rose", 20)); hs.add(new Person("hmm", 20)); hs.add(new Person("lilei", 20)); hs.add(new Person("jack", 20));
Iterator it = hs.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } }
class Person { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() { System.out.println("hashCode:" + this.name); return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { System.out.println(this + "---equals---" + obj); if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
}
|
問題:如今有一批數據,要求不能重複存儲元素,並且要排序。ArrayList 、 LinkedList不能去除重複數據。HashSet能夠去除重複,可是是無序。
案例:使用TreeSet集合存儲字符串元素,並遍歷
import java.util.TreeSet;
public class Demo5 { public static void main(String[] args) { TreeSet ts = new TreeSet(); ts.add("ccc"); ts.add("aaa"); ts.add("ddd"); ts.add("bbb");
System.out.println(ts); // [aaa, bbb, ccc, ddd]
} } |
---| Itreable 接口 實現該接口可使用加強for循環 ---| Collection 描述全部集合共性的接口 ---| List接口 有序,能夠重複,有角標的集合 ---| ArrayList ---| LinkedList ---| Set接口 無序,不能夠重複的集合 ---| HashSet 線程不安全,存取速度快。底層是以hash表實現的。 ---| TreeSet 紅-黑樹的數據結構,默認對元素進行天然排序(String)。若是在比較的時候兩個對象返回值爲0,那麼元素重複。 |
紅-黑樹
紅黑樹是一種特定類型的二叉樹
紅黑樹算法的規則: 左小右大。
既然TreeSet能夠天然排序,那麼TreeSet一定是有排序規則的。
1:讓存入的元素自定義比較規則。
2:給TreeSet指定排序規則。
方式一:元素自身具有比較性
元素自身具有比較性,須要元素實現Comparable接口,重寫compareTo方法,也就是讓元素自身具有比較性,這種方式叫作元素的天然排序也叫作默認排序。
方式二:容器具有比較性
當元素自身不具有比較性,或者自身具有的比較性不是所須要的。那麼此時可讓容器自身具有。須要定義一個類實現接口Comparator,重寫compare方法,並將該接口的子類實例對象做爲參數傳遞給TreeMap集合的構造方法。
注意:當Comparable比較方式和Comparator比較方式同時存在時,以Comparator的比較方式爲主;
注意:在重寫compareTo或者compare方法時,必需要明確比較的主要條件相等時要比較次要條件。(假設姓名和年齡一直的人爲相同的人,若是想要對人按照年齡的大小來排序,若是年齡相同的人,須要如何處理?不能直接return 0,由於可能姓名不一樣(年齡相同姓名不一樣的人是不一樣的人)。此時就須要進行次要條件判斷(須要判斷姓名),只有姓名和年齡同時相等的才能夠返回0.)
經過return 0來判斷惟一性。
問題:爲何使用TreeSet存入字符串,字符串默認輸出是按升序排列的?由於字符串實現了一個接口,叫作Comparable 接口.字符串重寫了該接口的compareTo 方法,因此String對象具有了比較性.那麼一樣道理,個人自定義元素(例如Person類,Book類)想要存入TreeSet集合,就須要實現該接口,也就是要讓自定義對象具有比較性.
存入TreeSet集合中的元素要具有比較性.
比較性要實現Comparable接口,重寫該接口的compareTo方法
TreeSet屬於Set集合,該集合的元素是不能重複的,TreeSet如何保證元素的惟一性
經過compareTo或者compare方法中的來保證元素的惟一性。
添加的元素必需要實現Comparable接口。當compareTo()函數返回值爲0時,說明兩個對象相等,此時該對象不會添加進來。
比較器接口
----| Comparable compareTo(Object o) 元素自身具有比較性 ----| Comparator compare( Object o1, Object o2 ) 給容器傳入比較器 |
TreeSet集合排序的兩種方式:
一,讓元素自身具有比較性。
也就是元素須要實現Comparable接口,覆蓋compareTo 方法。
這種方式也做爲元素的天然排序,也可稱爲默認排序。
年齡按照搜要條件,年齡相同再比姓名。
import java.util.TreeSet;
public class Demo4 { public static void main(String[] args) { TreeSet ts = new TreeSet(); ts.add(new Person("aa", 20, "男")); ts.add(new Person("bb", 18, "女")); ts.add(new Person("cc", 17, "男")); ts.add(new Person("dd", 17, "女")); ts.add(new Person("dd", 15, "女")); ts.add(new Person("dd", 15, "女"));
System.out.println(ts); System.out.println(ts.size()); // 5
} }
class Person implements Comparable { private String name; private int age; private String gender;
public Person() {
}
public Person(String name, int age, String gender) {
this.name = name; this.age = age; this.gender = gender; }
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 String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
@Override public int hashCode() { return name.hashCode() + age * 37; }
public boolean equals(Object obj) { System.err.println(this + "equals :" + obj); if (!(obj instanceof Person)) { return false; } Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age;
}
public String toString() { return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]"; }
@Override public int compareTo(Object obj) {
Person p = (Person) obj; System.out.println(this+" compareTo:"+p); if (this.age > p.age) { return 1; } if (this.age < p.age) { return -1; } return this.name.compareTo(p.name); }
}
|
二,讓容器自身具有比較性,自定義比較器。
需求:當元素自身不具有比較性,或者元素自身具有的比較性不是所需的。
那麼這時只能讓容器自身具有。
定義一個類實現Comparator 接口,覆蓋compare方法。
並將該接口的子類對象做爲參數傳遞給TreeSet集合的構造函數。
當Comparable比較方式,及Comparator比較方式同時存在,以Comparator
比較方式爲主。
import java.util.Comparator; import java.util.TreeSet;
public class Demo5 { public static void main(String[] args) { TreeSet ts = new TreeSet(new MyComparator()); ts.add(new Book("think in java", 100)); ts.add(new Book("java 核心技術", 75)); ts.add(new Book("現代操做系統", 50)); ts.add(new Book("java就業教程", 35)); ts.add(new Book("think in java", 100)); ts.add(new Book("ccc in java", 100));
System.out.println(ts); } }
class MyComparator implements Comparator {
public int compare(Object o1, Object o2) { Book b1 = (Book) o1; Book b2 = (Book) o2; System.out.println(b1+" comparator "+b2); if (b1.getPrice() > b2.getPrice()) { return 1; } if (b1.getPrice() < b2.getPrice()) { return -1; } return b1.getName().compareTo(b2.getName()); }
}
class Book { private String name; private double price;
public Book() {
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public Book(String name, double price) {
this.name = name; this.price = price; }
@Override public String toString() { return "Book [name=" + name + ", price=" + price + "]"; }
} |
TreeSet練習
將字符串中的數值進行排序。
例如String str="8 10 15 5 2 7"; 2,5,7,8,10,15
使用 TreeSet完成。
思路:1,將字符串切割。
2,能夠將這些對象存入TreeSet集合。
由於TreeSet自身具有排序功能。
public class Demo5 { public static void main(String[] args) { String str = "8 10 15 5 2 7"; String[] strs = str.split(" "); TreeSet ts = new TreeSet(); for (int x = 0; x < strs.length; x++) { int y = Integer.parseInt(strs[x]); ts.add(y); } System.out.println(ts); } }
|
會保存插入的順序。
看到array,就要想到角標。
看到link,就要想到first,last。
看到hash,就要想到hashCode,equals.
看到tree,就要想到兩個接口。Comparable,Comparator。
練習:去除數組中的重複元素.
若是程序中存儲了幾百萬個學生,並且常常須要使用學號來搜索某個學生,那麼這個需求有效的數據結構就是Map。Map是一種依照鍵(key)存儲元素的容器,鍵(key)很像下標,在List中下標是整數。在Map中鍵(key)可使任意類型的對象。Map中不能有重複的鍵(Key),每一個鍵(key)都有一個對應的值(value)。一個鍵(key)和它對應的值構成map集合中的一個元素。
Map中的元素是兩個對象,一個對象做爲鍵,一個對象做爲值。鍵不能夠重複,可是值能夠重複。
看頂層共性方法找子類特有對象.
Map與Collection在集合框架中屬並列存在
Map存儲的是鍵值對
Map存儲元素使用put方法,Collection使用add方法
Map集合沒有直接取出元素的方法,而是先轉成Set集合,在經過迭代獲取元素
Map集合中鍵要保證惟一性
也就是Collection是單列集合, Map 是雙列集合。
總結:
Map一次存一對元素, Collection 一次存一個。Map 的鍵不能重複,保證惟一。
Map 一次存入一對元素,是以鍵值對的形式存在.鍵與值存在映射關係.必定要保證鍵的惟一性.
查看api文檔:
interface Map<K,V>
K - 此映射所維護的鍵的類型
V - 映射值的類型
概念
將鍵映射到值的對象。一個映射不能包含重複的鍵;每一個鍵最多隻能映射到一個值。
特色
Key和Value是1對1的關係,如:門牌號 :家 老公:老婆
雙列集合
Map學習體系: ---| Map 接口 將鍵映射到值的對象。一個映射不能包含重複的鍵;每一個鍵最多隻能映射到一個值。 ---| HashMap 採用哈希表實現,因此無序 ---| TreeMap 能夠對健進行排序 |
---|Hashtable: 底層是哈希表數據結構,線程是同步的,不能夠存入null鍵,null值。 效率較低,被HashMap 替代。 ---|HashMap: 底層是哈希表數據結構,線程是不一樣步的,能夠存入null鍵,null值。 要保證鍵的惟一性,須要覆蓋hashCode方法,和equals方法。 ---| LinkedHashMap: 該子類基於哈希表又融入了鏈表。能夠Map集合進行增刪提升效率。 ---|TreeMap: 底層是二叉樹數據結構。能夠對map集合中的鍵進行排序。須要使用Comparable或者Comparator 進行比較排序。return 0,來判斷鍵的惟一性。 |
常見方法
一、添加: 一、V put(K key, V value) (能夠相同的key值,可是添加的value值會覆 蓋前面的,返回值是前一個,若是沒有就返回null) 二、putAll(Map<? extends K,? extends V> m) 從指定映射中將全部映射關 系複製到此映射中(可選操做)。 二、刪除 一、remove() 刪除關聯對象,指定key對象 二、clear() 清空集合對象 3、獲取 1:value get(key); 能夠用於判斷鍵是否存在的狀況。當指定的鍵不存在的時候,返 回的是null。
三、判斷: 一、boolean isEmpty() 長度爲0返回true不然false 二、boolean containsKey(Object key) 判斷集合中是否包含指定的key 三、boolean containsValue(Object value) 判斷集合中是否包含指定的value 4、長度: Int size()
|
添加:
該案例使用了HashMap,創建了學生姓名和年齡之間的映射關係。並試圖添加劇復的鍵。
import java.util.HashMap; import java.util.Map;
public class Demo1 { public static void main(String[] args) { // 定義一個Map的容器對象 Map<String, Integer > map1 = new HashMap<String, Integer >(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // 添加劇復的鍵值(值不一樣),會返回集合中原有(重複鍵)的值, System.out.println(map1.put("jack", 30)); //20
Map<String, Integer> map2 = new HashMap<String, Integer>(); map2.put("張三丰", 100); map2.put("虛竹", 20); System.out.println("map2:" + map2); // 從指定映射中將全部映射關係複製到此映射中。 map1.putAll(map2); System.out.println("map1:" + map1); // } }
|
刪除:
// 刪除: // remove() 刪除關聯對象,指定key對象 // clear() 清空集合對象
Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // 指定key,返回刪除的鍵值對映射的值。 System.out.println("value:" + map1.remove("java")); map1.clear(); System.out.println("map1:" + map1); |
獲取:
// 獲取: // V get(Object key) 經過指定的key對象獲取value對象 // int size() 獲取容器的大小 Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); // V get(Object key) 經過指定的key對象獲取value對象 // int size() 獲取容器的大小 System.out.println("value:" + map1.get("jack")); System.out.println("map.size:" + map1.size()); |
判斷:
// 判斷: // boolean isEmpty() 長度爲0返回true不然false // boolean containsKey(Object key) 判斷集合中是否包含指定的key // boolean containsValue(Object value)
Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("jack", 20); map1.put("rose", 18); map1.put("lucy", 17); map1.put("java", 25); System.out.println(map1); System.out.println("isEmpty:" + map1.isEmpty()); System.out.println("containskey:" + map1.containsKey("jack")); System.out.println("containsvalues:" + map1.containsValue(100)); |
遍歷Map的方式:
1、將map 集合中全部的鍵取出存入set集合。 Set<K> keySet() 返回全部的key對象的Set集合 再經過get方法獲取鍵對應的值。 2、 values() ,獲取全部的值. Collection<V> values()不能獲取到key對象 3、 Map.Entry對象 推薦使用 重點 Set<Map.Entry<k,v>> entrySet() 將map 集合中的鍵值映射關係打包成一個對象 Map.Entry對象經過Map.Entry 對象的getKey, getValue獲取其鍵和值。 |
|
第一種方式:使用keySet
將Map轉成Set集合(keySet()),經過Set的迭代器取出Set集合中的每個元素(Iterator)就是Map集合中的全部的鍵,再經過get方法獲取鍵對應的值。
import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set;
public class Demo2 { public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map);
// // 獲取方法: // 第一種方式: 使用keySet // 須要分別獲取key和value,沒有面向對象的思想 // Set<K> keySet() 返回全部的key對象的Set集合
Set<Integer> ks = map.keySet(); Iterator<Integer> it = ks.iterator(); while (it.hasNext()) { Integer key = it.next(); String value = map.get(key); System.out.println("key=" + key + " value=" + value); } } }
|
第二種方式: 經過values 獲取全部值,不能獲取到key對象
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map); // 第二種方式: // 經過values 獲取全部值,不能獲取到key對象 // Collection<V> values()
Collection<String> vs = map.values(); Iterator<String> it = vs.iterator(); while (it.hasNext()) { String value = it.next(); System.out.println(" value=" + value); } } |
第三種方式: Map.Entry
public static interface Map.Entry<K,V>
經過Map中的entrySet()方法獲取存放Map.Entry<K,V>對象的Set集合。
Set<Map.Entry<K,V>> entrySet()
面向對象的思想將map集合中的鍵和值映射關係打包爲一個對象,就是Map.Entry
,將該對象存入Set集合,Map.Entry是一個對象,那麼該對象具有的getKey,getValue得到鍵和值。
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "aaaa"); map.put(2, "bbbb"); map.put(3, "cccc"); System.out.println(map); // 第三種方式: Map.Entry對象 推薦使用 重點 // Set<Map.Entry<K,V>> entrySet()
// 返回的Map.Entry對象的Set集合 Map.Entry包含了key和value對象 Set<Map.Entry<Integer, String>> es = map.entrySet();
Iterator<Map.Entry<Integer, String>> it = es.iterator();
while (it.hasNext()) {
// 返回的是封裝了key和value對象的Map.Entry對象 Map.Entry<Integer, String> en = it.next();
// 獲取Map.Entry對象中封裝的key和value對象 Integer key = en.getKey(); String value = en.getValue();
System.out.println("key=" + key + " value=" + value); } } |
底層是哈希表數據結構,線程是不一樣步的,能夠存入null鍵,null值。要保證鍵的惟一性,須要覆蓋hashCode方法,和equals方法。
案例:自定義對象做爲Map的鍵。
package cn.itcast.gz.map;
import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set;
public class Demo3 { public static void main(String[] args) { HashMap<Person, String> hm = new HashMap<Person, String>(); hm.put(new Person("jack", 20), "1001"); hm.put(new Person("rose", 18), "1002"); hm.put(new Person("lucy", 19), "1003"); hm.put(new Person("hmm", 17), "1004"); hm.put(new Person("ll", 25), "1005"); System.out.println(hm); System.out.println(hm.put(new Person("rose", 18), "1006"));
Set<Entry<Person, String>> entrySet = hm.entrySet(); Iterator<Entry<Person, String>> it = entrySet.iterator(); while (it.hasNext()) { Entry<Person, String> next = it.next(); Person key = next.getKey(); String value = next.getValue(); System.out.println(key + " = " + value); } } }
class Person { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() {
return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
} }
|
TreeMap的排序,TreeMap能夠對集合中的鍵進行排序。如何實現鍵的排序?
方式一:元素自身具有比較性
和TreeSet同樣原理,須要讓存儲在鍵位置的對象實現Comparable接口,重寫compareTo方法,也就是讓元素自身具有比較性,這種方式叫作元素的天然排序也叫作默認排序。
方式二:容器具有比較性
當元素自身不具有比較性,或者自身具有的比較性不是所須要的。那麼此時可讓容器自身具有。須要定義一個類實現接口Comparator,重寫compare方法,並將該接口的子類實例對象做爲參數傳遞給TreeMap集合的構造方法。
注意:當Comparable比較方式和Comparator比較方式同時存在時,以Comparator的比較方式爲主;
注意:在重寫compareTo或者compare方法時,必需要明確比較的主要條件相等時要比較次要條件。(假設姓名和年齡一直的人爲相同的人,若是想要對人按照年齡的大小來排序,若是年齡相同的人,須要如何處理?不能直接return 0,覺得可能姓名不一樣(年齡相同姓名不一樣的人是不一樣的人)。此時就須要進行次要條件判斷(須要判斷姓名),只有姓名和年齡同時相等的才能夠返回0.)
經過return 0來判斷惟一性。
import java.util.TreeMap;
public class Demo4 { public static void main(String[] args) { TreeMap<String, Integer> tree = new TreeMap<String, Integer>(); tree.put("張三", 19); tree.put("李四", 20); tree.put("王五", 21); tree.put("趙六", 22); tree.put("周七", 23); tree.put("張三", 24); System.out.println(tree); System.out.println("張三".compareTo("李四"));//-2094 } } |
自定義元素排序
package cn.itcast.gz.map;
import java.util.Comparator; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap;
public class Demo3 { public static void main(String[] args) { TreeMap<Person, String> hm = new TreeMap<Person, String>( new MyComparator()); hm.put(new Person("jack", 20), "1001"); hm.put(new Person("rose", 18), "1002"); hm.put(new Person("lucy", 19), "1003"); hm.put(new Person("hmm", 17), "1004"); hm.put(new Person("ll", 25), "1005"); System.out.println(hm); System.out.println(hm.put(new Person("rose", 18), "1006"));
Set<Entry<Person, String>> entrySet = hm.entrySet(); Iterator<Entry<Person, String>> it = entrySet.iterator(); while (it.hasNext()) { Entry<Person, String> next = it.next(); Person key = next.getKey(); String value = next.getValue(); System.out.println(key + " = " + value); } } }
class MyComparator implements Comparator<Person> {
@Override public int compare(Person p1, Person p2) { if (p1.getAge() > p2.getAge()) { return -1; } else if (p1.getAge() < p2.getAge()) { return 1; } return p1.getName().compareTo(p2.getName()); }
}
class Person implements Comparable<Person> { private String name; private int age;
Person() {
}
public Person(String name, int age) {
this.name = name; this.age = age; }
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; }
@Override public int hashCode() {
return this.name.hashCode() + age * 37; }
@Override public boolean equals(Object obj) { if (obj instanceof Person) { Person p = (Person) obj; return this.name.equals(p.name) && this.age == p.age; } else { return false; } }
@Override public String toString() {
return "Person@name:" + this.name + " age:" + this.age; }
@Override public int compareTo(Person p) {
if (this.age > p.age) { return 1; } else if (this.age < p.age) { return -1; } return this.name.compareTo(p.name); }
} |
注意:Set的元素不可重複,Map的鍵不可重複,若是存入重複元素如何處理
Set元素重複元素不能存入add方法返回false
Map的重複健將覆蓋舊鍵,將舊值返回。
集合框架中的工具類:特色:該工具類中的方法都是靜態的。
Collections:常見方法: 1, 對list進行二分查找: 前提該集合必定要有序。 int binarySearch(list,key); //必須根據元素天然順序對列表進行升級排序 //要求list 集合中的元素都是Comparable 的子類。 int binarySearch(list,key,Comparator); 2,對list集合進行排序。 sort(list); //對list進行排序,其實使用的事list容器中的對象的compareTo方法 sort(list,comaprator); //按照指定比較器進行排序 3,對集合取最大值或者最小值。 max(Collection) max(Collection,comparator) min(Collection) min(Collection,comparator) reverse(list); 5,對比較方式進行強行逆轉。 Comparator reverseOrder(); Comparator reverseOrder(Comparator); 6,對list集合中的元素進行位置的置換。 swap(list,x,y); 7,對list集合進行元素的替換。若是被替換的元素不存在,那麼原集合不變。 replaceAll(list,old,new); 8,能夠將不一樣步的集合變成同步的集合。 Set synchronizedSet(Set<T> s) Map synchronizedMap(Map<K,V> m) List synchronizedList(List<T> list) 9. 若是想要將集合變數組: 可使用Collection 中的toArray 方法。注意:是Collection不是Collections工具類 傳入指定的類型數組便可,該數組的長度最好爲集合的size。 |
Arrays:用於對數組操做的工具類
1,二分查找,數組須要有序 binarySearch(int[]) binarySearch(double[])
2,數組排序 sort(int[]) sort(char[])…… 2, 將數組變成字符串。 toString(int[]) 3, 複製數組。 4, 複製部分數組。 copyOfRange(): 5, 比較兩個數組是否相同。 equals(int[],int[]); 6, 將數組變成集合。 List asList(T[]); 這樣能夠經過集合的操做來操做數組中元素, 可是不可使用增刪方法,add,remove。由於數組長度是固定的,會出現 UnsupportOperationExcetion。 可使用的方法:contains,indexOf。。。 若是數組中存入的基本數據類型,那麼asList會將數組實體做爲集合中的元素。 若是數組中的存入的引用數據類型,那麼asList會將數組中的元素做爲集合中 的元素。 |
import java.util.ArrayList; import java.util.Collections; import java.util.Arrays; import java.util.List; class Demo1 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(4); list.add(3); list.add(1); list.add(2); list.add(3); // 排序 Collections.sort(list); // 折半查找的前提是排序好的元素 System.out.println( Collections.binarySearch( list , 8 ) ); // 找不到返回-插入點-1 // 反序集合輸出 Collections.reverse( list ); System.out.println( list ); // 求最值 System.out.println( Collections.max( list ) ); // 4 // fill() 使用指定的元素替換指定集合中的全部元素 // Collections.fill( list, 5 ); System.out.println( list );
// 將數組轉換爲集合 Integer is[] = new Integer[]{6,7,8}; List<Integer> list2 = Arrays.asList(is); list.addAll( list2 ); System.out.println( list );
// 將List轉換爲數組 Object [] ins = list.toArray(); System.out.println( Arrays.toString( ins ) );
} } |
集合的練習
問題: 定義一個Person數組,將Person數組中的重複對象剔除?
思路:
1. 描述一個Person類
2. 將數組轉換爲Arrays.asList() List
3. Set addAll( list )
4. hashCode()且equals()
import java.util.Arrays; import java.util.Set; import java.util.List; import java.util.HashSet;
// 1. 描述Person類 class Person { public String name; public int age;
public Person() { }
public Person(String name, int age) { this.name = name; this.age = age; }
public String toString() {
return getClass().getName() + " : name=" + this.name + " age=" + this.age;
}
// 4. 重寫hashCode和equals() public int hashCode() {
return this.age; }
public boolean equals(Object o) { Person p = null; if (o instanceof Person) p = (Person) o; return this.name.equals(p.name) && (this.age == p.age); } }
class Demo2 { public static void main(String[] args) { Person[] ps = new Person[] { new Person("jack", 34), new Person("lucy", 20), new Person("lili", 10), new Person("jack", 34) }; // 遍歷數組 System.out.println(Arrays.toString(ps)); // 2. 將自定義對象數組轉換爲List集合 List<Person> list = Arrays.asList(ps); // 3. 將List轉換爲Set Set<Person> set = new HashSet<Person>(); set.addAll(list); System.out.println(set);
} } |
當集合中存儲的對象類型不一樣時,那麼會致使程序在運行的時候的轉型異常
import java.util.ArrayList; import java.util.Iterator;
public class Demo5 { public static void main(String[] args) { ArrayList arr = new ArrayList(); arr.add(new Tiger("華南虎")); arr.add(new Tiger("東北虎")); arr.add(new Sheep("喜羊羊")); System.out.println(arr); Iterator it = arr.iterator(); while (it.hasNext()) { Object next = it.next(); Tiger t = (Tiger) next; t.eat(); }
} } class Tiger { String name;
public Tiger() {
}
public Tiger(String name) { this.name = name; }
@Override public String toString() {
return "Tiger@name:" + this.name; }
public void eat() { System.out.println(this.name + "吃羊"); } }
class Sheep { String name;
public Sheep() {
}
public Sheep(String name) { this.name = name; }
@Override public String toString() { return "Sheep@name:" + this.name; }
public void eat() { System.out.println(this.name + "吃青草"); } } |
緣由 :發現雖然集合能夠存儲任意對象,可是若是須要使用對象的特有方法,那麼就須要類型轉換,若是集合中存入的對象不一樣,可能引起類型轉換異常.
[Tiger@name:華南虎, Tiger@name:東北虎, Sheep@name:喜羊羊] 華南虎吃羊 東北虎吃羊 Exception in thread "main" java.lang.ClassCastException: cn.itcast.gz.map.Sheep cannot be cast to cn.itcast.gz.map.Tiger at cn.itcast.gz.map.Demo5.main(Demo5.java:17) |
出現問題:
存入的是特定的對象,取出的時候是Object對象,須要強制類型轉換,可能誘發類型轉換異常.
沒法控制存入的是什麼類型的對象,取出對象的時候進行強轉時可能誘發異常.並且在編譯時期沒法發現問題.
雖然能夠再類型轉換的時候經過if語句進行類型檢查(instanceof),可是效率較低.(例如吃飯的時候,還須要判斷米飯裏有沒有沙子,吃飯效率低).能夠經過給容器加限定的形式規定容器只能存儲一種類型的對象.
就像給容器貼標籤說明該容器中只能存儲什麼樣類型的對象。
因此在jdk5.0後出現了泛型
泛型應用:
格式
public class Demo5 { public static void main(String[] args) { // 使用泛型後,規定該集合只能放羊,老虎就進不來了. ArrayList<Sheep> arr = new ArrayList<Sheep>(); arr.add(new Sheep("美羊羊")); arr.add(new Sheep("懶洋洋")); arr.add(new Sheep("喜羊羊")); // 編譯失敗 // arr.add(new Tiger("東北虎")); System.out.println(arr); Iterator<Sheep> it = arr.iterator(); while (it.hasNext()) { // 使用泛型後,不須要強制類型轉換了 Sheep next = it.next(); next.eat(); }
} } |
1. 將運行時的異常提早至編譯時發生。
2. 獲取元素的時候無需強轉類型,就避免了類型轉換的異常問題
格式 經過<> 來指定容器中元素的類型.
何時使用泛型:當類中操做的引用數據類型不肯定的時候,就可使用泛型類.
JDK5.0以前的Comparable
package java.lang; public interface Comparable {
public int compareTo(Object o); } |
JDK5.0以後的Comparable
package java.lang; public interface Comparable<T> {
public int compareTo(T o); } |
這裏的<T>表示泛型類型,隨後能夠傳入具體的類型來替換它.
細節一
聲明好泛型類型以後,集合中只能存放特定類型元素
public class Demo6 { public static void main(String[] args) { //建立一個存儲字符串的list ArrayList<String> arr=new ArrayList<String>(); arr.add("gz"); arr.add("itcast"); //存儲非字符串編譯報錯. arr.add(1); } } |
細節二:
泛型類型必須是引用類型
public class Demo6 { public static void main(String[] args) { // 泛型類型必須是引用類型,也就是說集合不能存儲基本數據類型 // ArrayList<int> arr2=new ArrayList<int>();
// 使用基本數據類型的包裝類 ArrayList<Integer> arr2 = new ArrayList<Integer>();
} }
|
細節三: 使用泛型後取出元素不須要類型轉換.
public class Demo6 { public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>(); arr.add("gzitcast"); arr.add("cditcast"); arr.add("bjitcast"); //使用泛型後取出元素不須要類型轉換. String str=arr.get(0); System.out.println(); } } |
需求:寫一個函數,調用者傳遞什麼類型的變量,該函數就返回什麼類型的變量?
實現一:
因爲沒法肯定具體傳遞什麼類型的數據.那麼方法的形參就定義爲Object類型.返回值也就是Object類型.可是使用該函數時須要強制類型轉換.
private Object getDate(Object obj) { return obj; } |
當不進行強制類型轉換可否寫出該功能.?
目前所學的知識沒法解決該問題
就須要使用泛型類解決
使用的泛型的自定義來解決以上問題。
泛型: 就是將類型看成變量處理。規範泛型的定義通常是一個大寫的任意字母。
1. 函數上的泛型定義
當函數中使用了一個不明確的數據類型,那麼在函數上就能夠進行泛型的定義。
public <泛型的聲明> 返回值類型 函數名( 泛型 變量名 ){
}
|
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 };
new Demo6().getData(5);
}
public <T> T getData(T data) { return data; } |
細節:
使用泛型方法前須要進行泛型聲明,使用一對尖括號 <泛型>,聲明的位置在static後返回值類型前。
當一個類中有多個函數聲明瞭泛型,那麼該泛型的聲明能夠聲明在類上。
格式
2. 類上的泛型聲明
修飾符 class 類名<泛型>{
} |
import java.util.Arrays;
public class Demo6<T> { public static void main(String[] args) { // 使用泛型類,建立對象的時候須要指定具體的類型 new Demo6<Integer>().getData(5); }
public T getData(T data) { return data; }
// 反序任意類型數組 public void reverse(T[] arr) { int start = 0; int end = arr.length - 1; for (int i = 0; i < arr.length; i++) { if (start < end) { T temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } }
}
|
在泛型類中定義一個靜態方法
public class Demo6<T> { public static void main(String[] args) { System.out.println(getData2(100)); }
public T getData(T data) { return data; }
//靜態方法 public static T getData2(T data) { return data; }
} |
注意:靜態方法不可使用類中定義的泛型
由於類中的泛型須要在對象初始化時指定具體的類型,而靜態優先於對象存在。那麼類中的靜態方法就須要單獨進行泛型聲明,聲明泛型必定要寫在static後,返回值類型以前
泛型類細節:
1、建立對象的時候要指定泛型的具體類型 2、建立對象時能夠不指定泛型的具體類型(和建立集合對象一眼)。默認是Object,例如咱們使用集合存儲元素的時候沒有使用泛型就是那麼參數的類型就是Object 3、類上面聲明的泛型只能應用於非靜態成員函數,若是靜態函數須要使用泛型,那麼 須要在函數上獨立聲明。 4、若是創建對象後指定了泛型的具體類型,那麼該對象操做方法時,這些方法只能操做一種數據類型。 5、因此既能夠在類上的泛型聲明,也能夠在同時在該類的方法中聲明泛型。 |
泛型練習:
定義泛型成員
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} |
若是Father類有子類,子類該如何實現
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} //子類指定了具體的類型 class Son extends Father<String>{
} //子類也須要使用泛型 class Son3<T> extends Father<T>{
} //錯誤寫法,父類上定義有泛型須要進行處理 class Son2 extends Father<T>{
} |
public class Demo8 { public static void main(String[] args) { MyInter<String> my = new MyInter<String>(); my.print("泛型");
MyInter2 my2 = new MyInter2(); my.print("只能傳字符串"); } }
interface Inter<T> { void print(T t); }
// 實現不知爲什麼類型時能夠這樣定義 class MyInter<T> implements Inter<T> { public void print(T t) { System.out.println("myprint:" + t); } } //使用接口時明確具體類型。 class MyInter2 implements Inter<String> {
@Override public void print(String t) { System.out.println("myprint:" + t);
}
}
|
需求:
定義一個方法,接收一個集合對象(該集合有泛型),並打印出集合中的全部元素。
例如集合對象以下格式:
Collection<Person> coll = new ArrayList<Person>(); coll.add(new Person("jack", 20)); coll.add(new Person("rose", 18)); Collection<Object> coll2 = new ArrayList<Object>(); coll2.add(new Object()); coll2.add(new Object()); coll2.add(new Object());
Collection<String> coll3 = new ArrayList<String>(); coll3.add("abc"); coll3.add("ddd"); coll3.add("eee"); |
分析,集合對象中的元素的類型是變化的,方法的形參的那麼泛型類型就只能定義爲Object類型.
import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator;
public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr);
//將集合的泛型設置類String類型,是Object子類 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); //因爲print方法接收的集合進行了元素限定,只接受限定爲Object類型的集合,編譯不經過 //print(hs); }
public static void print(Collection<Object> coll) { Iterator<Object> it = coll.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } }
|
可是,因爲print方法接收的集合進行了元素限定,只接受限定爲Object類型的集合,編譯不經過該問題如何解決?
能夠把方法的形參的泛型去掉,那麼方法中就把集合中的元素當作Object類型處理.
也可使用使用泛型通配符
public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr);
// 將集合的泛型設置類String類型,是Object子類 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); // 使用泛型通配符,編譯經過。 print(hs); }
public static void print(Collection<?> coll) { Iterator<?> it = coll.iterator(); while (it.hasNext()) {
Object next = it.next(); System.out.println(next); } } }
|
上述就使用了泛型通配符
通配符:? public void show(List<?> list) { } 能夠對類型進行限定範圍。 ?extends E: 接收E類型或者E的子類型。 ? super E: 接收E類型或者E的父類型。 |
限定泛型通配符的邊界
限定通配符的上邊界:
extends
接收Number 類型或者Number的子類型
正確:Vector<? extends Number> x = new Vector<Integer>(); 錯誤:Vector<? extends Number> x = new Vector<String>();
|
限定通配符的下邊界
super
接收Integer 或者Integer的父類型
正確:Vector<? super Integer> x = new Vector<Number>(); 錯誤:Vector<? super Integer> x = new Vector<Byte>(); |
總結:
JDK5中的泛型容許程序員在編寫集合代碼時,就限制集合的處理類型,從而把原來程序運行時可能發生問題,轉變爲編譯時的問題,以此提升程序的可讀性和穩定
注意:泛型是提供給javac編譯器使用的,它用於限定集合的輸入類型,讓編譯器在源代碼級別上,即擋住向集合中插入非法數據。但編譯器編譯完帶有泛形的java程序後,生成的class文件中將再也不帶有泛形信息,以此使程序運行效率不受到影響,這個過程稱之爲「擦除」。
泛型的基本術語,以ArrayList<E>爲例:<>念着typeof
ArrayList<E>中的E稱爲類型參數變量
ArrayList<Integer>中的Integer稱爲實際類型參數
整個稱爲ArrayList<E>泛型類型
整個ArrayList<Integer>稱爲參數化的類型ParameterizedType
最後:
關於數據結構能夠查看以下網站:
http://www.cs.armstrong.edu/liang/animation/index.html 數據結構