集合概述
-
概念:物件的容器,定義了對多個物件進項操作的的常用方法。可實現陣列的功能。
-
和陣列的區別:
-
陣列長度固定,集合長度不固定。
-
陣列可以儲存基本型別和引用型別,集合只能儲存引用型別。
- 位置: java.util.*;
Collection體系集合
Collection父介面
-
特點:代表一組任意型別的物件,無序、無下標、不能重複。
-
方法:
boolean add(Object obj) //新增一個物件。
boolean addAll(Collection c) //講一個集合中的所有物件新增到此集合中。
void clear() //清空此集合中的所有物件。
boolean contains(Object o) //檢查此集合中是否包含o物件。
boolean equals(Object o) //比較此集合是否與指定物件相等。
boolean isEmpty() //判斷此集合是否為空。
boolean remove(Object o) //在此集合中移除o物件。
int size() //返回此集合中的元素個數。
Object[] toArray() //姜此集合轉換成陣列。
1 /** 2 * Collection介面的使用(一) 3 * 1.新增元素 4 * 2.刪除元素 5 * 3.遍歷元素 6 * 4.判斷 7 */ 8 public class Demo01 { 9 public static void main(String[] args) { 10 //建立集合 11 Collection collection = new ArrayList(); 12 13 // * 1.新增元素 14 collection.add("西瓜"); 15 collection.add("蘋果"); 16 collection.add("榴蓮"); 17 System.out.println(collection.size()); 18 System.out.println(collection.toString()); 19 20 // * 2.刪除元素 21 collection.remove("西瓜"); 22 System.out.println("刪除之後:"+collection.size()); 23 24 // * 3.遍歷元素 25 //3.1 使用增強for 26 for (Object object:collection) { 27 String s = (String) object; 28 System.out.println(s); 29 } 30 //3.2 使用迭代器(迭代器專門用來遍歷集合的一種方式) 31 //hasnext();判斷是否有下一個元素 32 //next();獲取下一個元素 33 //remove();刪除當前元素 34 Iterator it = collection.iterator(); 35 while (it.hasNext()){ 36 String s = (String) it.next(); 37 System.out.println(s); 38 //刪除操作 39 //collection.remove(s);引發錯誤:併發修改異常 40 //iterator.remove();應使用迭代器的方法 41 42 // * 4.判斷 43 System.out.println(collection.contains("蘋果"));//true 是否存在蘋果 44 System.out.println(collection.isEmpty());//false 是否有資料,沒有資料為true,有資料為false 45 } 46 } 47 }
1 /** 2 * Collection介面的使用(二) 3 * 1.新增元素 4 * 2.刪除元素 5 * 3.遍歷元素 6 * 4.判斷 7 */ 8 public class Demo02 { 9 public static void main(String[] args) { 10 11 //建立Collection物件 12 Collection collection = new ArrayList(); 13 14 //建立Student物件 15 Student s1 = new Student("張三",18); 16 Student s2 = new Student("李四",19); 17 Student s3 = new Student("王五",20); 18 19 //1.新增資料 20 collection.add(s1); 21 collection.add(s2); 22 collection.add(s3); 23 //collection.add(s3);可重複新增相同物件 24 System.out.println("元素個數為:"+collection.size()); 25 System.out.println(collection.toString()); 26 27 //2.刪除資料 28 collection.remove(s1); 29 System.out.println("元素個數為:"+collection.size()); 30 System.out.println(collection.toString()); 31 32 //3.遍歷資料 33 //3.1 增強for迴圈 34 for (Object object:collection) { 35 Student s = (Student) object; 36 System.out.println(object); 37 } 38 39 //3.2 迭代資料 40 //迭代過程中不能使用Collection的刪除方法,即Collection.remove() 41 Iterator it = collection.iterator(); 42 while (it.hasNext()){ 43 Student s = (Student) it.next(); 44 System.out.println(s); 45 //刪除 46 //it.remove(); 47 } 48 49 //4判斷 50 System.out.println(collection.contains("張三"));//false 51 System.out.println(collection.contains(s2));//true 52 System.out.println(collection.isEmpty());//false 53 } 54 }
1 /** 2 * 學生類 3 */ 4 public class Student { 5 private String name; 6 private int age; 7 8 //有參構造 9 public Student(String name, int age) { 10 this.name = name; 11 this.age = age; 12 } 13 14 //無參構造 15 public Student() { 16 } 17 18 //重寫toString 19 @Override 20 public String toString() { 21 return "Student{" + 22 "name='" + name + '\'' + 23 ", age=" + age + 24 '}'; 25 } 26 27 //get/set靜態屬性 28 public String getName() { 29 return name; 30 } 31 32 public void setName(String name) { 33 this.name = name; 34 } 35 36 public int getAge() { 37 return age; 38 } 39 40 public void setAge(int age) { 41 this.age = age; 42 } 43 }
Collection子介面
List集合
-
特點:有序、有下標、元素可以重複。
-
方法:
void add(int index,Object o) //在index位置插入物件o。
boolean addAll(index,Collection c) //將一個集合中的元素新增到此集合中的index位置。
Object get(int index) //返回集合中指定位置的元素。
List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之間的集合元素。
1 /** 2 * List子介面的使用(一) 3 * 特點:1.有序有下標 2.可以重複 4 * 5 * 1.新增元素 6 * 2.刪除元素 7 * 3.遍歷元素 8 * 4.判斷 9 * 5.獲取位置 10 */ 11 public class Demo03 { 12 public static void main(String[] args) { 13 14 //1.建立list集合物件 15 List list = new ArrayList<>(); 16 17 //2.新增元素 18 list.add("迪迦"); 19 list.add("艾斯"); 20 list.add(0,"賽文");//根據index下標插入資料 21 System.out.println("元素個數為:"+list.size()); 22 System.out.println(list.toString()); 23 24 //3.刪除資料 25 list.remove(1); 26 //list.remove("迪迦");意思同上,一個是根據角標,一個是根據內容 27 System.out.println("刪除之後:"+list.size()); 28 System.out.println(list.toString()); 29 30 //4.遍歷元素 31 //4.1 因為有下標,所以可以使用for迴圈 32 for (int i = 0; i < list.size(); i++) { 33 System.out.println(list.get(i)); 34 } 35 //4.2 使用增強for迴圈 36 for (Object object:list) { 37 String s = (String) object; 38 System.out.println(s); 39 } 40 //4.3 使用迭代器 41 Iterator it = list.iterator(); 42 while (it.hasNext()){ 43 String s = (String) it.next(); 44 System.out.println(s); 45 } 46 //4.4 使用列表迭代器 47 ListIterator listIterator = list.listIterator(); 48 //從前往後遍歷 49 while (listIterator.hasNext()){ 50 String s = (String) listIterator.next(); 51 System.out.println(s); 52 } 53 //從後往前遍歷(此時“遍歷指標”已經指向末尾了) 54 while (listIterator.hasPrevious()){ 55 String s = (String) listIterator.previous(); 56 System.out.println(s); 57 } 58 59 //5.判斷 60 System.out.println(list.contains("賽文"));//true 61 System.out.println(list.isEmpty());//false 62 63 //6.獲取位置 64 System.out.println(list.indexOf("艾斯"));//1 65 } 66 }
1 /** 2 * List子介面的使用(二) 3 * 1.新增元素 4 * 2.刪除元素 5 * 3.遍歷元素 6 * 4.判斷 7 * 5.獲取位置 8 */ 9 public class Demo04 { 10 public static void main(String[] args) { 11 List list = new ArrayList<>(); 12 //1.新增基本型別數字資料(自動裝箱) 13 list.add(10); 14 list.add(20); 15 list.add(30); 16 list.add(40); 17 list.add(40); 18 System.out.println("元素個數為:"+list.size()); 19 System.out.println(list.toString()); 20 21 //2.刪除資料 22 list.remove(0);//根據下標刪除 23 //list.remove(40);下標越界 24 list.remove(new Integer(40));//根據內容刪除,將基本型別轉成引用型別 25 System.out.println("刪除後:"+list.size()); 26 System.out.println(list.toString()); 27 28 //3.遍歷元素 29 //3.1 使用for迴圈 30 for (int i = 0; i < list.size(); i++) { 31 System.out.println(list.get(i)); 32 } 33 //3.2 使用增強for迴圈 34 for (Object object:list) { 35 int s = (int) object; 36 System.out.println(s); 37 } 38 //3.3 使用迭代器 39 Iterator it = list.iterator(); 40 while (it.hasNext()){ 41 int s = (int) it.next(); 42 System.out.println(s); 43 } 44 //3.4 使用列表迭代器 45 ListIterator listIterator = list.listIterator(); 46 //從前往後遍歷 47 while (listIterator.hasNext()){ 48 int s = (int) listIterator.next(); 49 System.out.println(s); 50 } 51 //從後往前遍歷(此時“遍歷指標”已經指向末尾了) 52 while (listIterator.hasPrevious()){ 53 int s = (int) listIterator.previous(); 54 System.out.println(s); 55 } 56 57 //4.判斷 58 System.out.println(list.contains(20));//true 59 System.out.println(list.isEmpty());//false 60 61 //5.獲取位置 62 System.out.println(list.indexOf(30));//1 63 64 //6.補充方法sublist,根據角標範圍,返回集合元素,含頭不含尾 65 List list1 = list.subList(0,2); 66 System.out.println(list1.toString());//20,30 67 } 68 }
List實現類
ArrayList【重點】
- 陣列結構實現,查詢快、增刪慢;
- JDK1.2版本,執行效率快、執行緒不安全。
1 /** 2 * ArrayList的使用 3 * 儲存結構:陣列; 4 * 特點:查詢遍歷速度快,增刪慢。 5 * 1.新增元素 6 * 2.刪除元素 7 * 3.遍歷元素 8 * 4.判斷 9 * 5.查詢 10 */ 11 public class Demo05 { 12 public static void main(String[] args) { 13 ArrayList arrayList = new ArrayList<>(); 14 //1.新增元素 15 Student s1 = new Student("鋼鐵俠",3); 16 Student s2 = new Student("蜘蛛俠",4); 17 Student s3 = new Student("蝙蝠俠",5); 18 arrayList.add(s1); 19 arrayList.add(s2); 20 arrayList.add(s3); 21 arrayList.add(s3); 22 System.out.println("元素個數:"+arrayList.size()); 23 System.out.println(arrayList.toString()); 24 25 //2.刪除元素 26 arrayList.remove(s3); 27 System.out.println("刪除後:"+arrayList.size()); 28 System.out.println(arrayList.toString()); 29 //arrayList.remove(new Student("唐", 21)); 30 //注:這樣可以刪除嗎(不可以)?顯然這是兩個不同的物件。 31 //假如兩個物件屬性相同便認為其是同一物件,那麼如何修改程式碼? 32 33 //3.遍歷元素 34 //3.1使用迭代器 35 Iterator it = arrayList.iterator(); 36 while (it.hasNext()){ 37 Student s = (Student) it.next(); 38 System.out.println(s); 39 } 40 //3.2使用列表迭代器 41 ListIterator listIterator = arrayList.listIterator(); 42 //從前往後遍歷 43 while (listIterator.hasNext()){ 44 Student s = (Student) listIterator.next(); 45 System.out.println(s); 46 } 47 //從後往前遍歷 48 while (listIterator.hasPrevious()){ 49 Student s = (Student) listIterator.previous(); 50 System.out.println(s); 51 } 52 53 //4.判斷 54 System.out.println(arrayList.isEmpty());//false 55 //System.out.println(arrayList.contains(new Student("鋼鐵俠", 3))); 56 //注:與上文相同的問題。 57 58 //5.查詢 59 System.out.println(arrayList.indexOf(s1)); 60 } 61 }
注:Object裡的equals(this==obj)用地址和當前物件比較,如果想實現程式碼中的問題,可以在學生類中重寫equals方法:
1 //重寫equls方法 2 @Override 3 public boolean equals(Object obj) { 4 //1.是否為同一物件 5 if (this == obj){ 6 return true; 7 } 8 //2.判斷是否為空 9 if (obj == null){ 10 return false; 11 } 12 //3.判斷是否是Student型別 13 if (this instanceof Student){ 14 Student s = (Student) obj; 15 //4.比較屬性 16 if (this.name.equals(s.getName())&&this.age==s.age){ 17 return true; 18 } 19 } 20 return false; 21 }
ArrayList原始碼分析:
-
預設容量大小:
private static final int DEFAULT_CAPACITY = 10;
-
存放元素的陣列:
transient Object[] elementData;
-
實際元素個數:
private int size;
-
建立物件時呼叫的無參建構函式:
1 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 2 //Arraylist無參構造方法 3 public ArrayList() { 4 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 5 }
這段原始碼說明當你沒有向集合中新增任何元素時,集合容量為0。那麼預設的10個容量怎麼來的呢?這就得看看add方法的原始碼了:
1 public boolean add(E e) { 2 ensureCapacityInternal(size + 1); // Increments modCount!! 3 elementData[size++] = e; 4 return true; 5 }
假設你new了一個陣列,當前容量為0,size當然也為0。這時呼叫add方法進入到ensureCapacityInternal(size + 1);
該方法原始碼如下:
1 private void ensureCapacityInternal(int minCapacity) { 2 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 3 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 4 } 5 6 ensureExplicitCapacity(minCapacity); 7 }
由於一開始Arraylist的無參構造方法,就是將 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 賦值給了 elementData ,所以if的條件是成立的。
if語句中 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 就是在 DEFAULT_CAPACITY 和 minCapacity 中返回一個最大的引數, DEFAULT_CAPACITY 是Arrarylist中一開始就定義的靜態常量,大小為10。 minCapacity 傳入的值為size+1也就是 1
DEFAULT_CAPACITY值大,因此返回DEFAULT_CAPACITY的值為10,
這個值作為引數又傳入ensureExplicitCapacity()
方法中,進入該方法檢視原始碼:
1 private void ensureExplicitCapacity(int minCapacity) { 2 modCount++; 3 4 // overflow-conscious code 5 if (minCapacity - elementData.length > 0) 6 grow(minCapacity); 7 }
我們先不要管modCount這個變數,可以理解為這個變數是記錄陣列修改次數。
因為elementData陣列長度為0,minCapacity為10,所以if條件成立,呼叫grow方法,重要的部分來了,這個程式碼就是陣列擴容的程式碼,現在我們進入到grow方法的原始碼中:
1 private void grow(int minCapacity) { 2 // overflow-conscious code 3 int oldCapacity = elementData.length; //oldCapactity=0 4 int newCapacity = oldCapacity + (oldCapacity >> 1); //oldCapacity=0;(oldCapacity >> 1)0右移一位還是0;因此newCapacity=0 5 if (newCapacity - minCapacity < 0) //0-10=-10,if條件成立 6 newCapacity = minCapacity; //newCapacity=10 7 if (newCapacity - MAX_ARRAY_SIZE > 0) //MAX_ARRAY_SIZE這個值大家也可以進去看下,是好幾個億,這個條件基本上永遠不會成立。可以忽略不看,瞭解即可 8 newCapacity = hugeCapacity(minCapacity); 9 // minCapacity is usually close to size, so this is a win: 10 elementData = Arrays.copyOf(elementData, newCapacity); //copyOf(elementData,newCapacity)建立一個新的陣列elementDate,陣列長度為newCapacity即就是10 11 }
這個方法先宣告瞭一個oldCapacity變數將陣列長度賦給它,其值為0;又宣告瞭一個newCapacity變數其值為oldCapacity+一個增量
,可以發現這個增量是和原陣列長度有關的量,當然在這裡也為0。第一個if條件滿足,newCapacity的值為10(這就是預設的容量,不理解的話再看看前面)。第二個if條件不成立,也可以不用注意,因為MAX_ARRAY_SIZE的定義如下:
這個值太大了以至於第二個if條件沒有了解的必要。
最後一句話就是為elementData陣列賦予了新的長度,Arrays.copyOf()
方法返回的陣列是新的陣列物件,原陣列物件不會改變,該拷貝不會影響原來的陣列。copyOf()
的第二個自變數指定要建立的新陣列長度,如果新陣列的長度超過原陣列的長度,則保留陣列預設值。
這時候再回到add的方法中,接著就向下執行elementData[size++] = e,將add傳入的值e賦值給陣列elementData,下標index為[size++]。
到這裡為止關於ArrayList就講解得差不多了,當陣列長度為10的時候你們可以試著過一下原始碼,查一下每次的增量是多少(答案是每次擴容為原來的1.5倍)。
Vector
-
陣列結構實現,查詢快、增刪慢;
-
JDK1.0版本,執行效率慢、執行緒安全。
1 /** 2 * Vector的演示使用 3 * 4 *1.新增資料 5 *2.刪除資料 6 *3.遍歷 7 *4.判斷 8 */ 9 public class Demo01 { 10 public static void main(String[] args) { 11 //1.建立Vector物件 12 Vector v = new Vector<>(); 13 14 //2.新增資料 15 v.add("孫悟空"); 16 v.add("豬八戒"); 17 v.add("沙和尚"); 18 System.out.println("元素個數為:"+v.size()); 19 System.out.println(v.toString()); 20 21 //3.刪除資料 22 v.remove("沙和尚"); 23 //v.remove(2);效果同上 24 System.out.println("刪除後:"+v.size()); 25 System.out.println(v.toString()); 26 27 //4.遍歷 28 //使用列舉 29 Enumeration elements = v.elements(); 30 while (elements.hasMoreElements()){ 31 System.out.println(elements.nextElement()); 32 } 33 34 //5.判斷 35 System.out.println(v.isEmpty()); 36 System.out.println(v.contains("孫悟空")); 37 } 38 }
LinkedList
- 連結串列結構實現,增刪快,查詢慢。
1 /** 2 * LinkedList的用法 3 * 儲存結構:雙向連結串列 4 * 1.建立物件 5 * 2.新增元素 6 * 3.刪除元素 7 * 4.遍歷 8 * 5.判斷 9 */ 10 public class Demo01 { 11 public static void main(String[] args) { 12 //1.建立LinkedList物件 13 LinkedList link = new LinkedList<>(); 14 15 //2.新增元素 16 Student s1 = new Student("python",1); 17 Student s2 = new Student("Java",2); 18 Student s3 = new Student("go",3); 19 link.add(s1); 20 link.add(s2); 21 link.add(s3); 22 link.add(s3); 23 System.out.println("元素個數為:"+link.size()); 24 System.out.println(link.toString()); 25 26 //3.刪除元素 27 link.remove(s3); 28 link.remove(new Student("go",3)); 29 System.out.println("刪除後:"+link.size()); 30 System.out.println(link.toString()); 31 32 //4.遍歷 33 //4.1 使用for迴圈遍歷 34 for (int i = 0; i < link.size(); i++) { 35 System.out.println(link.get(i)); 36 } 37 //4.2 使用增強for 38 for (Object object:link) { 39 Student s = (Student) object; 40 System.out.println(s.toString()); 41 } 42 //4.3 使用迭代器 43 Iterator it = link.iterator(); 44 while (it.hasNext()){ 45 System.out.println(it.next()); 46 } 47 //4.4 使用列表迭代器 48 ListIterator listIterator = link.listIterator(); 49 //從前往後遍歷 50 while (listIterator.hasNext()){ 51 System.out.println(listIterator.next()); 52 } 53 //從後往前遍歷 54 while (listIterator.hasPrevious()){ 55 System.out.println(listIterator.previous()); 56 } 57 58 //5.判斷 59 System.out.println(link.isEmpty()); 60 System.out.println(link.contains(s1)); 61 System.out.println(link.contains(new Student("Java",2))); 62 } 63 }
LinkedList原始碼分析
LinkedList首先有三個屬性:
- 連結串列大小:
transient int size = 0;
- (指向)第一個結點/頭結點:
transient Node<E> first;
- (指向)最後一個結點/尾結點:
transient Node<E> last;
關於Node型別我們再進入到類裡看看:
1 private static class Node<E> { 2 E item; 3 Node<E> next; 4 Node<E> prev; 5 6 Node(Node<E> prev, E element, Node<E> next) { 7 this.item = element; 8 this.next = next; 9 this.prev = prev; 10 } 11 }
首先item存放的是實際資料;next指向下一個結點而prev指向上一個結點。
Node帶參構造方法的三個引數分別是前一個結點、儲存的資料、後一個結點,呼叫這個構造方法時將它們賦值給當前物件。
LinkedList是如何新增元素的呢?先看看add方法:
1 public boolean add(E e) { 2 linkLast(e); 3 return true; 4 }
進入到linkLast方法:
1 void linkLast(E e) { 2 final Node<E> l = last; 3 final Node<E> newNode = new Node<>(l, e, null); 4 last = newNode; 5 if (l == null) 6 first = newNode; 7 else 8 l.next = newNode; 9 size++; 10 modCount++; 11 }
假設剛開始new了一個LinkedList物件,first和last屬性都為空,呼叫add進入到linkLast方法。
首先建立一個Node變數 l 將last(此時為空)賦給它,然後new一個newNode變數儲存資料,並且它的前驅指向l,後繼指向null;再把last指向newNode。如下圖所示:
如果滿足if條件,說明這是新增的第一個結點,將first指向newNode:
至此,LinkedList物件的第一個資料新增完畢。假設需要再新增一個資料,我們可以再來走一遍,過程同上不再贅述,圖示如下:
ArrayList和LinkedList區別
- ArrayList:必須開闢連續空間,查詢快,增刪慢。
- LinkedList:無需開闢連續空間,查詢慢,增刪快。
泛型概述
- Java泛型是JDK1.5中引入的一個新特性,其本質是引數化型別,把型別作為引數傳遞。
- 常見形式有泛型類、泛型介面、泛型方法。
- 語法:
- <T,…> T稱為型別佔位符,表示一種引用型別。
- 好處:
- 提高程式碼的重用性。
- 防止型別轉換異常,提高程式碼的安全性。
泛型類
1 /** 2 * 泛型類 3 * 語法:類名<T> 4 * T是型別佔位符,表示一種引用型別,編寫多個使用逗號隔開 5 * 6 */ 7 public class MyGeneric<T> { 8 //1.建立泛型變數 9 //不能使用new來建立,因為泛型是不確定的型別,也可能擁有私密的構造方法 10 T t; 11 //2.泛型作為方法的引數 12 public void show(T t){ 13 System.out.println(t); 14 } 15 16 //3.泛型作為方法的返回值 17 public T getT(){ 18 return t; 19 } 20 }
1 /** 2 * 注意: 3 * 1.泛型只能使用引用型別 4 * 2.不同泛型型別的物件不能相互賦值 5 */ 6 public class TestGeneric { 7 public static void main(String[] args) { 8 //使用泛型類建立物件 9 MyGeneric<String> stringMyGeneric = new MyGeneric<>(); 10 stringMyGeneric.t = "葫蘆娃"; 11 stringMyGeneric.show("火娃"); 12 13 MyGeneric<Integer> integerMyGeneric = new MyGeneric<>(); 14 integerMyGeneric.t=20; 15 integerMyGeneric.show(30); 16 Integer integer = integerMyGeneric.getT(); 17 System.out.println(integer); 18 } 19 }
泛型介面
1 /** 2 * 泛型介面 3 * 語法:介面名<T> 4 * 注意:不能建立泛型靜態常量 5 */ 6 public interface MyInterface<T> { 7 //建立常量 8 String nameString="金剛大芭比"; 9 10 //介面方法 11 T server(T t); 12 }
1 /** 2 * 實現介面時確定泛型類 3 */ 4 public class MyInterfaceImpl implements MyInterface<String> { 5 6 @Override 7 public String server(String s) { 8 System.out.println(nameString); 9 System.out.println(s); 10 return s; 11 } 12 }
1 //測試 2 public class Application { 3 public static void main(String[] args) { 4 MyInterfaceImpl myInterface = new MyInterfaceImpl(); 5 myInterface.server("xxx"); 6 } 7 }
1 /** 2 * 實現介面時不確定泛型類 3 */ 4 public class MyInterfaceImpl2<T> implements MyInterface<T>{ 5 @Override 6 public T server(T t) { 7 System.out.println(nameString); 8 System.out.println(t); 9 return t; 10 } 11 }
1 //測試 2 public class Application { 3 public static void main(String[] args) { 4 MyInterfaceImpl2<Integer> integerMyInterfaceImpl2 = new MyInterfaceImpl2<>(); 5 integerMyInterfaceImpl2.server(20); 6 } 7 }
泛型方法
1 /** 2 * 泛型方法 3 * 語法:<T> 返回型別 4 */ 5 public class MyGenericMethod { 6 public <T> void show(T t){ 7 System.out.println("泛型方法"+t); 8 } 9 }
1 //測試 2 public class Application { 3 public static void main(String[] args) { 4 MyGenericMethod myGenericMethod = new MyGenericMethod(); 5 myGenericMethod.show("金剛"); 6 myGenericMethod.show(18); 7 } 8 }
泛型集合
- 概念:引數化型別、型別安全的集合,強制集合元素的型別必須一致。
- 特點:
- 編譯時即可檢查,而非執行時丟擲異常。
- 訪問時,不必型別轉換(拆箱)。
- 不同泛型指尖引用不能相互賦值,泛型不存在多型。
之前我們在建立LinkedList型別物件的時候並沒有使用泛型,但是進到它的原始碼中會發現:
1 public class LinkedList<E> 2 extends AbstractSequentialList<E> 3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable 4 {//略
它是一個泛型類,而我之前使用的時候並沒有傳遞,說明java語法是允許的,這個時候傳遞的型別是Object類,雖然它是所有類的父類,可以儲存任意的型別,但是在遍歷、獲取元素時需要原來的型別就要進行強制轉換。這個時候就會出現一些問題,假如往連結串列裡儲存了許多不同型別的資料,在強轉的時候就要判斷每一個原來的型別,這樣就很容易出現錯誤。
Set集合概述
Set子介面
- 特點:無序、無下標、元素不可重複。
- 方法:全部繼承自Collection中的方法。
1 /** 2 * 測試Set介面的使用 3 * 特點:1.無序,沒有下標;2.重複 4 * 1.新增資料 5 * 2.刪除資料 6 * 3.遍歷【重點】 7 * 4.判斷 8 */ 9 public class Demo01 { 10 public static void main(String[] args) { 11 Set<String> set = new HashSet<>(); 12 //1.新增資料 13 set.add("火娃"); 14 set.add("金娃"); 15 set.add("木娃"); 16 set.add("水娃"); 17 //set.add("水娃");無法新增元素相同資料 18 System.out.println("元素個數為:"+set.size()); 19 System.out.println(set.toString()); 20 21 //2.刪除資料 22 set.remove("水娃"); 23 System.out.println("刪除後:"+set.size()); 24 System.out.println(set.toString()); 25 26 //3.遍歷 27 //3.1 使用增強for迴圈 28 for (String s:set) { 29 System.out.println(s); 30 } 31 //3.2 使用迭代器 32 Iterator<String> iterator = set.iterator(); 33 while (iterator.hasNext()){ 34 System.out.println(iterator.next()); 35 } 36 37 //4. 判斷 38 System.out.println(set.isEmpty());//false 39 System.out.println(set.contains("火娃"));//true 40 } 41 }
Set實現類
HashSet【重點】
- 基於HashCode計算元素存放位置。
- 當存入元素的雜湊碼相同時,會呼叫equals進行確認,如結果為true,則拒絕後者存入。
1 /** 2 * 人類 3 */ 4 public class Person { 5 private String name; 6 private int age; 7 8 public Person(String name, int age) { 9 this.name = name; 10 this.age = age; 11 } 12 13 public Person() { 14 } 15 16 public String getName() { 17 return name; 18 } 19 20 public void setName(String name) { 21 this.name = name; 22 } 23 24 public int getAge() { 25 return age; 26 } 27 28 public void setAge(int age) { 29 this.age = age; 30 } 31 32 @Override 33 public String toString() { 34 return "Person{" + 35 "name='" + name + '\'' + 36 ", age=" + age + 37 '}'; 38 } 39 }
1 /** 2 * HashSet集合的使用 3 * 儲存結構:雜湊表(陣列+連結串列+紅黑樹) 4 * 1.新增元素 5 * 2.刪除元素 6 * 3.遍歷 7 * 4.判斷 8 */ 9 public class Demo02 { 10 public static void main(String[] args) { 11 HashSet<Person> hashSet=new HashSet<>(); 12 Person p1=new Person("tang",21); 13 Person p2=new Person("he", 22); 14 Person p3=new Person("yu", 21); 15 //1.新增元素 16 hashSet.add(p1); 17 hashSet.add(p2); 18 hashSet.add(p3); 19 //重複,新增失敗 20 hashSet.add(p3); 21 //直接new一個相同屬性的物件,依然會被新增,不難理解。 22 //假如相同屬性便認為是同一個物件,怎麼修改? 23 hashSet.add(new Person("yu", 21)); 24 System.out.println(hashSet.toString()); 25 //2.刪除元素 26 hashSet.remove(p2); 27 //3.遍歷 28 //3.1 增強for 29 for (Person person : hashSet) { 30 System.out.println(person); 31 } 32 //3.2 迭代器 33 Iterator<Person> iterator=hashSet.iterator(); 34 while (iterator.hasNext()) { 35 System.out.println(iterator.next()); 36 } 37 //4.判斷 38 System.out.println(hashSet.isEmpty()); 39 //直接new一個相同屬性的物件結果輸出是false,不難理解。 40 //注:假如相同屬性便認為是同一個物件,該怎麼做? 41 System.out.println(hashSet.contains(new Person("tang", 21))); 42 } 43 }
注:hashSet儲存過程:
- 根據hashCode計算儲存的位置,如果位置為空,則直接儲存,否則執行第二步。
- 執行equals方法,如果方法返回true,則認為是重複,拒絕儲存,否則形成連結串列。
儲存過程實際上就是重複依據,要實現“注”裡的問題,可以重寫hashCode和equals程式碼:
1 @Override 2 public int hashCode() { 3 final int prime = 31; 4 int result = 1; 5 result = prime * result + age; 6 result = prime * result + ((name == null) ? 0 : name.hashCode()); 7 return result; 8 } 9 @Override 10 public boolean equals(Object obj) { 11 if (this == obj) 12 return true; 13 if (obj == null) 14 return false; 15 if (getClass() != obj.getClass()) 16 return false; 17 Person other = (Person) obj; 18 if (age != other.age) 19 return false; 20 if (name == null) { 21 if (other.name != null) 22 return false; 23 } else if (!name.equals(other.name)) 24 return false; 25 return true; 26 }
hashCode方法裡為什麼要使用31這個數字大概有兩個原因:
- 31是一個質數,這樣的數字在計算時可以儘量減少雜湊衝突。
- 可以提高執行效率,因為31*i=(i<<5)-i,31乘以一個數可以轉換成移位操作,這樣能快一點;但是也有網上一些人對這兩點提出質疑。
TreeSet
- 基於排序順序實現不重複。
- 實現了SortedSet介面,對集合元素自動排序。
- 元素物件的型別必須實現Comparable介面,指定排序規則。
- 通過CompareTo方法確定是否為重複元素。
1 /** 2 * 使用TreeSet儲存資料 3 * 儲存結構:紅黑樹 4 * 要求:元素類必須實現Comparable介面,compareTo方法返回0,認為是重複元素 5 */ 6 public class Demo01 { 7 public static void main(String[] args) { 8 9 TreeSet<Person> treeSet = new TreeSet<>(); 10 11 Person p1 = new Person("it",12); 12 Person p2 = new Person("he",12); 13 Person p3 = new Person("she",12); 14 15 //1.新增元素 16 treeSet.add(p1); 17 treeSet.add(p2); 18 treeSet.add(p3); 19 treeSet.add(p3);//相同無法新增 20 //注:直接新增會報型別錯誤,需要實現Comparable介面 21 System.out.println("元素個數為:"+treeSet.size()); 22 System.out.println(treeSet.toString()); 23 24 //2.刪除元素 25 treeSet.remove(p3); 26 treeSet.remove(new Person("she",12)); 27 System.out.println(treeSet.toString()); 28 29 //3.遍歷 30 //3.1 增強for迴圈 31 for (Person person:treeSet) { 32 System.out.println(person); 33 } 34 //3.2 迭代器 35 Iterator<Person> iterator = treeSet.iterator(); 36 while (iterator.hasNext()){ 37 System.out.println(iterator.next()); 38 } 39 40 //4.判斷 41 System.out.println(treeSet.contains(new Person("it",12))); 42 } 43 }
執行報錯,因為treeset是紅黑樹,當前程式碼沒有定義比較的屬性,需要重寫Comparable方法
先檢視Comparable介面的原始碼,發現只有一個compareTo抽象方法,在Person類中實現它:
1 public class Person implements Comparable<Person> { 2 @Override 3 public int compareTo(Person o) { 4 int n1 = this.getName().compareTo(o.getName());//名字相同,返回0,不同返回其他數字 5 int n2 = this.getAge()-o.getAge();//比較年齡 6 return n1==0?n2:n1;//名字相同,返回n2,名字不同,返回n1 7 } 8 }
再次執行TreeSet的Demo01,執行成功
除了實現Comparable介面裡的比較方法,還有另外一種方法,TreeSet也提供了一個帶比較器Comparator的構造方法,使用匿名內部類來實現它:
1 /** 2 * TreeSet的使用 3 * Comparator:實現定製比較(比較器) 4 */ 5 public class Demo02 { 6 public static void main(String[] args) { 7 TreeSet<Person> person = new TreeSet<Person>(new Comparator<Person>() { 8 @Override 9 public int compare(Person o1, Person o2) { 10 // 先按年齡比較 11 // 再按姓名比較 12 int n1=o1.getAge()-o2.getAge(); 13 int n2=o1.getName().compareTo(o2.getName()); 14 return n1==0?n2:n1; 15 } 16 }); 17 Person p1 = new Person("it",12); 18 Person p2 = new Person("he",12); 19 Person p3 = new Person("she",12); 20 21 //1.新增元素 22 person.add(p1); 23 person.add(p2); 24 person.add(p3); 25 //注:直接新增會報型別錯誤,需要實現Comparable介面 26 System.out.println("元素個數為:"+person.size()); 27 System.out.println(person.toString()); 28 } 29 }
接下來我們來做一個小案例:
1 /** 2 * 要求:使用TreeSet集合實現字串按照長度進行排序 3 * helloworld tangrui hechengyang wangzixu yuguoming 4 * Comparator介面實現定製比較 5 */ 6 public class Demo03 { 7 public static void main(String[] args) { 8 TreeSet<String> s = new TreeSet<>(new Comparator<String>() { 9 @Override 10 //1.先比較長度 11 //2.在比較字串 12 public int compare(String o1, String o2) { 13 int n1 = o1.length()-o2.length(); 14 int n2 = o1.compareTo(o2); 15 return n1==0?n2:n1; 16 } 17 }); 18 s.add("hello world"); 19 s.add("hello Java"); 20 s.add("hello Python"); 21 System.out.println(s.toString()); 22 } 23 }
Map體系集合
-
Map介面的特點:
- 用於儲存任意鍵值對(Key-Value)。
- 鍵:無序、無下標、不允許重複(唯一)。
- 值:無序、無下標、允許重複。
Map集合概述
-
特點:儲存一對資料(Key-Value),無序、無下標,鍵不可重複。
-
方法:
V put(K key,V value)
//將物件存入到集合中,關聯鍵值。key重複則覆蓋原值。
-
Object get(Object key)
//根據鍵獲取相應的值。Set<K>
//返回所有的keyCollection<V> values()
//返回包含所有值的Collection集合。Set<Map.Entry<K,V>>
//鍵值匹配的set集合
1 /** 2 * Map介面的使用 3 * 特點:1.儲存鍵值對 2.鍵不能重複,值可以重複 3.無序 4 */ 5 public class Demo01 { 6 public static void main(String[] args) { 7 //建立Map集合 8 HashMap<String, String> map = new HashMap<>(); 9 10 //1.新增元素 11 map.put("cn","中國"); 12 map.put("uk","英國"); 13 map.put("usa","美國"); 14 map.put("cn","Chinse");//相同key新增,會替換之前的value值 15 System.out.println("元素個數為:"+map.size()); 16 System.out.println(map.toString()); 17 18 //2.刪除 19 map.remove("usa"); 20 System.out.println("刪除後元素個數為:"+map.size()); 21 System.out.println(map.toString()); 22 23 //3.遍歷 24 //3.1 使用keyset(),先獲取key,再使用get(key)方法獲取value值 25 Set<String> keySet = map.keySet(); 26 for (String key:keySet) { 27 System.out.println(key+"-----"+map.get(key)); 28 } 29 //簡寫 30 for (String s:map.keySet()) { 31 System.out.println(s+"-----"+map.get(s)); 32 } 33 //3.2 使用entrySet()方法;效率更高,把key和value都拿出來了,不需要為了value再去多做一遍查詢key的操作 34 Set<Map.Entry<String, String>> entries = map.entrySet(); 35 for (Map.Entry<String,String> entry:entries) { 36 System.out.println(entry.getKey()+"------"+entry.getValue()); 37 } 38 //簡寫 39 for (Map.Entry<String,String> entry:map.entrySet()) { 40 System.out.println(entry.getKey()+"------"+entry.getValue()); 41 } 42 43 44 } 45 }
keySet遍歷和entrySet的區別
Map集合的實現類
HashMap【重點】
-
JDK1.2版本,執行緒不安全,執行效率快;允許用null作為key或是value。
1 /** 2 * 學生類 3 */ 4 public class Student { 5 private String name; 6 private int age; 7 8 public String getName() { 9 return name; 10 } 11 12 public void setName(String name) { 13 this.name = name; 14 } 15 16 public int getAge() { 17 return age; 18 } 19 20 public void setAge(int age) { 21 this.age = age; 22 } 23 24 @Override 25 public String toString() { 26 return "Student{" + 27 "name='" + name + '\'' + 28 ", age=" + age + 29 '}'; 30 } 31 32 public Student(String name, int age) { 33 this.name = name; 34 this.age = age; 35 } 36 }
1 /** 2 * HashMap的使用 3 * 儲存結構:雜湊表(陣列+連結串列+紅黑樹) 4 */ 5 public class Demo01 { 6 public static void main(String[] args) { 7 HashMap<Student, String> hashMap = new HashMap<>(); 8 Student p1 = new Student("趙一",11); 9 Student p2 = new Student("王二",11); 10 Student p3 = new Student("張三",11); 11 Student p4 = new Student("李四",11); 12 //1.新增元素 13 hashMap.put(p1,"北京"); 14 hashMap.put(p2,"上海"); 15 hashMap.put(p3,"廣州"); 16 hashMap.put(p4,"曹縣"); 17 hashMap.put(p4,"曹縣(山東)");//不能新增重複的key,但是會將value更新 18 19 //?,上面已經有一個李四的key了,通過這個還可以繼續新增,那能不能讓系統根據key的資料判重,而不是根據記憶體地址? 20 hashMap.put(new Student("李四",11),"南京"); 21 System.out.println("元素個數為:"+hashMap.size()); 22 System.out.println(hashMap.toString()); 23 24 //2.刪除元素 25 hashMap.remove(p2); 26 System.out.println(hashMap.toString()); 27 28 //3.遍歷 29 //3.1 使用keySet遍歷 30 for (Student s:hashMap.keySet()) { 31 System.out.println(s+"------"+hashMap.get(s)); 32 } 33 //3.2 使用entrySet遍歷 34 for (Map.Entry<Student,String> entry:hashMap.entrySet()) { 35 System.out.println(entry.getKey()+"------"+entry.getValue()); 36 } 37 38 //4.判斷 39 System.out.println(hashMap.containsKey(p1)); 40 System.out.println(hashMap.containsKey(new Student("李四",11))); 41 System.out.println(hashMap.containsValue("曹縣(山東)")); 42 } 43 }
-
注:和之前說過的HashSet類似,重複依據是hashCode和equals方法,重寫即可:
1 @Override 2 public int hashCode() { 3 final int prime = 31; 4 int result = 1; 5 result = prime * result + id; 6 result = prime * result + ((name == null) ? 0 : name.hashCode()); 7 return result; 8 } 9 @Override 10 public boolean equals(Object obj) { 11 if (this == obj) 12 return true; 13 if (obj == null) 14 return false; 15 if (getClass() != obj.getClass()) 16 return false; 17 Student other = (Student) obj; 18 if (id != other.id) 19 return false; 20 if (name == null) { 21 if (other.name != null) 22 return false; 23 } else if (!name.equals(other.name)) 24 return false; 25 return true; 26 }
HashMap原始碼分析
-
預設初始化容量:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 陣列最大容量:
static final int MAXIMUM_CAPACITY = 1 << 30;
- 陣列最大容量:
-
預設載入因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
-
連結串列調整為紅黑樹的連結串列長度閾值(JDK1.8):
static final int TREEIFY_THRESHOLD = 8;
-
紅黑樹調整為連結串列的連結串列長度閾值(JDK1.8):
static final int UNTREEIFY_THRESHOLD = 6;
-
連結串列調整為紅黑樹的陣列最小閾值(JDK1.8):
static final int MIN_TREEIFY_CAPACITY = 64;
-
HashMap儲存的陣列:
transient Node<K,V>[] table;
- HashMap儲存的元素個數:
transient int size;
- 預設載入因子是什麼?
- 就是判斷陣列是否擴容的一個因子。假如陣列容量為100,如果HashMap的儲存元素個數超過了100*0.75=75,那麼就會進行擴容。
- 連結串列調整為紅黑樹的連結串列長度閾值是什麼?
- 假設在陣列中下標為3的位置已經儲存了資料,當新增資料時通過雜湊碼得到的儲存位置又是3,那麼就會在該位置形成一個連結串列,當連結串列過長時就會轉換成紅黑樹以提高執行效率,這個閾值就是連結串列轉換成紅黑樹的最短連結串列長度;
- 紅黑樹調整為連結串列的連結串列長度閾值是什麼?
- 當紅黑樹的元素個數小於該閾值時就會轉換成連結串列。
- 連結串列調整為紅黑樹的陣列最小閾值是什麼?
- 並不是只要連結串列長度大於8就可以轉換成紅黑樹,在前者條件成立的情況下,陣列的容量必須大於等於64才會進行轉換。
HashMap的陣列table儲存的就是一個個的Node<K,V>型別,很清晰地看到有一對鍵值,還有一個指向next的指標(以下只擷取了部分原始碼):
1 static class Node<K,V> implements Map.Entry<K,V> { 2 final K key; 3 V value; 4 Node<K,V> next; 5 }
之前的程式碼中在new物件時呼叫的是HashMap的無參構造方法,進入到該構造方法的原始碼檢視一下:
1 public HashMap() { 2 this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted 3 }
發現沒什麼內容,只是賦值了一個預設載入因子;而在上文我們觀察到原始碼中table和size都沒有賦予初始值,說明剛建立的HashMap物件沒有分配容量,並不擁有預設的16個空間大小,這樣做的目的是為了節約空間,此時table為null,size為0。
當我們往物件裡新增元素時呼叫put方法:
1 public V put(K key, V value) { 2 return putVal(hash(key), key, value, false, true); 3 }
put方法把key和value傳給了putVal,同時還傳入了一個hash(Key)所返回的值,這是一個產生雜湊值的方法,再進入到putVal方法(部分原始碼):
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 2 boolean evict) { 3 Node<K,V>[] tab; Node<K,V> p; int n, i; 4 if ((tab = table) == null || (n = tab.length) == 0) 5 n = (tab = resize()).length; 6 if ((p = tab[i = (n - 1) & hash]) == null) 7 tab[i] = newNode(hash, key, value, null); 8 else{ 9 //略 10 } 11 }
這裡面建立了一個tab陣列和一個Node變數p,第一個if實際是判斷table是否為空,而我們現在只關注剛建立HashMap物件時的狀態,此時tab和table都為空,滿足條件,執行內部程式碼,這條程式碼其實就是把resize()所返回的結果賦給tab,n就是tab的長度,resize顧名思義就是重新調整大小。檢視resize()原始碼(部分):
1 final Node<K,V>[] resize() { 2 Node<K,V>[] oldTab = table; 3 int oldCap = (oldTab == null) ? 0 : oldTab.length; 4 int oldThr = threshold; 5 if (oldCap > 0); 6 else if (oldThr > 0); 7 else { // zero initial threshold signifies using defaults 8 newCap = DEFAULT_INITIAL_CAPACITY; 9 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 10 } 11 @SuppressWarnings({"rawtypes","unchecked"}) 12 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 13 table = newTab; 14 return newTab; 15 }
該方法首先把table及其長度賦值給oldTab和oldCap;threshold是閾值的意思,此時為0,所以前兩個if先不管,最後else裡newCap的值為預設初始化容量16;往下建立了一個newCap大小的陣列並將其賦給了table,剛建立的HashMap物件就在這裡獲得了初始容量。然後我們再回到putVal方法,第二個if就是根據雜湊碼得到的tab中的一個位置是否為空,為空便直接新增元素,此時陣列中無元素所以直接新增。至此HashMap物件就完成了第一個元素的新增。當新增的元素超過16*0.75=12時,就會進行擴容:
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict){ 2 if (++size > threshold) 3 resize(); 4 }
擴容的程式碼如下(部分):
1 final Node<K,V>[] resize() { 2 int oldCap = (oldTab == null) ? 0 : oldTab.length; 3 int newCap; 4 if (oldCap > 0) { 5 if (oldCap >= MAXIMUM_CAPACITY) {//略} 6 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 7 oldCap >= DEFAULT_INITIAL_CAPACITY) 8 } 9 }
核心部分是else if裡的移位操作,也就是說每次擴容都是原來大小的兩倍。
-
*注**:額外說明的一點是在JDK1.8以前連結串列是頭插入,JDK1.8以後連結串列是尾插入。
HashSet原始碼分析
瞭解完HashMap之後,再回過頭來看之前的HashSet原始碼,為什麼放在後面寫你們看一下原始碼就知道了(部分):
1 public class HashSet<E> 2 extends AbstractSet<E> 3 implements Set<E>, Cloneable, java.io.Serializable 4 { 5 private transient HashMap<E,Object> map; 6 private static final Object PRESENT = new Object(); 7 public HashSet() { 8 map = new HashMap<>(); 9 } 10 }
可以看見HashSet的儲存結構就是HashMap,那它的儲存方式是怎樣的呢?可以看一下add方法:
1 public boolean add(E e) { 2 return map.put(e, PRESENT)==null; 3 }
很明瞭地發現它的add方法呼叫的就是map的put方法,把元素作為map的key傳進去的。。
Hashtable
- JDK1.0版本,執行緒安全,執行效率慢;不允許null作為key或是value。
-
初始容量11,載入因子0.75。
這個集合在開發過程中已經不用了,稍微瞭解即可。
Properties
- Hashtable的子類,要求key和value都是String。通常用於配置檔案的讀取。
它繼承了Hashtable的方法,與流關係密切,此處不詳解。
TreeMap
- 實現了SortedMap介面(是Map的子介面),可以對key自動排序。
1 /** 2 * TreeMap的使用 3 * 儲存結構:紅黑樹 4 */ 5 public class Demo3 { 6 public static void main(String[] args) { 7 TreeMap<Student, Integer> treeMap=new TreeMap<Student, Integer>(); 8 Student s1=new Student("tang", 36); 9 Student s2=new Student("yu", 101); 10 Student s3=new Student("he", 10); 11 //1.新增元素 12 treeMap.put(s1, 21); 13 treeMap.put(s2, 22); 14 treeMap.put(s3, 21); 15 //不能直接列印,需要實現Comparable介面,因為紅黑樹需要比較大小 16 System.out.println(treeMap.toString()); 17 //2.刪除元素 18 treeMap.remove(new Student("he", 10)); 19 System.out.println(treeMap.toString()); 20 //3.遍歷 21 //3.1 使用keySet() 22 for (Student key : treeMap.keySet()) { 23 System.out.println(key+" "+treeMap.get(key)); 24 } 25 //3.2 使用entrySet() 26 for (Entry<Student, Integer> entry : treeMap.entrySet()) { 27 System.out.println(entry.getKey()+" "+entry.getValue()); 28 } 29 //4.判斷 30 System.out.println(treeMap.containsKey(s1)); 31 System.out.println(treeMap.isEmpty()); 32 } 33 }
在學生類中實現Comparable介面:
1 public class Student implements Comparable<Student>{ 2 @Override 3 public int compareTo(Student o) { 4 int n1=this.id-o.id; 5 return n1; 6 }
除此之外還可以使用比較器來定製比較:
1 TreeMap<Student, Integer> treeMap2=new TreeMap<Student, Integer>(new Comparator<Student>() { 2 @Override 3 public int compare(Student o1, Student o2) { 4 // 略 5 return 0; 6 } 7 });
TreeSet原始碼
和HashSet類似,放在TreeMap之後講便一目瞭然(部分):
1 public class TreeSet<E> extends AbstractSet<E> 2 implements NavigableSet<E>, Cloneable, java.io.Serializable 3 { 4 private transient NavigableMap<E,Object> m; 5 private static final Object PRESENT = new Object(); 6 TreeSet(NavigableMap<E,Object> m) { 7 this.m = m; 8 } 9 public TreeSet() { 10 this(new TreeMap<E,Object>()); 11 } 12 }
TreeSet的儲存結構實際上就是TreeMap,再來看其儲存方式:
1 public boolean add(E e) { 2 return m.put(e, PRESENT)==null; 3 }
它的add方法呼叫的就是TreeMap的put方法,將元素作為key傳入到儲存結構中。
Collections工具類
-
概念:集合工具類,定義了除了存取以外的集合常用方法。
-
方法:
public static void reverse(List<?> list)
//反轉集合中元素的順序public static void shuffle(List<?> list)
//隨機重置集合元素的順序public static void sort(List<T> list)
//升序排序(元素型別必須實現Comparable介面)
1 /** 2 * 演示Collections工具類的使用 3 * 4 */ 5 public class Demo4 { 6 public static void main(String[] args) { 7 List<Integer> list=new ArrayList<Integer>(); 8 list.add(20); 9 list.add(10); 10 list.add(30); 11 list.add(90); 12 list.add(70); 13 14 //sort排序 15 System.out.println(list.toString()); 16 Collections.sort(list); 17 System.out.println(list.toString()); 18 System.out.println("---------"); 19 20 //binarySearch二分查詢 21 int i=Collections.binarySearch(list, 10); 22 System.out.println(i); 23 24 //copy複製 25 List<Integer> list2=new ArrayList<Integer>(); 26 for(int i1=0;i1<5;++i1) { 27 list2.add(0); 28 } 29 //該方法要求目標元素容量大於等於源目標 30 Collections.copy(list2, list); 31 System.out.println(list2.toString()); 32 33 //reserve反轉 34 Collections.reverse(list2); 35 System.out.println(list2.toString()); 36 37 //shuffle 打亂 38 Collections.shuffle(list2); 39 System.out.println(list2.toString()); 40 41 //補充:list轉成陣列 42 Integer[] arr=list.toArray(new Integer[0]); 43 System.out.println(arr.length); 44 //補充:陣列轉成集合 45 String[] nameStrings= {"tang","he","yu"}; 46 //受限集合,不能新增和刪除 47 List<String> list3=Arrays.asList(nameStrings); 48 System.out.println(list3); 49 50 //注:基本型別轉成集合時需要修改為包裝類 51 } 52 }