thinking-in-java(11) 持有对象
?【11.1】泛型和類型安全的容器
(1)ArrayList<Apple> 中尖括號括起來的是: 類型參數,它指定了這個容器實例可以保存的類型;
【荔枝:有泛型和沒有泛型的區別】
class Apple {private static long counter;private final long id = counter++;public long id() {return id;} } class Orange { } // 沒有泛型的 Apples 和 Oranges 類 public class ApplesAndOrangesWithoutGenerics {@SuppressWarnings("unchecked") // 禁止"不受檢查的異常"的警告信息public static void main(String[] args) {ArrayList apples = new ArrayList();for (int i = 0; i < 3; i++)apples.add(new Apple());apples.add(new Orange());for (int i = 0; i < apples.size(); i++)((Apple) apples.get(i)).id(); // 拋出運行時異常;不能把 Orange 強轉為 Apple的。} } //有泛型的 Apples 和 Oranges 類 public class ApplesAndOrangesWithGenerics {public static void main(String[] args) {ArrayList<Apple> apples = new ArrayList<Apple>();for (int i = 0; i < 3; i++)apples.add(new Apple());// apples.add(new Orange());for (int i = 0; i < apples.size(); i++)System.out.println(apples.get(i).id());// Using foreach:for (Apple c : apples)System.out.println(c.id());} }2)編譯器阻止將 Orange 放置到 apples容器中, 它是一個編譯器錯誤,而不是運行時錯誤;
?【(向上轉型)泛型參數為 基類,可以添加子類對象。基類指針指向子類對象?!?/strong>
class GrannySmith extends Apple {} class Gala extends Apple {} class Fuji extends Apple {} class Braeburn extends Apple {}public class GenericsAndUpcasting {public static void main(String[] args) {// (向上轉型)泛型參數為 基類,可以添加子類對象?;愔羔樦赶蜃宇悓ο蟆rrayList<Apple> apples = new ArrayList<Apple>();apples.add(new GrannySmith()); // GrannySmith 是 Apple的子類apples.add(new Gala()); // Gala 是 Apple的子類apples.add(new Fuji()); // Fuji 是 Apple的子類apples.add(new Braeburn()); // Braeburn 是 Apple的子類for (Apple c : apples)System.out.println(c);} } // 打印結果 chapter11.GrannySmith@2a139a55 // @ 后面的字符串是 對象散列碼的無符號16進制字符串;(Object.hashCode() 函數) chapter11.Gala@15db9742 chapter11.Fuji@6d06d69c chapter11.Braeburn@7852e922 【11.2】基本概念
1)java容器類類庫的用途是 保存對象,有兩種類型的容器;
容器1)Collection:一條記錄保存一個值;
List:必須按照插入的順序保存元素;(ArrayList ?LinkedList) 不管重復與否;
ArrayList:底層實現是數組,查詢速度快;
LinkedList: 底層實現是鏈表,刪除速度快;
Set:不能有重復元素且無序;
HashSet:通過哈希函數獲取函數,是最快的獲取元素的方式;元素的存儲順序看起來并無實際意義(或不關心元素的存儲順序);無序不重復HashSet,底層用HashMap.key 實現;
TreeSet:如果存儲順序重要;它按照比較結果升序保存對象;無序不重復TreeSet,底層用 TreeMap.key 實現;
LinkedHashSet:它按照元素被添加的順序保存對象;?底層用 LinkedHashMap.key 實現;
Queue: 只允許在容器的一端插入對象,并從另外一端移除對象;
容器2)Map:一條記錄保存兩個值,key值和value值,即鍵值對;
HashMap:沒有按照任何明顯的順序保存元素,且通過哈希查找算法保證了查找的快速;
TreeMap:按照比較結果的升序保存鍵;
LinkedHashMap:按照元素的插入順序保存鍵值對,同時還保留了HashMap的查詢速度;底層用? HashMap 實現;
2)Collection接口概括了序列的概念。一種存放一組對象的方式;(Collection 就是是一個對象數組);
// 簡單的容器操作實例 public class SimpleCollection {public static void main(String[] args) {//ArrayList間接實現了 Collection 接口Collection<Integer> c = new ArrayList<Integer>();for (int i = 0; i < 10; i++)c.add(i); // 自動裝箱for (Integer i : c)System.out.print(i + ", ");System.out.println("\n======");//HashSet間接實現了 Collection 接口c = new HashSet<Integer>();for (int i = 0; i < 10; i++)c.add(i); // 自動裝箱for (Integer i : c)System.out.print(i + ", ");} } // 類繼承結構如下: // ArrayList 的類結構層次 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable // AbstractList public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> // List public interface List<E> extends Collection<E> // HashSet 的類結構層次 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable // AbstractSet public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> // Set public interface Set<E> extends Collection<E> // AbstractCollection public abstract class AbstractCollection<E> implements Collection<E> // 打印結果: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ====== 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3)所有 Collection 都可以用到 foreach 語法;
【11.3】添加一組元素
// 添加一組元素荔枝 public class AddingGroups {public static void main(String[] args) {// Arrays.asList() 接受一個數組或用 逗號分隔的元素列表 并轉換為一個 List對象.Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));Integer[] moreInts = { 6, 7, 8, 9, 10 };collection.addAll(Arrays.asList(moreInts));// Collections.addAll() 方法 達到同樣的效果.Collections.addAll(collection, 11, 12, 13, 14, 15);Collections.addAll(collection, moreInts);for(Integer i : collection) {System.out.print(i + ", ");}/* Arrays.asList()方法返回的是 ArrayList */List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);list.set(1, 99); // OK -- modify an elementlist.add(21); // 報錯,因為潛在數組不會改變原有大小.} } // 打印結果: 拋出異常 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractList.add(AbstractList.java:148)at java.util.AbstractList.add(AbstractList.java:108)at chapter11.AddingGroups.main(AddingGroups.java:27) // Arrays.asList() 方法詳情@SafeVarargs@SuppressWarnings("varargs")public static <T> List<T> asList(T... a) {return new ArrayList<>(a);} // Arrays$ArrayList 內部類 /*** @serial include*/private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}@Overridepublic int size() {return a.length;}@Overridepublic Object[] toArray() {return a.clone();}@Override@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();if (a.length < size)return Arrays.copyOf(this.a, size,(Class<? extends T[]>) a.getClass());System.arraycopy(this.a, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}@Overridepublic E get(int index) {return a[index];}@Overridepublic E set(int index, E element) {E oldValue = a[index];a[index] = element;return oldValue;}@Overridepublic int indexOf(Object o) {E[] a = this.a;if (o == null) {for (int i = 0; i < a.length; i++)if (a[i] == null)return i;} else {for (int i = 0; i < a.length; i++)if (o.equals(a[i]))return i;}return -1;}@Overridepublic boolean contains(Object o) {return indexOf(o) != -1;}@Overridepublic Spliterator<E> spliterator() {return Spliterators.spliterator(a, Spliterator.ORDERED);}@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (E e : a) {action.accept(e);}}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);E[] a = this.a;for (int i = 0; i < a.length; i++) {a[i] = operator.apply(a[i]);}}@Overridepublic void sort(Comparator<? super E> c) {Arrays.sort(a, c);}} 【代碼解說】(1)注意區分Collection的成員方法,即addAll() 方法 和 Collections.addAll() 類方法;(干貨——Collections.addAll() 類方法是添加元素的首選方式)
(2)Collection.addAll() 成員方法只能接受另一個Collection對象作為參數,沒有 Arrays.asList() 和 Collections.addAll() 類方法靈活,后面的兩個方法都可以使用 可變參數列表;
(3)可以直接把 Arrays.asList() 的輸出作為輸入, 但其輸出的數組是固定的,不能調整尺寸了;參見 Arrays.asList() 方法的內部調用詳情,它實際上實例化了 Arrays的內部類ArrayList 的一個實例并返回的,且ArrayList是基于數組的,該數組的大小是固定的;
【看個荔枝】
class Snow {} class Powder extends Snow {} class Light extends Powder {} class Heavy extends Powder {} class Crusty extends Snow {} class Slush extends Snow {}public class AsListInference {public static void main(String[] args) {/* Arrays.asList() 方法 產生大小固定的Arrays$ArrayList內部類實例 */List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder()); System.out.println("===snow1===");for (int i = 0; i < snow1.size(); i++) {System.out.println(snow1.get(i));}/* Arrays.asList() 方法 產生大小固定的Arrays$ArrayList內部類實例 */List<Snow> snow2 = Arrays.asList(new Light(), new Heavy()); System.out.println("===snow2===");for (int i = 0; i < snow2.size(); i++) {System.out.println(snow2.get(i));}System.out.println(snow2.getClass().getSimpleName().toLowerCase()); // 輸出的是 arraylistList<Snow> snow3 = new ArrayList<>();/* 推薦用 Collections.addAll() 向 Collection實例中添加元素.( Collections.addAll() 是添加元素的首選方法) */Collections.addAll(snow3, new Light(), new Heavy()); // 向snow3容器添加 后面兩個元素;(可變參數列表)System.out.println("===snow3===");for (int i = 0; i < snow3.size(); i++) {System.out.println(snow3.get(i));}/* (插入了一條線索<Snow>) Arrays.asList() 方法 *//* 告訴 */List<Snow> snow4 = Arrays.<Snow> asList(new Light(), new Heavy()); System.out.println("===snow4===");for (int i = 0; i < snow4.size(); i++) {System.out.println(snow4.get(i));}} } // 打印結果: ===snow1=== chapter11.Crusty@2a139a55 chapter11.Slush@15db9742 chapter11.Powder@6d06d69c ===snow2=== chapter11.Light@7852e922 chapter11.Heavy@4e25154f arraylist snow2 = [chapter11.Light@7852e922, chapter11.Heavy@4e25154f] ===snow3=== chapter11.Light@70dea4e chapter11.Heavy@5c647e05 ===snow4=== chapter11.Light@33909752 chapter11.Heavy@55f96302【11.4】容器的打印
// 常用容器打印荔枝 public class PrintingContainers {static Collection fill(Collection<String> collection) {collection.add("rat1");collection.add("cat2");collection.add("dog3");collection.add("dog3");return collection;}static Map fill(Map<String, String> map) {map.put("rat1", "Fuzzy");map.put("cat2", "Rags2");map.put("dog3", "Bosco3");map.put("dog3", "Spot3");return map;}public static void main(String[] args) {System.out.println("ArrayList = " + fill(new ArrayList<String>()));System.out.println("LinkedList = " + fill(new LinkedList<String>()));System.out.println("HashSet = " + fill(new HashSet<String>())); // 無序不重復HashSet,底層用HashMap.key 實現System.out.println("TreeSet = " + fill(new TreeSet<String>())); // 無序不重復TreeSet,底層用 TreeMap.key 實現;System.out.println("LinkedHashSet = " + fill(new LinkedHashSet<String>())); // 底層用 LinkedHashMap.key 實現;System.out.println("HashMap = " + fill(new HashMap<String, String>()));System.out.println("TreeMap = " + fill(new TreeMap<String, String>()));System.out.println("LinkedHashMap = " + fill(new LinkedHashMap<String, String>())); // 底層用 HashMap 實現} } // 常用容器的打印荔枝: ArrayList = [rat1, cat2, dog3, dog3] LinkedList = [rat1, cat2, dog3, dog3] HashSet = [cat2, dog3, rat1] TreeSet = [cat2, dog3, rat1] LinkedHashSet = [rat1, cat2, dog3] HashMap = {cat2=Rags2, dog3=Spot3, rat1=Fuzzy} TreeMap = {cat2=Rags2, dog3=Spot3, rat1=Fuzzy} LinkedHashMap = {rat1=Fuzzy, cat2=Rags2, dog3=Spot3}【11.5】 List
1)ArrayList: 擅長于隨機訪問元素,但在List的中間插入和刪除元素時速度較慢;
2)LinkedList:擅長于 插入和刪除元素,但隨機訪問元素時速度較慢;
【荔枝-List方法列表】
// List 方法的荔枝 public class ListFeatures {public static void main(String[] args) {Random rand = new Random(47);List<Pet> pets = Pets.arrayList(7);print("1: " + pets);Hamster h = new Hamster();pets.add(h); // 添加元素,自動調整大小print("2: " + pets);print("3: " + pets.contains(h)); // 包含某個元素pets.remove(h); // 移除某個元素.Pet p = pets.get(2);print("4: " + p + " " + pets.indexOf(p)); // 元素的序號.Pet cymric = new Cymric();print("5: " + pets.indexOf(cymric)); // 查找元素的序號,沒有的話,返回-1print("6: " + pets.remove(cymric)); // 沒有該元素,移除的話,返回false// Must be the exact object:print("7: " + pets.remove(p)); // 移除成功print("8: " + pets);pets.add(3, new Mouse()); // Insert at an index, 在序列3上插入一個元素print("9: " + pets);List<Pet> sub = pets.subList(1, 4); // 截取子序列,包括1,不包括4print("10: subList = " + sub);print("10: " + pets.containsAll(sub)); // 是否包含子序列Collections.sort(sub); // In-place sort, 對元素排序print("11: sorted subList = " + sub);// Order is not important in containsAll():print("11: " + pets.containsAll(sub)); // 排序與否 不影響 包含關系Collections.shuffle(sub, rand); // 將元素序號打亂-shuffle-存儲print("12: shuffled subList = " + sub);print("12: " + pets.containsAll(sub)); // 打亂之后 還是 不 影響 包含關系List<Pet> copy = new ArrayList<Pet>(pets);sub = Arrays.asList(pets.get(1), pets.get(4));print("13: sub = " + sub);print("13: copy = " + copy);copy.retainAll(sub); // 求交集print("13: copy.retainAll(sub) 方法求交集并賦值給 copy, copy = " + copy);copy = new ArrayList<Pet>(pets); // 獲得一個全新的 拷貝。注意是全新。print("14: befor copy = " + copy);copy.remove(2); // Remove by index,移除序號為2上的元素print("14: after remove(2), copy = " + copy);print("15: before copy = " + copy);print("15: before sub = " + sub);copy.removeAll(sub); // 移除 copy中 copy 與 sub 的交集元素print("15: after copy.removeAll(sub)-補集: " + copy);copy.set(1, new Mouse()); // 修改某個序號上的元素print("16: copy.set(1, new Mouse())-修改某序號1上的元素, copy = " + copy);copy.addAll(2, sub); // 將子序列中的元素 從 給定序號 插入到 原始序列中print("17: copy.addAll(2, sub)-在序號2上插入sub子集 , copy = " + copy);print("18: pets.isEmpty() = " + pets.isEmpty());pets.clear(); // 刪除所有元素print("19: pets.clear(), pets = " + pets);print("20: pets.isEmpty() = " + pets.isEmpty()); // 序列是否為空pets.addAll(Pets.arrayList(4)); // collection實例的 addAll() 方法 添加元素print("21: after pets.addAll(Pets.arrayList(4)), pets = " + pets);Object[] o = pets.toArray(); // 列表轉為 數組print("22: after Object[] o = pets.toArray(), p[3] = " + o[3]);Pet[] array = new Pet[4];// array = (Pet[]) pets.toArray(); // pets.toArray(array); //print("23: after Pet[] array = new Pet[4], pets.toArray(array), array[3].id() = " + array[3].id());} } 1: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug] 2: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster] 3: true 4: Cymric 2 5: -1 6: false 7: true 8: [Rat, Manx, Mutt, Pug, Cymric, Pug] 9: [Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug] 10: subList = [Manx, Mutt, Mouse] 10: true 11: sorted subList = [Manx, Mouse, Mutt] 11: true 12: shuffled subList = [Mouse, Manx, Mutt] 12: true 13: sub = [Mouse, Pug] 13: copy = [Rat, Mouse, Manx, Mutt, Pug, Cymric, Pug] 13: copy.retainAll(sub) 方法求交集并賦值給 copy, copy = [Mouse, Pug] 14: befor copy = [Rat, Mouse, Manx, Mutt, Pug, Cymric, Pug] 14: after remove(2), copy = [Rat, Mouse, Mutt, Pug, Cymric, Pug] 15: before copy = [Rat, Mouse, Mutt, Pug, Cymric, Pug] 15: before sub = [Mouse, Pug] 15: after copy.removeAll(sub)-補集: [Rat, Mutt, Cymric, Pug] 16: copy.set(1, new Mouse())-修改某序號1上的元素, copy = [Rat, Mouse, Cymric, Pug] 17: copy.addAll(2, sub)-在序號2上插入sub子集 , copy = [Rat, Mouse, Mouse, Pug, Cymric, Pug] 18: pets.isEmpty() = false 19: pets.clear(), pets = [] 20: pets.isEmpty() = true 21: after pets.addAll(Pets.arrayList(4)), pets = [Manx, Cymric, Rat, EgyptianMau] 22: after Object[] o = pets.toArray(), p[3] = EgyptianMau 23: after Pet[] array = new Pet[4], pets.toArray(array), array[3].id() = 14
【代碼解說】
1)equals 方法的重要性: ?retainAll() ?removeAll() ?或 ?contains() ?或 indexof() ?或 remove() 等方法, 都需要用到 equals() 方法;
2)List 方法列表:
2.1)add方法:add(h) , ??add(3, new Mouse()), ?addAll(2, sub), ?
2.2)contains方法:contains(h),??containsAll(sub),
2.3)remove方法:?remove(h),?remove(2),list.removeAll(sub)-求補集方法=>list,??
2.4)get方法:?get(2),?
2.5)查找序號 indexof方法:??indexOf(p),?
2.6)求交集方法retainAll:copy.retainAll(sub)-交集,
2.7)修改元素方法set:?set(1, new Mouse())
2.8)子集方法subList() :???List<Pet> sub = pets.subList(1, 4), ??
2.9)是否為空 isEmpty() 方法:?pets.isEmpty();
2.10)List轉數組方法 toArray :?pets.toArray();
或?Pet[] array = new Pet[4];
// array = (Pet[]) pets.toArray(); //?
pets.toArray(array); //
2.12)清空List所有元素方法 clear:pets.clear(); // 刪除所有元素
2.11)其他方法:
Collections的排序方法sort:?Collections.sort(sub) ,?
Arrays.asList() 數組轉List方法: ??sub = Arrays.asList(pets.get(1), pets.get(4)),?
(Attention——List轉數組 和 數組轉List;交集方法和補集方法)
【11.6】迭代器
1)迭代器的目的: 遍歷并選擇序列中的對象,而不必關心該序列底層的結構;
2)如何遍歷?
iterator() 方法返回一個 Iterator 對象;
next() 方法獲的下一個元素;
hasNext() 檢查序列中是否還有元素;
remove() 把迭代器返回的元素刪除;
【迭代器的荔枝】Iterator 僅能單向移動
// 遍歷 List 等容器的 三種方式 public class SimpleIteration {public static void main(String[] args) {List<Pet> pets = Pets.arrayList(12);Iterator<Pet> it = pets.iterator(); // 迭代器對象while (it.hasNext()) { // 是否還有元素 Pet p = it.next(); // 獲取下一個元素System.out.print(p.id() + ":" + p + " ");}System.out.println();for (Pet p : pets) // foreach 循環System.out.print(p.id() + ":" + p + " ");System.out.println();it = pets.iterator();for (int i = 0; i < 6; i++) { // for 循環it.next();it.remove(); // 調用 remove() 方法前,必須先 調用 next() 元素;}System.out.println(pets); // 輸出 剩下的6個元素,因為當前的游標在 序號6那個位置} } // 打印結果: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster [Pug, Manx, Cymric, Rat, EgyptianMau, Hamster]
(干貨——Iterator 只能單向移動,ListIterator 能夠雙向移動)
1)獲得ListIterator對象: 調用 List 的 listIterator() 方法產生一個指向 List的 ListIterator ,還可以調用 listIterator(n) 創建一個一開始就指向列表索引為 n 的元素處的 ListIterator;
【ListIterator的荔枝】
public class ListIteration { // ListIterator 迭代器可雙向移動.public static void main(String[] args) {List<Pet> pets = Pets.arrayList(8);ListIterator<Pet> it = pets.listIterator();while (it.hasNext()) // 前向移動System.out.print(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex() + "; ");System.out.println();while (it.hasPrevious()) // 后向移動 (所以 ListIterator 是雙向移動)System.out.print(it.previous().id() + " ");System.out.println();System.out.println(pets);// 創建一個 一開始就指向索引 為 3 的元素處的 ListIteraor.it = pets.listIterator(3);while (it.hasNext()) {it.next(); // next()方法是 先加 后 返回 值;it.add(Pets.randomPet()); // 從索引==3處開始添加元素}System.out.println(pets);} } /* public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } public E next() { checkForComodification(); int i = cursor; if (i >= size)throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length)throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; // next()方法是 先加 后 返回 值; } */ // 打印結果: Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7; 7 6 5 4 3 2 1 0 [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx] [Rat, Manx, Cymric, Mutt, Cymric, Pug, Rat, Cymric, EgyptianMau, Pug, Hamster, Manx, EgyptianMau]【11.7】LinkedList
1)基本原理: LinkedList 底層用 鏈表實現,所以它擅長于 元素的移除 和 添加, 但查找元素的速度較慢;
2)LinkedList的特性: 還添加了 可以使其用作棧, 隊列或 雙端隊列的方法;
3)LinkedList 方法列表:
3.1)返回第一個元素: getFirst() 和 element() 方法, peek()方法;如果list 為空,則getFirst() 和 element() 方法拋出異常 NoSuchElementException;peek() 方法不同的是 當列表為空時, 會返回 null;
3.2)移除第一個元素: removeFilrst() 和 remove() 方法, poll() 方法, 如果list 為空, 則 removeFirst() 和 remove() 方法拋出異常 NoSuchElementException;而poll() 不同的是, 當列表為空時 返回 null;
3.3)插入第一個元素: addFirst() 方法;
3.4)插入最后一個元素: offer() 方法 == add()方法 和?addLast() 方法;
3.4)刪除最后一個元素并返回刪除的元素:removeLast() 方法;若list 為null,則拋出異常NoSuchElementException;
【LinkedList 荔枝】
// LinkedList 的方法列表 // 利用LinkedList可實現 棧,隊列,雙端隊列 public class LinkedListFeatures {public static void main(String[] args) {LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));print("0:pets = " + pets);print("1:pets.getFirst() = " + pets.getFirst()); // 獲取 第一個元素,列表為空 拋異常print("1:pets.element() = " + pets.element()); // 獲取 第一個元素, 列表為空 拋異常 print("2:pets.peek() = " + pets.peek()); // 獲取 第一個元素,列表為空 不拋異常 返回nullprint("3:pets.remove() = " + pets.remove()); // 刪除第一個元素并返回刪除的元素,為空拋異常 print("3:pets.removeFirst() = " + pets.removeFirst()); // 刪除第一個元素并返回刪除的元素,為空拋異常print("4:pets.poll() = " + pets.poll()); // 刪除第一個元素,為空不拋異常 返回nullprint("4:pets = " + pets);pets.addFirst(new Rat()); // 在隊首 插入print("5:After pets.addFirst(new Rat()), pets = " + pets);pets.offer(Pets.randomPet()); // 元素進隊, 在隊尾插入print("6:After pets.offer(Pets.randomPet()), pets = " + pets);pets.add(Pets.randomPet()); // 在隊尾插入print("7:After pets.add(Pets.randomPet()), pets = " + pets);pets.addLast(new Hamster()); // 在隊尾插入print("8:After pets.addLast(new Hamster()), pets = " + pets);print("9:pets.removeLast() = " + pets.removeLast()); // 刪除隊尾元素print("9:pets = " + pets);} } // 打印結果: 0:pets = [Rat, Manx, Cymric, Mutt, Pug] 1:pets.getFirst() = Rat 1:pets.element() = Rat 2:pets.peek() = Rat 3:pets.remove() = Rat 3:pets.removeFirst() = Manx 4:pets.poll() = Cymric 4:pets = [Mutt, Pug] 5:After pets.addFirst(new Rat()), pets = [Rat, Mutt, Pug] 6:After pets.offer(Pets.randomPet()), pets = [Rat, Mutt, Pug, Cymric] 7:After pets.add(Pets.randomPet()), pets = [Rat, Mutt, Pug, Cymric, Pug] 8:After pets.addLast(new Hamster()), pets = [Rat, Mutt, Pug, Cymric, Pug, Hamster] 9:pets.removeLast() = Hamster 9:pets = [Rat, Mutt, Pug, Cymric, Pug] 【代碼解說】Queue的實現, 在LinkedList的基礎上 添加了 element(), offer() ?peek(), ?poll(), ?remove() ?等方法;
【11.8】Stack 棧
1)LinkedList 具有能夠直接實現 棧的所有功能的方法,因此可以直接將 LinkedList 作為 棧使用;
2)基于LinkedList 的 Stack實現(強烈推薦)
// Stack 的源碼實現, 不過這不是 java api 提供的,是作者自己寫的 public class Stack<T> {java.util.Stack<Integer> s = null;private LinkedList<T> storage = new LinkedList<T>();// 入棧public void push(T v) {// 在鏈表表頭 插入storage.addFirst(v);}// 返回棧頂元素,僅僅是查看,或偷看,嘿嘿public T peek() {// 獲取 第一個元素,列表為空 拋異常return storage.getFirst(); }// 出棧,表頭出棧;public T pop() {// 刪除第一個元素,為空拋異常return storage.removeFirst();}public boolean empty() {return storage.isEmpty();}public String toString() {return storage.toString();} } /* java.util.Stack 源碼實現(不建議用) */ public class Stack<E> extends Vector<E> {public Stack() {}public E push(E item) {addElement(item);return item;}public synchronized E pop() { // 同步方法,性能較低,已棄用E obj;int len = size();obj = peek();removeElementAt(len - 1);return obj;}public synchronized E peek() { // 同步方法,性能較低,已棄用int len = size();if (len == 0)throw new EmptyStackException();return elementAt(len - 1);}public boolean empty() {return size() == 0;}public synchronized int search(Object o) { // 同步方法,性能較低,已棄用 int i = lastIndexOf(o);if (i >= 0) {return size() - i;}return -1;}/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = 1224463164541339165L; }
【代碼解說】
1)push方法: 元素進棧;
2)peek 和 pop方法: 都是返回棧頂元素,不過 peek僅僅是返回,而pop方法移除并返回棧頂元素;
【java.util.Stack荔枝】
public class StackTest {public static void main(String[] args) {Stack<String> stack = new Stack<String>();for (String s : "My dog has fleas".split(" "))stack.push(s);while (!stack.empty())System.out.print(stack.pop() + " ");} } // 打印結果: fleas has dog My
【11.9】Set 不保存重復元素
1)查找操作: 成了 Set中最重要的操作,因此你通常都會用 HashSet 來實現, 能夠快速查找;
2)Set的基本原理: Set 就是 Collection,只是行為不同而已;Set是基于對象的值來確定歸屬性的;
【Set荔枝】
// HashSet 以最快的查詢速度進行 存儲 (比較) public class SetOfInteger { public static void main(String[] args) {Random rand = new Random(47);Set<Integer> intset = new HashSet<Integer>();for (int i = 0; i < 10000; i++) // 10000次循環也僅有30個元素 intset.add(rand.nextInt(30));intset.add(100); // 無序不重復intset.add(10000); // 無序不重復intset.add(1000); // 無序不重復System.out.println(intset);} } // 打印結果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 10000, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 100, 1000]
3)3種Set對比——HashSet, TreeSet 和 LinkedHashSet 比較
3.1)HashSet:使用散列函數來確定元素的存儲位置 (無序不重復);底層用 HashMap.key 實現 或 LinkedHashMap.key 實現;
3.2)TreeSet:把元素存儲在 紅黑樹數據結構?中(有序不重復);?底層用 TreeMap.key 實現;
3.3)LinkedHashSet:因為查詢速度的原因也使用了散列,但是他也使用了鏈表來維護元素的插入順序,且保留了 哈??焖俨檎业牟檎宜俣?#xff1b;(有序不重復);繼承了?HashSet, 故底層用 LinkedHashMap.key 實現 而沒有用 HashMap.key實現;
【TreeSet荔枝】
// TreeSet 對 輸出結果排序 (比較) public class SortedSetOfInteger {public static void main(String[] args) {Random rand = new Random(47);SortedSet<Integer> intset = new TreeSet<Integer>();for (int i = 0; i < 10000; i++)intset.add(rand.nextInt(30));intset.add(100); // 有序不重復intset.add(10000); // 有序不重復intset.add(1000); // 有序不重復System.out.println(intset);} } // 打印結果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // , 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 100, 1000, 10000]
【Set容器的操作荔枝】
public class SetOperations {public static void main(String[] args) {Set<String> set1 = new HashSet<String>();Collections.addAll(set1, "A B C D E F G H I J K L".split(" "));print("set1 = " + set1);set1.add("M");print("H: set1.contains(\"H\") = " + set1.contains("H")); print("N: set1.contains(\"N\") = " + set1.contains("N")); // set 是否包含 元素Set<String> set2 = new HashSet<String>();Collections.addAll(set2, "H I J K L".split(" "));print("\nset2 = " + set2);print("set2 in set1: set1.containsAll(set2) = " + set1.containsAll(set2)); // set1 是否包含 set2。 。集合的包含。set1.remove("H"); // 異常元素 H print("\nset1 = " + set1);print("set2 in set1: set1.containsAll(set2) = " + set1.containsAll(set2));print("\nbefore: set1 = " + set1);print("before: set2 = " + set2);set1.removeAll(set2); // 移除 set1 和 set2的 交集print("after: set2 removed from set1: set1.removeAll(set2), set1 = " + set1);Collections.addAll(set1, "X Y Z".split(" ")); // 添加數組元素 到 set1.print("\n'X Y Z' added to set1: Collections.addAll(set1, \"X Y Z\".split(\" \")), set1 = " + set1);} } // 打印結果: set1 = [A, B, C, D, E, F, G, H, I, J, K, L] H: set1.contains("H") = true N: set1.contains("N") = falseset2 = [H, I, J, K, L] set2 in set1: set1.containsAll(set2) = trueset1 = [A, B, C, D, E, F, G, I, J, K, L, M] set2 in set1: set1.containsAll(set2) = falsebefore: set1 = [A, B, C, D, E, F, G, I, J, K, L, M] before: set2 = [H, I, J, K, L] after: set2 removed from set1: set1.removeAll(set2), set1 = [A, B, C, D, E, F, G, M]'X Y Z' added to set1: Collections.addAll(set1, "X Y Z".split(" ")), set1 = [A, B, C, D, E, F, G, M, X, Y, Z]【TreeSet荔枝】 /* 將讀取的文件內容分割為多個不重復單詞,并存儲到 TreeSet容器中 */ public class UniqueWords {public static void main(String[] args) {Set<String> words = new TreeSet<String>(new TextFile("src/chapter11/UniqueWords.java", "\\W+"));System.out.println(words);} } // Read a file, split by any regular expression:public TextFile(String fileName, String splitter) {super(Arrays.asList(read(fileName).split(splitter)));// Regular expression split() often leaves an empty// String at the first position:if (get(0).equals(""))remove(0);} // 打印結果(有序集合): [Set, String, System, TextFile, TreeSet, UniqueWords, W, args, chapter11, class, import, java, main, mindview, net, new, out, package, println, public, src, static, util, void, words] 【代碼解說】TreeSet 輸出的結果是有序的,且按照 字典序排序的,因此大寫和小寫字母 劃分到了不同的組中(注意: 大寫字母的ASCII碼 小于 小寫字母的 ASCII 碼);
【如何讓 TreeSet 使用字母序 對 元素進行排序 ?】?
向 TreeSet 構造器傳入 String.CASE_INSENTIVE_ORDER ?比較器;
【把比較器傳入 TreeSet構造器的荔枝】
public class UniqueWordsAlphabetic {public static void main(String[] args) {Set<String> words = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);words.addAll(new TextFile("src/chapter11/SetOperations.java", "\\W+"));System.out.println(words);} } public static final Comparator<String> CASE_INSENSITIVE_ORDER= new CaseInsensitiveComparator(); private static class CaseInsensitiveComparatorimplements Comparator<String>, java.io.Serializable { // 這個比較器 是 String$CaseInsensitiveComparator 是 String類的靜態內部類// use serialVersionUID from JDK 1.2.2 for interoperabilityprivate static final long serialVersionUID = 8575799808933029326L;public int compare(String s1, String s2) {int n1 = s1.length();int n2 = s2.length();int min = Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 = s1.charAt(i);char c2 = s2.charAt(i);if (c1 != c2) {c1 = Character.toUpperCase(c1);c2 = Character.toUpperCase(c2);if (c1 != c2) {c1 = Character.toLowerCase(c1);c2 = Character.toLowerCase(c2);if (c1 != c2) {// No overflow because of numeric promotionreturn c1 - c2;}}}}return n1 - n2;}/** Replaces the de-serialized object. */private Object readResolve() { return CASE_INSENSITIVE_ORDER; }} // 打印結果: [A, add, addAll, added, after, args, B, before, C, chapter11, class, Collections, contains, containsAll, D, E, F, from, G, H, HashSet, I, import, in, J, java, K, L, M, main, mindview, N, nbefore, net, new, nset1, nset2, package, Print, public, remove, removeAll, removed, Set, set1, set2, SetOperations, split, static, String, to, util, void, X, Y, Z]
【總結】字典序和字母序的區別在于:?字典序按照ASCII 碼來比較; 字母序在忽略字母大小寫的基礎上用 ASCII碼來比較;
【11.10】Map
// HashMap(以最快的查找速度進行存儲) 的荔枝. public class Statistics {public static void main(String[] args) {Random rand = new Random(47);Map<Integer, Integer> m = new HashMap<Integer, Integer>();for (int i = 0; i < 10000; i++) {int r = rand.nextInt(20); // 產生20以內的隨機數Integer freq = m.get(r); // 若沒有key 對應的value 則返回 nullm.put(r, freq == null ? 1 : freq + 1); // 發生了對 Integer 的裝箱和拆箱}System.out.println(m);} } // 打印結果: {0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464} 2)Map.containsKey()方法 和?containsValue()方法 // Map.containsKey() 和 Map.containsValue() 的區別 public class PetMap {public static void main(String[] args) {Map<String, Pet> petMap = new HashMap<String, Pet>();petMap.put("My Cat", new Cat("Molly"));petMap.put("My Dog", new Dog("Ginger"));petMap.put("My Hamster", new Hamster("Bosco"));print(petMap);Pet dog = petMap.get("My Dog");print(dog);print(petMap.containsKey("My Dog")); // 是否包含該keyprint(petMap.containsValue(dog)); // 是否包含該value} } // 打印結果: {My Dog=Dog Ginger, My Cat=Cat Molly, My Hamster=Hamster Bosco} Dog Ginger true true 【代碼解說】容器很容易就擴展到 多維, 只需要把值設置到 map中,就可以構成強大的數據結構:最好的證明就是一般框架分前后臺,只要定義好輸入或輸出接口,也就是map的key;則前臺通過json 字符串傳入相應的字段值,然后后臺也通過相應的鍵值獲取value以進行前后臺的數據交互;不管你數據維度多么復雜,都是 a piece of cake. public class MapOfList {public static Map<Person, List<? extends Pet>> petPeople = new HashMap<Person, List<? extends Pet>>();static {petPeople.put(new Person("Dawn-1"),Arrays.asList(new Cymric("Molly1-1"), new Mutt("Spot1-2")));petPeople.put(new Person("Kate-2"), Arrays.asList(new Cat("Shackleton2-1"),new Cat("Elsie May2-2"), new Dog("Margrett2-3")));petPeople.put(new Person("Marilyn-3"), Arrays.asList(new Pug("Louie aka Louis Snorkelstein Dupree3-1"), new Cat("Stanford aka Stinky el Negro3-2"), new Cat("Pinkola3-3")));petPeople.put(new Person("Luke-4"),Arrays.asList(new Rat("Fuzzy4-1"), new Rat("Fizzy4-2")));petPeople.put(new Person("Isaac-5"), Arrays.asList(new Rat("Freckly5-1")));}public static void main(String[] args) {print("petPeople.keySet() = " + petPeople.keySet()); // 鍵setprint("petPeople.values() = " + petPeople.values()); // 值setprint("petPeople.entrySet() = " + petPeople.entrySet()); // 鍵值對setfor (Person person : petPeople.keySet()) {print(person + " has:"); // map.keyfor (Pet pet : petPeople.get(person)) // map.value 是一個 list,遍歷整個listprint(" " + pet);}} } // 打印結果: petPeople.keySet() = [Person Luke-4, Person Marilyn-3, Person Kate-2, Person Isaac-5, Person Dawn-1] petPeople.values() = [[Rat Fuzzy4-1, Rat Fizzy4-2], [Pug Louie aka Louis Snorkelstein Dupree3-1, Cat Stanford aka Stinky el Negro3-2, Cat Pinkola3-3], [Cat Shackleton2-1, Cat Elsie May2-2, Dog Margrett2-3], [Rat Freckly5-1], [Cymric Molly1-1, Mutt Spot1-2]] petPeople.entrySet() = [Person Luke-4=[Rat Fuzzy4-1, Rat Fizzy4-2], Person Marilyn-3=[Pug Louie aka Louis Snorkelstein Dupree3-1, Cat Stanford aka Stinky el Negro3-2, Cat Pinkola3-3], Person Kate-2=[Cat Shackleton2-1, Cat Elsie May2-2, Dog Margrett2-3], Person Isaac-5=[Rat Freckly5-1], Person Dawn-1=[Cymric Molly1-1, Mutt Spot1-2]] Person Luke-4 has:Rat Fuzzy4-1Rat Fizzy4-2 Person Marilyn-3 has:Pug Louie aka Louis Snorkelstein Dupree3-1Cat Stanford aka Stinky el Negro3-2Cat Pinkola3-3 Person Kate-2 has:Cat Shackleton2-1Cat Elsie May2-2Dog Margrett2-3 Person Isaac-5 has:Rat Freckly5-1 Person Dawn-1 has:Cymric Molly1-1Mutt Spot1-2
【代碼解說】:map 可以返回她的鍵set 或 值 set,或鍵值對set;
【11.11】 Queue 隊列
1)隊列基礎知識:隊列是一個容器,從隊列的一端插入元素,從另一端取出元素,并且插入順序和取出順序是相同的;
2)LinkedList 支持 Queue:LinkedList 提供了方法以支持隊列的行為, LinkedList 實現了 Queue 接口,故LinkedList 可以作為 Queue來使用;
【Queue 由 LinkedList 來實現的 荔枝】
/* public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E> */ // Queue 由 LinkedList 來實現的 荔枝 public class QueueDemo {public static void printQ(Queue queue) {while (queue.peek() != null) // peek() 獲取第一個元素,列表為空,返回null,不會拋異常System.out.print(queue.remove() + " ");System.out.println();}public static void main(String[] args) {// LinkedList 是 Queue的子類// 確切地說, LinkedList 是 Deque 的實現類,而Deque 是 Queue的子類Queue<Integer> queue = new LinkedList<Integer>();Random rand = new Random(47);for (int i = 0; i < 10; i++)queue.offer(rand.nextInt(i + 10)); // offer() == add() 方法,元素進隊,在隊尾插入printQ(queue);Queue<Character> qc = new LinkedList<Character>();for (char c : "Brontosaurus".toCharArray())qc.offer(c);printQ(qc);LinkedList<String> mylist = new LinkedList<>(); // 容器為空,因為沒有元素mylist.element(); // LinkedList.element() == getFirst() 方法;為空拋異常} } // 打印結果: 8 1 1 1 5 14 3 1 0 1 B r o n t o s a u r u s Exception in thread "main" java.util.NoSuchElementExceptionat java.util.LinkedList.getFirst(LinkedList.java:244)at java.util.LinkedList.element(LinkedList.java:663)at chapter11.QueueDemo.main(QueueDemo.java:33) 3)Queue 方法列表,和 LinkedList 方法雷同;?1)插入尾部:offer() == add() 方法
2)不刪除頭部并返回頭部: peek() 當queue為空返回空, element() ==getFirst() 拋出?NoSuchElementException;
3)刪除頭部并返回頭部: poll() 當queue為空返回空, remove() 拋出NoSuchElementException;
【11.11.1】 PriorityQueue 優先級隊列
1)優先級隊列: 聲明下一個彈出元素是最需要的元素(具有最高的優先級);
【荔枝】優先級隊列 PriorityQueue 荔枝(默認優先級排序規則 和 自定義優先級排序規則)
// 優先級隊列 PriorityQueue 荔枝(默認優先級排序規則 和 自定義優先級排序規則) public class PriorityQueueDemo {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();Random rand = new Random(47);for (int i = 0; i < 10; i++)priorityQueue.offer(rand.nextInt(i + 10)); // 入隊,插入隊尾QueueDemo.printQWithStr("默認是自然排序 == ASCII排序規則, priorityQueue = ", priorityQueue); // 默認是自然排序 == ASCII排序規則List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2,3, 9, 14, 18, 21, 23, 25); // 不可變的列表priorityQueue = new PriorityQueue<Integer>(ints);QueueDemo.printQWithStr("默認是自然排序 == ASCII排序規則, priorityQueue = ", priorityQueue);// 自定義排序規則priorityQueue = new PriorityQueue<Integer>(ints.size(),Collections.reverseOrder()); // 優先級逆序隊列.priorityQueue.addAll(ints); // 添加整個List 元素QueueDemo.printQWithStr(" 自定義排序規則 == 優先級逆序隊列 Collections.reverseOrder(), priorityQueue = ", priorityQueue);String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";List<String> strings = Arrays.asList(fact.split("")); // 注意這種分割方式和分割結果PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);QueueDemo.printQWithStr("默認是自然排序 == ASCII排序規則, stringPQ = ", stringPQ);// 自定義排序規則stringPQ = new PriorityQueue<String>(strings.size(),Collections.reverseOrder()); // 優先級逆序隊列.stringPQ.addAll(strings);QueueDemo.printQWithStr(" 自定義排序規則 == 優先級逆序隊列 Collections.reverseOrder(), stringPQ = ", stringPQ);Set<Character> charSet = new HashSet<Character>();for (char c : fact.toCharArray())charSet.add(c); // Autoboxing 自動裝箱PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet);QueueDemo.printQWithStr("HashSet構造的PriorityQueue,并ASCII排序規則后, characterPQ = ", characterPQ);} } // 打印結果; 默認是自然排序 == ASCII排序規則, priorityQueue = 0 1 1 1 1 1 3 5 8 14 (優先級重復) 默認是自然排序 == ASCII排序規則, priorityQueue = 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 (優先級重復)自定義排序規則 == 優先級逆序隊列 Collections.reverseOrder(), priorityQueue = 25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1 (同上) 默認是自然排序 == ASCII排序規則, stringPQ = A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W (同上)自定義排序規則 == 優先級逆序隊列 Collections.reverseOrder(), stringPQ = W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A HashSet構造的PriorityQueue,并ASCII排序規則后, characterPQ = A B C D E F H I L N O S T U W (優先級不重復)
【11.12】Collection 和 Iterator
1)Collection 是描述所有容器共性的根接口。 容器之間的共性都是通過 迭代器來達成的。
2)要實現 Collection 接口就需要 提供Iterator() 方法;
【荔枝】容器的兩種遍歷方式
// 容器的兩種遍歷方式: 接口遍歷 和 迭代器遍歷(其遍歷方式 與 底層容器 解耦合). public class InterfaceVsIterator {public static void display(Iterator<Pet> it) { // 參數是 Iteraotr 迭代器類型 while (it.hasNext()) {Pet p = it.next();System.out.print(p.id() + ":" + p + " ");}System.out.println();}public static void display(Collection<Pet> pets) { // 參數是 Collection類型for (Pet p : pets) System.out.print(p.id() + ":" + p + " ");System.out.println();}public static void main(String[] args) {List<Pet> petList = Pets.arrayList(8);Set<Pet> petSet = new HashSet<Pet>(petList);Map<String, Pet> petMap = new LinkedHashMap<String, Pet>();String[] names = ("Ralph, Eric, Robin, Lacey, "+ "Britney, Sam, Spot, Fluffy").split(", ");for (int i = 0; i < names.length; i++)petMap.put(names[i], petList.get(i));display(petList); // 通過 Collection接口 遍歷display(petSet); // 通過 Collection接口 遍歷display(petList.iterator()); // 通過 迭代器 遍歷display(petSet.iterator()); // 通過 迭代器 遍歷System.out.println(petMap);System.out.println(petMap.keySet());display(petMap.values()); // 通過 Collection接口 遍歷display(petMap.values().iterator()); // 通過 迭代器 遍歷} } // 打印結果 : 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx {Ralph=Rat, Eric=Manx, Robin=Cymric, Lacey=Mutt, Britney=Pug, Sam=Cymric, Spot=Pug, Fluffy=Manx} [Ralph, Eric, Robin, Lacey, Britney, Sam, Spot, Fluffy] 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 【其他解說】
當實現一個 Collection時,盡量使用 迭代器的方式去遍歷,而不是使用 Colleciton 的 foreach 循環遍歷;
【實現Collection的兩種方式】
方式1)繼承?AbstractCollection;
方式2)實現?Iterable接口;
【方式1:繼承AbstractCollection的荔枝(不推薦,因為如果實現類必須繼承其他父類的話,那豈不是很難堪嗎?)】
// 通過繼承 AbstractCollection 的方式實現Collection(1) public class CollectionSequence extends AbstractCollection<Pet> {private Pet[] pets = Pets.createArray(8);public int size() {return pets.length;}public Iterator<Pet> iterator() {return new Iterator<Pet>() {// 通過匿名內部類實現 迭代器設計模式private int index = 0;public boolean hasNext() {return index < pets.length;}public Pet next() {return pets[index++];}public void remove() { // Not implementedthrow new UnsupportedOperationException();}};}public static void main(String[] args) {CollectionSequence c = new CollectionSequence();InterfaceVsIterator.display(c);InterfaceVsIterator.display(c.iterator());} }
// 打印結果: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx // 容器的兩種遍歷方式: 接口遍歷 和 迭代器遍歷(其遍歷方式 與 底層容器 解耦合). public class InterfaceVsIterator {// 參數是 Iteraotr 迭代器類型 (1 首選)public static void display(Iterator<Pet> it) {while (it.hasNext()) {Pet p = it.next();System.out.print(p.id() + ":" + p + " ");}System.out.println();}// 參數是 Collection類型(不推薦,因為有可能無法通過繼承AbstractCollection來實現Collection )public static void display(Collection<Pet> pets) { for (Pet p : pets) System.out.print(p.id() + ":" + p + " ");System.out.println();}
【方式2:實現?Iterable接口的荔枝(強力推薦,特別當實現類繼承了其他父類而無法繼承 AbstractCollection 的時候)】
class PetSequence {protected Pet[] pets = Pets.createArray(8); }// 實現一個不是 Collection 的外部類. 方式2: 實現 Iterable接口. // 主要是為了要創建一個 自定義的 迭代器來 遍歷 Collection 中的元素. public class NonCollectionSequence extends PetSequence implements Iterable<Pet>{@Overridepublic Iterator<Pet> iterator() {return new Iterator<Pet>() {private int index = 0;public boolean hasNext() {return index < pets.length;}public Pet next() {return pets[index++];}public void remove() { // Not implementedthrow new UnsupportedOperationException();}};}public static void main(String[] args) {NonCollectionSequence nc = new NonCollectionSequence();InterfaceVsIterator.display(nc.iterator());} } // 打印結果: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
方式1)迭代器的方式:參數是 Iteraotr 迭代器類型 (1 首選);
方式2)foreach 循環:參數是 Collection類型(不推薦,因為有可能無法通過繼承AbstractCollection來實現Collection );
【Collection實現類的總結】當然了,條件允許的話,你既可以繼承?AbstractCollection 也可以 實現?Iterable 接口,以利于代碼維護;
【11.13】foreach 與 迭代器
1)foreach 遍歷: foreach 不僅用于數組,也可以用于 Collection 的任何容器;
【foreach遍歷容器的荔枝】
// foreach 工作的 荔枝. public class ForEachCollections {public static void main(String[] args) {Collection<String> cs = new LinkedList<String>();Collections.addAll(cs, "Take the long way home".split(" "));for (String s : cs)System.out.print("'" + s + "' ");} } // 打印結果: 'Take' 'the' 'long' 'way' 'home'
2)foreach 工作原理: 因為 java se5 ?引入了 Iterable 接口, 該接口包含一個能夠產生 Iterator 的 iterator() 方法, 并且 Iterable 接口被 foreach 用來在 序列中移動。如果實現類實現了 Iterable,則都可以用 foreach 去遍歷 該實現類的對象數組, 或以該實現類作為泛型的容器;
【foreach 遍歷的荔枝(這個荔枝非常重要)】
// 只要創建了任何實現 Iterable的類,都可以將它用于 foreach 語句中. public class IterableClass implements Iterable<String> {protected String[] words = ("And that is how "+ "we know the Earth to be banana-shaped.").split(" ");public Iterator<String> iterator() {/* 返回一個匿名內部類 */return new Iterator<String>() {private int index = 0;public boolean hasNext() {return index < words.length;}public String next() {return words[index++];}public void remove() { // Not implemented/* 注意這里拋出的異常類型UnsupportedOperationException */throw new UnsupportedOperationException();}};}public static void main(String[] args) {for (String s : new IterableClass())System.out.print(s + " ");System.out.println();} } // 打印結果: And that is how we know the Earth to be banana-shaped.
【foreach 用于遍歷數組】
// foreach 語句也可以 應用于 遍歷數組 public class ArrayIsNotIterable {static <T> void test(Iterable<T> ib) {for (T t : ib)System.out.print(t + " ");}public static void main(String[] args) {test(Arrays.asList(1, 2, 3));String[] strings = { "A", "B", "C" };// An array works in foreach, but it's not Iterable:// test(strings);// You must explicitly convert it to an Iterable:test(Arrays.asList(strings));} } //打印結果: 1 2 3 A B C 【代碼解說】 在java se5中,大量的類都是 Iterable類型,主要包括 所有的 Collection,但是肯定不包括 Map 類型;
【foreach荔枝--打印操作系統環境變量】
/* 打印操作系統環境變量 */ public class EnvironmentVariables {public static void main(String[] args) {for (Map.Entry entry : System.getenv().entrySet()) { // entry 是一個 SetSystem.out.println(entry.getKey() + ": " + entry.getValue());}} } // 打印結果: LOCALAPPDATA: C:\Users\pacoson\AppData\Local PROCESSOR_LEVEL: 6 FP_NO_HOST_CHECK: NO USERDOMAIN: pacoson-PC LOGONSERVER: \\PACOSON-PC JAVA_HOME: D:\java\jdk1.8.0_91 SESSIONNAME: Console ALLUSERSPROFILE: C:\ProgramData PROCESSOR_ARCHITECTURE: AMD64 PSModulePath: C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ SystemDrive: C: APPDATA: C:\Users\pacoson\AppData\Roaming USERNAME: pacoson windows_tracing_logfile: C:\BVTBin\Tests\installpackage\csilogfile.log ProgramFiles(x86): C:\Program Files (x86) CommonProgramFiles: C:\Program Files\Common Files Path: C:\ProgramData\Oracle\Java\javapath;.;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\java\jdk1.8.0_91\bin;D:\Program Files (x86)\MySQL\MySQL Server 5.1\bin;C:\Program Files (x86)\MySQL\MySQL Server 5.1\bin;E:\cmb\elasticsearch\elasticsearch-5.2.0\bin;E:\cmb\elasticsearch\kibana-5.2.0-windows-x86\bin; PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC OS: Windows_NT windows_tracing_flags: 3 COMPUTERNAME: PACOSON-PC PROCESSOR_REVISION: 3c03 CLASSPATH: .;D:\java\jdk1.8.0_91\lib\tools.jar;D:\java\jdk1.8.0_91\lib\dt.jar; CommonProgramW6432: C:\Program Files\Common Files ComSpec: C:\Windows\system32\cmd.exe ProgramData: C:\ProgramData ProgramW6432: C:\Program Files HOMEPATH: \Users\pacoson SystemRoot: C:\Windows TEMP: C:\Users\pacoson\AppData\Local\Temp HOMEDRIVE: C: PROCESSOR_IDENTIFIER: Intel64 Family 6 Model 60 Stepping 3, GenuineIntel USERPROFILE: C:\Users\pacoson TMP: C:\Users\pacoson\AppData\Local\Temp CommonProgramFiles(x86): C:\Program Files (x86)\Common Files ProgramFiles: C:\Program Files PUBLIC: C:\Users\Public NUMBER_OF_PROCESSORS: 4 windir: C:\Windows =::: ::\ 【注意】不存在任何 從數組到 Iterable類型的 自動轉換,你必須手動轉換;
【11.13.1】適配器方法慣用法,適配器設計模式
1)添加一個能夠產生 Iterable對象的方法:
【反向迭代器的荔枝】
// ArrayList的子類 class ReversibleArrayList<T> extends ArrayList<T> {public ReversibleArrayList(Collection<T> c) {super(c);}/* 添加一個能夠產生 Iterable對象的方法 */public Iterable<T> reversed() {return new Iterable<T>() { // 匿名內部類// 這里會產生一個 反向迭代器public Iterator<T> iterator() {return new Iterator<T>() {int current = size() - 1;public boolean hasNext() {return current > -1;}public T next() {return get(current--);}public void remove() { // Not implementedthrow new UnsupportedOperationException();}};}};} } public class AdapterMethodIdiom {public static void main(String[] args) {ReversibleArrayList<String> ral = new ReversibleArrayList<String>(Arrays.asList("To be or not to be".split(" ")));for (String s : ral) // foreach 循環遍歷System.out.print(s + " ");System.out.println();for (String s : ral.reversed()) // 反向迭代遍歷System.out.print(s + " ");} } // 打印結果: // To be or not to be // be to not or be To
【多種迭代器方式的荔枝】
【荔枝--Arrays.asList()返回的List 是否用 ArrayList 進行包裝的 區別】 // Arrays.asList()返回的List 是否用 ArrayList 進行包裝的 區別. public class ModifyingArraysAsList {public static void main(String[] args) {Random rand = new Random(47);Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));/* ArrayList的構造函數首先會拷貝數組,所以List引用的數組與原數組不是同一個數組 */System.out.println("Before shuffling: list1 = " + list1);Collections.shuffle(list1, rand); System.out.println("After shuffling: Collections.shuffle(list1, rand), list1 = " + list1);System.out.println("array: Arrays.toString(ia) = " + Arrays.toString(ia));System.out.println("\n================================================\n");List<Integer> list2 = Arrays.asList(ia);/* 沒有使用 ArrayList 構造函數,所以 list2 引用的數組 與 原數組 ia 是同一個數組 */System.out.println("Before shuffling: " + list2);Collections.shuffle(list2, rand);System.out.println("After shuffling: " + list2);System.out.println("array: " + Arrays.toString(ia));} } // 打印結果 : Before shuffling: list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] After shuffling: Collections.shuffle(list1, rand), list1 = [4, 6, 3, 1, 8, 7, 2, 5, 10, 9] array: Arrays.toString(ia) = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]================================================Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8] array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
【11.14】總結
總結
以上是生活随笔為你收集整理的thinking-in-java(11) 持有对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 截取反斜杠--java使用sp
- 下一篇: DevExperience(1710)