【Java心得总结六】Java容器中——Collection
在【Java心得總結五】Java容器上——容器初探這篇博文中,我對Java容器類庫從一個整體的偏向于宏觀的角度初步認識了Java容器類庫。而在這篇博文中,我想著重對容器類庫中的Collection容器做一個著重的探索與總結。
Collection:一個獨立元素的序列,這些元素都服從一條或多條規則。(注:Collection其實就是將一組數據對象按照一維線性的方式組織起來)List必須按照插入的順序保存元素,而set不能有重復元素。Queue按照排隊規則來確定對象產生的順序(通常與它們被插入的順序相同)。
廢話不說先上圖!
Java容器類庫
——摘自《Thinking in java》
從上圖中我們可以看出Collection是Java類庫中的一個大的子模塊,它主要包含了:List和Set兩部分
一、List接口
Java中List可以將元素維護在特定的序列中(這里元素既可以是基本類型也可以自定義類【Java心得總結一】Java基本類型和包裝類型解析)
List接口主要有兩種實現類型:
- 基本的ArrayList,它長于隨機訪問元素,但是在List的中間插入和移除元素時較慢
- LinkedList,它通過代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問。LinkedList在隨機訪問方面相對比較慢。
(二者的關系基本就是數據機構中我們學過的順序存儲結構和鏈表的關系)
ArrayList:
聲明:
1 import java.util.ArrayList; 2 import java.util.List; 3 4 class MyClass{} 5 6 public class ArrayListTest{ 7 // 聲明存放整數的列表,這里必須用包裝器類型 8 List<Integer> l1 = new ArrayList<Integer>(); 9 // 聲明存放自定義類的列表 10 List<MyClass> l2 = new ArrayList<MyClass>(); 11 // 也可以像這樣不給出類型參數,編譯器會在向l3中加入元素的時候進行判定 12 List l3 = new ArrayList(); 13 }聲明一個List容器的時候,用到了泛型的知識(【Java心得總結三】Java泛型上——初識泛型和【Java心得總結四】Java泛型下——萬惡的擦除)
還有一點有趣的是,我們聲明了ArrayList對象,卻將引用賦值給了List引用,從上面圖中可以看出ArrayList是List接口的實現,所以我們用List引用來持有ArrayList對象是完全可行的。
方法:
| add() | 將元素添加到列表中(這是最常用的方法之一,當然了添加的元素類型必須是同一類型或者說繼承自相同基類的類型 |
| contains() | 確定某個對象是否在列表中 |
| remove() | 將某一個對象的引用傳遞給remove方法,即可移除列表中的一個對象 |
| indexOf() | 持有一個對象的引用,則利用該方法可以獲得其在列表中的編號 |
| equals() | 該方法是Object基類中的一個方法remove方法在刪除元素時要要用到這個方法進行匹配 |
| subList() | 該方法允許你從較大的列表中創出一個片段 |
| retainAll() | 該方法是一種有效的交集操作 |
| removeAll() | 移除List中的所有元素 |
| get() | 取得指定索引位置的元素 |
?
?
?
?
?
?
?
代碼示例:
1 //: holding/ListFeatures.java 2 import java.util.*; 3 4 public class ArrayListTest { 5 public static void main(String[] args) { 6 Random rand = new Random(47); 7 List<String> ls = new ArrayList<String>(); 8 ls.add("s1"); 9 ls.add("s2"); 10 ls.add("s3"); 11 ls.add("s4"); 12 ls.add("s5"); 13 System.out.println("1: " + ls); 14 String s6 = new String("s6"); 15 ls.add(s6); // Automatically resizes 16 System.out.println("2: " + ls); 17 System.out.println("3: " + ls.contains(s6)); 18 ls.remove(s6); // Remove by object 19 String s3 = ls.get(2); 20 System.out.println("4: " + s3 + " " + ls.indexOf(s3)); 21 String s7 = new String(); 22 System.out.println("5: " + ls.indexOf(s7)); 23 System.out.println("6: " + ls.remove(s7)); 24 // Must be the exact object: 25 System.out.println("7: " + ls.remove(s3)); 26 System.out.println("8: " + ls); 27 ls.add(3, new String("s8")); // Insert at an index 28 System.out.println("9: " + ls); 29 List<String> sub = ls.subList(1, 4); 30 System.out.println("subList: " + sub); 31 System.out.println("10: " + ls.containsAll(sub)); 32 Collections.sort(sub); // In-place sort 33 System.out.println("sorted subList: " + sub); 34 // Order is not important in containsAll(): 35 System.out.println("11: " + ls.containsAll(sub)); 36 Collections.shuffle(sub, rand); // Mix it up 37 System.out.println("shuffled subList: " + sub); 38 System.out.println("12: " + ls.containsAll(sub)); 39 List<String> copy = new ArrayList<String>(ls); 40 sub = Arrays.asList(ls.get(1), ls.get(4)); 41 System.out.println("sub: " + sub); 42 copy.retainAll(sub); 43 System.out.println("13: " + copy); 44 copy = new ArrayList<String>(ls); // Get a fresh copy 45 copy.remove(2); // Remove by index 46 System.out.println("14: " + copy); 47 copy.removeAll(sub); // Only removes exact objects 48 System.out.println("15: " + copy); 49 copy.set(1, new String("s9")); // Replace an element 50 System.out.println("16: " + copy); 51 copy.addAll(2, sub); // Insert a list in the middle 52 System.out.println("17: " + copy); 53 System.out.println("18: " + ls.isEmpty()); 54 ls.clear(); // Remove all elements 55 System.out.println("19: " + ls); 56 System.out.println("20: " + ls.isEmpty()); 57 List newLs = new ArrayList<String>(); 58 newLs.add("newS1"); 59 newLs.add("newS2"); 60 newLs.add("newS3"); 61 newLs.add("newS4"); 62 newLs.add("newS5"); 63 ls.addAll(newLs); 64 System.out.println("21: " + ls); 65 Object[] o = ls.toArray(); 66 System.out.println("22: " + o[3]); 67 String[] str = ls.toArray(new String[0]); 68 System.out.println("23: " + str[3]); 69 } 70 } 71 /* 72 1: [s1, s2, s3, s4, s5] 73 2: [s1, s2, s3, s4, s5, s6] 74 3: true 75 4: s3 2 76 5: -1 77 6: false 78 7: true 79 8: [s1, s2, s4, s5] 80 9: [s1, s2, s4, s8, s5] 81 subList: [s2, s4, s8] 82 10: true 83 sorted subList: [s2, s4, s8] 84 11: true 85 shuffled subList: [s4, s2, s8] 86 12: true 87 sub: [s4, s5] 88 13: [s4, s5] 89 14: [s1, s4, s8, s5] 90 15: [s1, s8] 91 16: [s1, s9] 92 17: [s1, s9, s4, s5] 93 18: false 94 19: [] 95 20: true 96 21: [newS1, newS2, newS3, newS4, newS5] 97 22: newS4 98 23: newS4 99 *///:~上面的代碼我們以String作為List容器的存儲對象,基本涵蓋了所有基本的ArrayList操作。
我們在代碼36行使用了Collections的shuffle方法,它的作用就是將容器中的元素打亂。
LinkedList
正如我們前面提到的,像ArrayList一樣LinkedList也實現了基本的List接口,但是在某些方面它要比ArrayList要高效一些,如插入和移除操作,但是在隨機訪問方面要遜色一些。
另外LinkedList還有一個重要的作用是用來實現棧、隊列以及雙端隊列等數據結構中的一些基本結構
聲明:
1 import java.util.*; 2 class MyClass{} 3 public class LinkedListTest { 4 // 聲明持有String類型的列表,同樣這里我們可以用List來持有LinkedList的引用 5 List<String> ls = new LinkedList<String>(); 6 // 聲明持有自定義類型的列表 7 List<MyClass> lsm = new LinkedList<MyClass>(); 8 // 同樣我們可以用LinkedList的引用來持有它 9 LinkedList<String> lss = new LinkedList<String>(); 10 // 同ArrayList一樣我們也可以并不在聲明時賦以類型參數,而再賦值時再確定 11 List l = new LinkedList(); 12 }上面的聲明方式同ArrayList是基本一樣的,我們可以利用List接口來持有LinkedList對象的引用,同樣也可以用LinkedList自己來持有這個引用
方法:
因為LinkedList也實現了List接口,當然上面ArrayList中的方法也都包含,除此之外它還包含如下方法
| getFirst() | 返回列表的頭(第一個元素),而不移除它。如果List為空則拋出NoSuchElementException |
| element() | 同getFirst() |
| peek() | 與前兩個方法的唯一區別是,如果List為空則返回null |
| removeFirst() | 移除并返回列表頭,如果List為空,同上拋出相同的異常 |
| remove() | 同removeFirst() |
| poll() | 與前兩個方法的唯一區別是,如果List為空則返回null |
| addFirst() | 將某個元素插入到列表頭部 |
| addLast() | 將某個元素插入到列表尾部 |
| add() | 同addLast() |
| removeLast | 移除并返回列表的最后一個元素 |
?
?
?
?
?
?
?
?
代碼示例
1 import java.util.*; 2 3 public class LinkedListTest { 4 public static void main(String[] args) { 5 LinkedList<String> ls = new LinkedList<String>(); 6 ls.add("s1"); 7 ls.addFirst("s2"); 8 ls.addLast("s3"); 9 System.out.println(ls); 10 // Identical: 11 System.out.println("ls.getFirst(): " + ls.getFirst()); 12 System.out.println("ls.element(): " + ls.element()); 13 // Only differs in empty-list behavior: 14 System.out.println("ls.peek(): " + ls.peek()); 15 // Identical; remove and return the first element: 16 System.out.println("ls.remove(): " + ls.remove()); 17 System.out.println("ls.removeFirst(): " + ls.removeFirst()); 18 // Only differs in empty-list behavior: 19 System.out.println("ls.poll(): " + ls.poll()); 20 System.out.println(ls); 21 ls.addFirst(new String("s4")); 22 System.out.println("After addFirst(): " + ls); 23 ls.offer(new String("s5")); 24 System.out.println("After offer(): " + ls); 25 ls.add(new String("s6")); 26 System.out.println("After add(): " + ls); 27 ls.addLast(new String("s7")); 28 System.out.println("After addLast(): " + ls); 29 System.out.println("ls.removeLast(): " + ls.removeLast()); 30 } 31 } 32 /* 33 [s2, s1, s3] 34 ls.getFirst(): s2 35 ls.element(): s2 36 ls.peek(): s2 37 ls.remove(): s2 38 ls.removeFirst(): s1 39 ls.poll(): s3 40 [] 41 After addFirst(): [s4] 42 After offer(): [s4, s5] 43 After add(): [s4, s5, s6] 44 After addLast(): [s4, s5, s6, s7] 45 ls.removeLast(): s7 46 *///:~說完了List,我們一定會想到數據結構中非常重要的兩種,隊列和棧,在Java中這兩種數據結構我們應該怎么實現呢?這就要用到我們剛介紹的LinkedList
棧和隊列
棧:
“棧”通常指后進先出(LIFO)的容器,在博文剛開始的圖中,我們肯定會發現Java容器類庫的結構圖中已經包含了棧這個結構,但是基于在Java1.0中設計者的失誤導致Stack在新版本的Java中是不推薦使用(這里原因不具體廢話了,反正就是不用它便是),然而有了LinkedList,我們完全可以自己很快的寫一個,因為說到底棧無非就是操作受限的鏈表(它只允許在鏈表的一端進行讀寫操作)。
代碼:
1 import java.util.LinkedList; 2 3 public class Stack<T> { 4 private LinkedList<T> storage = new LinkedList<T>(); 5 6 public void push(T v) { 7 storage.addFirst(v); 8 } 9 10 public T peek() { 11 return storage.getFirst(); 12 } 13 14 public T pop() { 15 return storage.removeFirst(); 16 } 17 18 public boolean empty() { 19 return storage.isEmpty(); 20 } 21 22 public String toString() { 23 return storage.toString(); 24 } 25 } // /:~這里我們將LinkedList用組合的方式封裝在我們的Stack類中,切記這里不能使用繼承,因為如果我們的Stack類繼承自LinkedList,那么從外部我們就可以獲得所有LinkedList的public接口,那么棧就沒意義了(其實Java1.0中的Stack的設計就犯了這個錯誤)
隊列:
“隊列”是一個典型的先進先出(FIFO)的容器,并且在【Java心得總結五】Java容器上——容器初探博文的圖中我們可以看到LinkedList除了實現了List接口還實現了Queue接口,這也就是為什么我們在上面介紹LinkedList時會有那么多功能重復但是名字不同的方法的原因了。
代碼:
1 import java.util.*; 2 3 public class QueueDemo { 4 public static void printQ(Queue queue) { 5 while (queue.peek() != null) 6 System.out.print(queue.remove() + " "); 7 System.out.println(); 8 } 9 10 public static void main(String[] args) { 11 Queue<Integer> queue = new LinkedList<Integer>(); 12 Random rand = new Random(47); 13 for (int i = 0; i < 10; i++) 14 queue.offer(rand.nextInt(i + 10)); 15 printQ(queue); 16 Queue<Character> qc = new LinkedList<Character>(); 17 for (char c : "Brontosaurus".toCharArray()) 18 qc.offer(c); 19 printQ(qc); 20 } 21 } /* 22 * Output: 8 1 1 1 5 14 3 1 0 1 B r o n t o s a u r u s 23 */// :~其實從上面我們可以看出我們利用Queue來持有LinkedList對象時,Queue窄化了LinkedList的方法訪問權限,以使得有恰當的方法才可以使用。(這很合理,因為同棧一樣,隊列說到底也是操作受限的鏈表,它只允許在一端寫入另一端讀)
List的遍歷
像數組一樣,我們對容器列表經常做得操作就是遍歷,當然了我們可以向遍歷數組一樣用一個迭代變量進行疊加,然后利用get()方法來取出對應索引位置的元素來達到遍歷的目的。但是還有一個更好的方法就是利用Iterator接口(參見【Java心得總結五】Java容器上——容器初探)
二、Set接口
Set最最重要的特征就是不保存重復元素。從博文開始的圖中我們可以看出Set接口主要有兩種具體的實現:HashSet和TreeSet,而在HashSet的基礎上還實現了一個LinkedHashSet。
- HashSet:擁有最快的查詢速度,存入HashSet的元素必須定義hashCode()方法
- TreeSet:保持元素處于排序狀態,底層為樹結構。使用它可以從Set中提取有序的序列。元素必須實現Comparable接口
- LinkedHashSet:以插入順序保持元素,用迭代器進行遍歷時會按照插入時的順序顯示,但也必須定義hashCode()方法
Set具有與Collection完全一樣的接口,因此沒有任何額外的功能,不像前面有兩個不同的List。實際上Set就是Collection,只是行為不同。(這是繼承與多態思想的典型應用:表現不同的行為。)Set是基于對象的值來確定對象的歸屬性的。
代碼示例:
1 import java.util.*; 2 3 class SetType { 4 int i; 5 6 public SetType(int n) { 7 i = n; 8 } 9 10 public boolean equals(Object o) { 11 return o instanceof SetType && (i == ((SetType) o).i); 12 } 13 14 public String toString() { 15 return Integer.toString(i); 16 } 17 } 18 19 class HashType extends SetType { 20 public HashType(int n) { 21 super(n); 22 } 23 24 public int hashCode() { 25 return i; 26 } 27 } 28 29 class TreeType extends SetType implements Comparable<TreeType> { 30 public TreeType(int n) { 31 super(n); 32 } 33 34 public int compareTo(TreeType arg) { 35 return (arg.i < i ? -1 : (arg.i == i ? 0 : 1)); 36 } 37 } 38 39 public class TypesForSets { 40 static <T> Set<T> fill(Set<T> set, Class<T> type) { 41 try { 42 for (int i = 0; i < 10; i++) 43 set.add(type.getConstructor(int.class).newInstance(i)); 44 } 45 catch (Exception e) { 46 throw new RuntimeException(e); 47 } 48 return set; 49 } 50 51 static <T> void test(Set<T> set, Class<T> type) { 52 fill(set, type); 53 fill(set, type); // Try to add duplicates 54 fill(set, type); 55 System.out.println(set); 56 } 57 58 public static void main(String[] args) { 59 test(new HashSet<HashType>(), HashType.class); 60 test(new LinkedHashSet<HashType>(), HashType.class); 61 test(new TreeSet<TreeType>(), TreeType.class); 62 // Things that don’t work: 63 test(new HashSet<SetType>(), SetType.class); 64 test(new HashSet<TreeType>(), TreeType.class); 65 test(new LinkedHashSet<SetType>(), SetType.class); 66 test(new LinkedHashSet<TreeType>(), TreeType.class); 67 try { 68 test(new TreeSet<SetType>(), SetType.class); 69 } 70 catch (Exception e) { 71 System.out.println(e.getMessage()); 72 } 73 try { 74 test(new TreeSet<HashType>(), HashType.class); 75 } 76 catch (Exception e) { 77 System.out.println(e.getMessage()); 78 } 79 } 80 } 81 /* 82 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 83 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 84 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 85 [8, 9, 2, 1, 6, 7, 1, 5, 8, 7, 8, 0, 5, 5, 2, 0, 1, 6, 4, 7, 3, 2, 9, 0, 6, 9, 4, 4, 3, 3] 86 [0, 9, 4, 0, 8, 5, 6, 7, 7, 9, 8, 6, 1, 4, 1, 3, 3, 7, 6, 2, 0, 4, 3, 5, 9, 2, 8, 5, 1, 2] 87 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 88 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 89 java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable 90 java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable 91 *///:~這里不得不提的是hashCode()和Comparable接口這會在下篇博文中進行詳細解釋(【Java心得總結七】Java容器下——Map)
代碼中SetType作為基類,而HashType和TreeType分別對HashSet和TreeSet做了展示(二者分別實現了hashCode()和Comparable接口)
?
總結:
這篇博文對Java容器類庫中的Collection部分做了詳細的闡述,在Set這里還留了個尾巴即hashCode()和Comparable接口的問題(要不篇幅太長了),將在下篇博文進行總結。
?
?
轉載于:https://www.cnblogs.com/xlturing/p/3790064.html
總結
以上是生活随笔為你收集整理的【Java心得总结六】Java容器中——Collection的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wikioi 1017--乘积最大
- 下一篇: 昨天帮同学的学校写了首校歌