John Jiang

          a cup of Java, cheers!
          https://github.com/johnshajiang/blog

             :: 首頁 ::  :: 聯系 :: 聚合  :: 管理 ::
            131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
          你所不知道的五件事情--Java集合框架API(第一部分)
                                                            --定制與擴展Java集合框架
          這是Ted NewardIBM developerWorks5 things系列文章中的一篇,講述了關于Java集合框架的一些應用竅門,值得大家學習。(2010.05.28最后更新)

              概要:Java集合API遠不止是數組的替代品,盡管那是一個不壞的認知起點。Ted Neward展示了5個能更大利用集合框架的竅門,包含一個定制并擴展Java集合API的入門級應用。

              對于許多Java開發者而言,Java集合API是標準的Java數組及其全部缺點的必要替代品。將集合框架主要與ArrayList聯系起來并不是一個錯誤,但集合框架中還有許多需要關注的地方。
              同樣地,盡管對于名-值或鍵-值對來說,Map(及其它的常選實現HashMap)是極好的,但仍沒理由把自己限制在這些熟悉的工具中。你可使用正確的 API,甚至是正確的集合API去修正許多存在潛在錯誤的代碼。
              本文是5 things系列的第二篇文章,也是針對集合框架若干篇文章中的第一篇,因為在我們進行Java編程時,集合框架處于核心地位。開始時,將會看到處理常見工作,例如將Array轉換成List,的便捷(但并不是最通用的)方法。之后,我們將深入研究集合框架中不太常見的主題,例如擴展Java集合框架API并定制一個集合類.

          1. 使用集合對象處理數組
              新接觸Java技術的開發者可能不知道數組是天然包含在Java語言中的,這是為了應對上世紀九十年代早期來自于C++開發者們有關性能的批評。是的,從那以后,我們已經歷了很長時間,當與Java集合類庫相比較時,數組的性能優勢正在變小。
              例如,將數組中的內容導出成一個字符串時,需要遍歷數組,然后將其中的內容連接成一個String類型;然而,集合框架的實現都有其各自不同的 toString實現。
          除了極少數情況,一種好的實踐方式就是盡快將任何數組轉換成集合對象。這就提出了一個問題,有什么最簡單的方法來做這種轉換呢?事實證明,Java集合框架API使這一過程變得簡單,如清單1所示:

          清單1. ArrayToList
          import java.util.*;

          public class ArrayToList
          {
              
          public static void main(String[] args)
              {
                  
          // This gives us nothing good
                  System.out.println(args);
                  
                  
          // Convert args to a List of String
                  List<String> argList = Arrays.asList(args);
                  
                  
          // Print them out
                  System.out.println(argList);
              }
          }

          注意,返回的List是不可修改的,所以當試圖向其中添加新的元素時,將會拋出UnsupportedOperationException。
              因為Arrays.asList方法對添加到List中的元素使用vararg(可變長參數),你就可使用它輕松地創建被新建對象的List對象。

          2. 迭代的效率不高
              將一個集合對象(特別是該集合是由一數組轉化而成的)中的內容移入到另一個集合對象,或是從一個較大對象集合中刪除一個較小的對象集合,這些都是不常見的需求。
              你可能會嘗試著迭代該集合,然后向其中添加或刪除每一個被找到的元素,但不能這樣做。
              在這個例子中,迭代有很大的缺點:
          • 在每一次添加或刪除動作之后,無法高效地重新調整集合的大小。
          • 為了進行這些操作,在獲取和釋放鎖時會有潛在的并發性夢魘。
          • 當正在進行添加或刪除元素時,其它線程也在操作你的集合,這就會導致競爭條件。
          針對你想添加或刪除元素的集合對象,使用addAll或removeAll方法,你就能避免上述所有問題。

          3. 利用for循環遍歷Iterable對象

              在Java 5中加入的最好的Java語言便捷特性之一,改進的for循環,消除了操作Java集合對象的最后一道障礙。
              在此之前,開發者們必須得手工地獲取Iterator,使用next()方法去獲得Iterator中被指定的對象,以及通過hasNext()方法來檢查是否還有更多的對象。在Java 5之后,我們可以方便地使用for-loop變體來默默地處理上述所有操作。
              準確地說,這種改進并不僅僅針對集合對象,而可用于任何實現了Iterable接口的對象。
              清單2展示了如何將Person對象中的children作為一個Iterator去制成一個列表。不同于操作一個指向內部List的引用(這會允許 Person的外部調用者向你的家庭中添加新的子女--大多數父母會對此感到不快的),Person類型實現了Iterable接口。該方法也能使改進的 for循環可遍歷其中的孩子列表。

          清單2. 改進的for循環:列出你的子女
          // Person.java
          import java.util.*;

          public class Person
              
          implements Iterable<Person>
          {
              
          public Person(String fn, String ln, int a, Person kids)
              {
                  
          this.firstName = fn; this.lastName = ln; this.age = a;
                  
          for (Person child : kids)
                      children.add(child);
              }
              
          public String getFirstName() { return this.firstName; }
              
          public String getLastName() { return this.lastName; }
              
          public int getAge() { return this.age; }
              
              
          public Iterator<Person> iterator() { return children.iterator(); }
              
              
          public void setFirstName(String value) { this.firstName = value; }
              
          public void setLastName(String value) { this.lastName = value; }
              
          public void setAge(int value) { this.age = value; }
              
              
          public String toString() {
                  
          return "[Person: " +
                      
          "firstName=" + firstName + " " +
                      
          "lastName=" + lastName + " " +
                      
          "age=" + age + "]";
              }
              
              
          private String firstName;
              
          private String lastName;
              
          private int age;
              
          private List<Person> children = new ArrayList<Person>();
          }

          // App.java
          public class App
          {
              
          public static void main(String[] args)
              {
                  Person ted 
          = new Person("Ted""Neward"39,
                      
          new Person("Michael""Neward"16),
                      
          new Person("Matthew""Neward"10));

                  
          // Iterate over the kids
                  for (Person kid : ted)
                  {
                      System.out.println(kid.getFirstName());
                  }
              }
          }

          對于域模型,使用Iterable接口會有一些明顯的缺點,因為只有一個對象集合能夠通過iterator()方法得到如此"隱式"的支持。在這種情況下,children集合在何處是確定且透明的,然而,Iterable接口可使程序設計在應對域類型時要容易得多,也會更明顯。

          4. 典型算法與定制算法
              你曾經會想過遍歷一個集合對象,但是從相反的方向呢?這就是典型的Java集合框架算法用得上的地方。
              上述清單2中Person的子女以他們被添加的順序來排列的;但現在你希望以相反的順序列出這些子女。當你編寫另一個for循環,以相反的順序向各個對象添加到一個新的ArrayList中時,代碼在編寫了第三或第四遍之后將變得冗長。
              清單3就是未被利用的算法:

          清單3. ReverseIterator
          public class ReverseIterator
          {
              
          public static void main(String[] args)
              {
                  Person ted 
          = new Person("Ted""Neward"39,
                      
          new Person("Michael""Neward"16),
                      
          new Person("Matthew""Neward"10));

                  
          // Make a copy of the List
                  List<Person> kids = new ArrayList<Person>(ted.getChildren());
                  
          // Reverse it
                  Collections.reverse(kids);
                  
          // Display it
                  System.out.println(kids);
              }
          }

          Collections類有一組這樣的"算法",該類所實現的靜態方法將Collections作為參數,并且提供了完全獨立于任何實現的行為。
          另外,Collections類所示的算法在好的API設計中肯定不會是最終版本--例如,我不希望直接修改這些方法(由集合對象傳入)的內容。你可以為自己編寫定制的算法,這是一件很好的事情,如清單4所示:

          清單4. 簡潔的反轉迭代器
          class MyCollections
          {
              
          public static <T> List<T> reverse(List<T> src)
              {
                  List
          <T> results = new ArrayList<T>(src);
                  Collections.reverse(results);
                  
          return results;
              }
          }

          5. 擴展的集合框架API
              上述定制的算法證明了Java集合API的最后一個問題:Java集合框架一直被設計為是可擴展的,可被改變以去適應開發者的特殊目的。
              例如,假設你需要Person中的children總是按年齡排序。雖然你可以一遍一遍地編寫代碼去對children進行排序(可能會使用 Collections.sort方法),要是有一個集合類能夠幫你作這樣的排序則會好得多。
              實際上,你可能并不關心添加到集合中的對象是以何種順序存儲的(這就是使用List的主要理由),你只是想讓它們保持一定的順序罷了。
              java.util包中沒有哪一個集合類能滿足這些需求,寫一個卻很容易。你所需要做的只是創建一個接口,該接口描述了該集合類需要提供的抽象行為。在 SortedCollection這個例子中,上述意圖是完全可行的。

          清單5. SortedCollection
          public interface SortedCollection<E> extends Collection<E>
          {
              
          public Comparator<E> getComparator();
              
          public void setComparator(Comparator<E> comp);
          }

          基本上是虎頭蛇尾般地寫成了這個新接口的實現:

          清單6. ArraySortedCollection
          import java.util.*;

          public class ArraySortedCollection<E>
              
          implements SortedCollection<E>, Iterable<E>
          {
              
          private Comparator<E> comparator;
              
          private ArrayList<E> list;
                  
              
          public ArraySortedCollection(Comparator<E> c)
              {
                  
          this.list = new ArrayList<E>();
                  
          this.comparator = c;
              }
              
          public ArraySortedCollection(Collection<? extends E> src, Comparator<E> c)
              {
                  
          this.list = new ArrayList<E>(src);
                  
          this.comparator = c;
                  sortThis();
              }

              
          public Comparator<E> getComparator() { return comparator; }
              
          public void setComparator(Comparator<E> cmp) { comparator = cmp; sortThis(); }
              
              
          public boolean add(E e)
              { 
          boolean r = list.add(e); sortThis(); return r; }
              
          public boolean addAll(Collection<? extends E> ec)
              { 
          boolean r = list.addAll(ec); sortThis(); return r; }
              
          public boolean remove(Object o)
              { 
          boolean r = list.remove(o); sortThis(); return r; }
              
          public boolean removeAll(Collection<?> c)
              { 
          boolean r = list.removeAll(c); sortThis(); return r; }
              
          public boolean retainAll(Collection<?> ec)
              { 
          boolean r = list.retainAll(ec); sortThis(); return r; }
              
              
          public void clear() { list.clear(); }
              
          public boolean contains(Object o) { return list.contains(o); }
              
          public boolean containsAll(Collection <?> c) { return list.containsAll(c); }
              
          public boolean isEmpty() { return list.isEmpty(); }
              
          public Iterator<E> iterator() { return list.iterator(); }
              
          public int size() { return list.size(); }
              
          public Object[] toArray() { return list.toArray(); }
              
          public <T> T[] toArray(T[] a) { return list.toArray(a); }
              
              
          public boolean equals(Object o)
              {
                  
          if (o == this)
                      
          return true;
                  
                  
          if (o instanceof ArraySortedCollection)
                  {
                      ArraySortedCollection
          <E> rhs = (ArraySortedCollection<E>)o;
                      
          return this.list.equals(rhs.list);
                  }
                  
                  
          return false;
              }
              
          public int hashCode()
              {
                  
          return list.hashCode();
              }
              
          public String toString()
              {
                  
          return list.toString();
              }
              
              
          private void sortThis()
              {
                  Collections.sort(list, comparator);
              }
          }

          這個應急式的實現,未經縝密的思索就寫成了,毫無疑問它需要進行重構。但重點是,對于所有與集合相關的事情中,Java集合框架API原本就不是被設計成不需被修改的。它既需要也鼓勵擴展。
              當然,有一些擴展會是有"重要職責"的變體,例如由java.util.concurrent包引用的擴展實現。但其它的一些則只是簡單地編寫一個定制算法,或是對已有集合類的一個簡單擴展。
              擴展Java集合框架API看起來很具顛覆性,但一旦你開始做了,你就會發現并不如你所想像的那般困難。

          結論
              與Java序列化相同,Java集合框架API滿是未被探究的細微之處--這也是為什么我們沒有結束該主題的原因。5 things系列的下一篇文章將會向你展示使用Java集合框API做更多事情的另外5種方法。

          請關注你所不知道的五件事情--Java集合框架API(第二部分)

          祝大家五·一假期愉快 ^_^

          posted on 2010-05-02 18:21 John Jiang 閱讀(6122) 評論(3)  編輯  收藏 所屬分類: Java翻譯

          評論

          # re: 你所不知道的五件事情--Java集合框架API(第一部分)(譯)[未登錄] 2010-05-08 22:05 cc
          Person ted = new Person("Ted", "Neward", 39,
          new Person("Michael", "Neward", 16),
          new Person("Matthew", "Neward", 10));
          這段明顯跟你的Person構造方法,感覺應該寫個kid(String fn, String ln, int a)來繼承Person對象;
          Person ted = new Person("Ted", "Neward", 39,
          new Kid("Michael", "Neward", 16),
          new Kid("Matthew", "Neward", 10));
          這樣才對吧!  回復  更多評論
            

          # re: 你所不知道的五件事情--Java集合框架API(第一部分)(譯)[未登錄] 2010-05-08 22:19 cc
          其實也就是說一句話:在jdk1.5中,只要我們的類實現了Iterable接口的,即也可以使用如下語句, for(Object o : iterable);

            回復  更多評論
            

          # re: 你所不知道的五件事情--Java集合框架API(第一部分)(譯) 2010-05-08 22:47 Sha Jiang
          @cc
          > 感覺應該寫個kid(String fn, String ln, int a)來繼承Person對象;
          > Person ted = new Person("Ted", "Neward", 39,
          > new Kid("Michael", "Neward", 16),
          > new Kid("Matthew", "Neward", 10));
          我認為不需要創建這個Kid類。
          至少從你的示例沒看出這種"必要性"。實質上,Kid并沒有對Person進行擴展。  回復  更多評論
            

          主站蜘蛛池模板: 德庆县| 兰州市| 库车县| 大庆市| 恭城| 和林格尔县| 乡宁县| 烟台市| 巴塘县| 紫阳县| 宜川县| 白河县| 陇南市| 吐鲁番市| 江孜县| 玉林市| 竹山县| 定远县| 秦皇岛市| 宣威市| 南陵县| 肥乡县| 孝昌县| 乌拉特后旗| 天柱县| 苏州市| 蒙山县| 印江| 克山县| 聊城市| 凤庆县| 河西区| 凤山市| 华安县| 澄城县| 黔西县| 电白县| 绥化市| 南宫市| 鄯善县| 丽水市|