如果現在有一個Iterable類,你想要添加一種或多種在foreach語句中使用這個類的方法,例如方向迭代,應該怎么做呢? 如果之間繼承這個類,並且覆蓋iterator()方法,你只能替換現有的方法,而不能實現選擇
一種解決方案是所謂的adapter方法的慣用法,"適配器"部分來自於設計模式,因為你必須提供特定接口以滿足foreach語句,當你有一個接口並需要另一個接口時,編寫adapter就可以解決問題,這里,希望在默認的前向迭代器的基礎上,添加方向迭代器的能力,因此不能使用覆蓋,而是添加了一個能夠產生Iterable對象的方法,該對象可以用於foreach語句,正如你所見,可以提供多種使用foreach的方式
package object; //: holding/AdapterMethodIdiom.java // The "Adapter Method" idiom allows you to use foreach // with additional kinds of Iterables. import java.util.*; class ReversibleArrayList<T> extends ArrayList<T> { public ReversibleArrayList(Collection<T> c) { super(c); } public Iterable<T> reversed() {//返回一個具有反向迭代器的Iterable 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 implemented throw 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(" "))); // Grabs the ordinary iterator via iterator(): for(String s : ral) System.out.print(s + " "); System.out.println(); // Hand it the Iterable of your choice for(String s : ral.reversed()) System.out.print(s + " "); } } /* Output: To be or not to be be to not or be To *///:~
通過這種方法,可以在IterableClass.java的示例中添加兩種適配器方法
package object; //: holding/MultiIterableClass.java // Adding several Adapter Methods. import java.util.*; public class MultiIterableClass extends IterableClass { public Iterable<String> reversed() { return new Iterable<String>() { public Iterator<String> iterator() { return new Iterator<String>() { int current = words.length - 1; public boolean hasNext() { return current > -1; } public String next() { return words[current--]; } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } }; } public Iterable<String> randomized() { return new Iterable<String>() { public Iterator<String> iterator() { List<String> shuffled = new ArrayList<String>(Arrays.asList(words)); Collections.shuffle(shuffled, new Random(47));//// return shuffled.iterator(); } }; } public static void main(String[] args) { MultiIterableClass mic = new MultiIterableClass(); for(String s : mic.reversed()) System.out.print(s + " "); System.out.println(); for(String s : mic.randomized()) System.out.print(s + " "); System.out.println(); for(String s : mic) System.out.print(s + " "); } } /* Output: banana-shaped. be to Earth the know we how is that And is banana-shaped. Earth that how the be And we know to And that is how we know the Earth to be banana-shaped. *///:~
Collections方法並沒有打亂原來的數組,而是打亂了shuffled中的引用,之所以這樣,只是因為,randmize()方法用一個ArrayList將Arrays.asList()結果包裝了起來
如果這個有Arrays.asList()產生的List被直接打亂,那么就會修改底層是數組,就像下面這樣;
package object; //: holding/ModifyingArraysAsList.java import java.util.*; 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));//ia的輸出被傳給了ArrayList構造器,這將創建一個ia的元素引用的ArrayList System.out.println("Before shuffling: " + list1); Collections.shuffle(list1, rand); System.out.println("After shuffling: " + list1); System.out.println("array: " + Arrays.toString(ia)); List<Integer> list2 = Arrays.asList(ia);//lsit2 直接使用Arrays.asList(ia),這樣做會直接修改ia的數據 System.out.println("Before shuffling: " + list2); Collections.shuffle(list2, rand); System.out.println("After shuffling: " + list2); System.out.println("array: " + Arrays.toString(ia)); } } /* Output: Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] After shuffling: [4, 6, 3, 1, 8, 7, 2, 5, 10, 9] array: [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] *///:~
意識到Arrays.asLIst()產生的對象會使用底層數組作為其物理實現是很重要的,只要你執行的操作會修改這個List,並且你不想原來的數組被修改那么你就應該在另一個容器中創建副本