精彩的人生

          好好工作,好好生活

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks

          #

          java.util 中的集合類包含 Java 中某些最常用的類。 最常用的集合類是 List 和 Map。 List 的具體實現包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構建、存儲和操作任何類型對象的元素列表。 List 適用于按數值索引訪問元素的情形。

          Map 提供了一個更通用的元素存儲方法。 Map 集合類用于存儲元素對(稱作“鍵”和“值”),其中每個鍵映射到一個值。 從概念上而言,您可以將 List 看作是具有數值鍵的 Map。 而實際上,除了 List 和 Map 都在定義 java.util 中外,兩者并沒有直接的聯系。本文將著重介紹核心 Java 發行套件中附帶的 Map,同時還將介紹如何采用或實現更適用于您應用程序特定數據的專用 Map。

          了解 Map 接口和方法

          Java 核心類中有很多預定義的 Map 類。 在介紹具體實現之前,我們先介紹一下 Map 接口本身,以便了解所有實現的共同點。 Map 接口定義了四種類型的方法,每個 Map 都包含這些方法。 下面,我們從兩個普通的方法(表 1)開始對這些方法加以介紹。

          表 1: 覆蓋的方法。 我們將這 Object 的這兩個方法覆蓋,以正確比較 Map 對象的等價性。 equals(Object o) 比較指定對象與此 Map 的等價性
          hashCode() 返回此 Map 的哈希碼



          Map 構建

          Map 定義了幾個用于插入和刪除元素的變換方法(表 2)。

          表 2: Map 更新方法: 可以更改 Map 內容。 clear() 從 Map 中刪除所有映射
          remove(Object key) 從 Map 中刪除鍵和關聯的值
          put(Object key, Object value) 將指定值與指定鍵相關聯
          clear() 從 Map 中刪除所有映射
          putAll(Map t) 將指定 Map 中的所有映射復制到此 map



          盡管您可能注意到,縱然假設忽略構建一個需要傳遞給 putAll() 的 Map 的開銷,使用 putAll() 通常也并不比使用大量的 put() 調用更有效率,但 putAll() 的存在一點也不稀奇。 這是因為,putAll() 除了迭代 put() 所執行的將每個鍵值對添加到 Map 的算法以外,還需要迭代所傳遞的 Map 的元素。 但應注意,putAll() 在添加所有元素之前可以正確調整 Map 的大小,因此如果您未親自調整 Map 的大小(我們將對此進行簡單介紹),則 putAll() 可能比預期的更有效。

          查看 Map

          迭代 Map 中的元素不存在直接了當的方法。 如果要查詢某個 Map 以了解其哪些元素滿足特定查詢,或如果要迭代其所有元素(無論原因如何),則您首先需要獲取該 Map 的“視圖”。 有三種可能的視圖(參見表 3)

          所有鍵值對 — 參見 entrySet()
          所有鍵 — 參見 keySet()
          所有值 — 參見 values()

          前兩個視圖均返回 Set 對象,第三個視圖返回 Collection 對象。 就這兩種情況而言,問題到這里并沒有結束,這是因為您無法直接迭代 Collection 對象或 Set 對象。要進行迭代,您必須獲得一個 Iterator 對象。 因此,要迭代 Map 的元素,必須進行比較煩瑣的編碼


          Iterator keyValuePairs = aMap.entrySet().iterator();
          Iterator keys = aMap.keySet().iterator();
          Iterator values = aMap.values().iterator();


          值得注意的是,這些對象(Set、Collection 和 Iterator)實際上是基礎 Map 的視圖,而不是包含所有元素的副本。 這使它們的使用效率很高。 另一方面,Collection 或 Set 對象的 toArray() 方法卻創建包含 Map 所有元素的數組對象,因此除了確實需要使用數組中元素的情形外,其效率并不高。

          我運行了一個小測試(隨附文件中的 Test1),該測試使用了 HashMap,并使用以下兩種方法對迭代 Map 元素的開銷進行了比較:


          int mapsize = aMap.size();

          Iterator keyValuePairs1 = aMap.entrySet().iterator();
          for (int i = 0; i < mapsize; i++)
          {
          Map.Entry entry = (Map.Entry) keyValuePairs1.next();
          Object key = entry.getKey();
          Object value = entry.getValue();
          ...
          }

          Object[] keyValuePairs2 = aMap.entrySet().toArray();
          for (int i = 0; i < rem; i++) {
          {
          Map.Entry entry = (Map.Entry) keyValuePairs2[i];
          Object key = entry.getKey();


          Object value = entry.getValue();
          ...
          }


          此測試使用了兩種測量方法: 一種是測量迭代元素的時間,另一種測量使用 toArray 調用創建數組的其他開銷。 第一種方法(忽略創建數組所需的時間)表明,使用已從 toArray 調用中創建的數組迭代元素的速度要比使用 Iterator 的速度大約快 30%-60%。 但如果將使用 toArray 方法創建數組的開銷包含在內,則使用 Iterator 實際上要快 10%-20%。 因此,如果由于某種原因要創建一個集合元素的數組而非迭代這些元素,則應使用該數組迭代元素。 但如果您不需要此中間數組,則不要創建它,而是使用 Iterator 迭代元素。

          表 3: 返回視圖的 Map 方法: 使用這些方法返回的對象,您可以遍歷 Map 的元素,還可以刪除 Map 中的元素。 entrySet() 返回 Map 中所包含映射的 Set 視圖。 Set 中的每個元素都是一個 Map.Entry 對象,可以使用 getKey() 和 getValue() 方法(還有一個 setValue() 方法)訪問后者的鍵元素和值元素
          keySet() 返回 Map 中所包含鍵的 Set 視圖。 刪除 Set 中的元素還將刪除 Map 中相應的映射(鍵和值)
          values() 返回 map 中所包含值的 Collection 視圖。 刪除 Collection 中的元素還將刪除 Map 中相應的映射(鍵和值)



          訪問元素

          表 4 中列出了 Map 訪問方法。Map 通常適合按鍵(而非按值)進行訪問。 Map 定義中沒有規定這肯定是真的,但通常您可以期望這是真的。 例如,您可以期望 containsKey() 方法與 get() 方法一樣快。 另一方面,containsValue() 方法很可能需要掃描 Map 中的值,因此它的速度可能比較慢。

          表 4: Map 訪問和測試方法: 這些方法檢索有關 Map 內容的信息但不更改 Map 內容。 get(Object key) 返回與指定鍵關聯的值
          containsKey(Object key) 如果 Map 包含指定鍵的映射,則返回 true
          containsValue(Object value) 如果此 Map 將一個或多個鍵映射到指定值,則返回 true
          isEmpty() 如果 Map 不包含鍵-值映射,則返回 true
          size() 返回 Map 中的鍵-值映射的數目



          對使用 containsKey() 和 containsValue() 遍歷 HashMap 中所有元素所需時間的測試表明,containsValue() 所需的時間要長很多。 實際上要長幾個數量級! (參見圖 1 和圖 2,以及隨附文件中的 Test2)。 因此,如果 containsValue() 是應用程序中的性能問題,它將很快顯現出來,并可以通過監測您的應用程序輕松地將其識別。 這種情況下,我相信您能夠想出一個有效的替換方法來實現 containsValue() 提供的等效功能。 但如果想不出辦法,則一個可行的解決方案是再創建一個 Map,并將第一個 Map 的所有值作為鍵。 這樣,第一個 Map 上的 containsValue() 將成為第二個 Map 上更有效的 containsKey()。


          圖 1: 使用 JDeveloper 創建并運行 Map 測試類




          圖 2: 在 JDeveloper 中使用執行監測器進行的性能監測查出應用程序中的瓶頸



          核心 Map

          Java 自帶了各種 Map 類。 這些 Map 類可歸為三種類型:


          通用 Map,用于在應用程序中管理映射,通常在 java.util 程序包中實現
          HashMap
          Hashtable
          Properties
          LinkedHashMap
          IdentityHashMap
          TreeMap
          WeakHashMap
          ConcurrentHashMap
          專用 Map,您通常不必親自創建此類 Map,而是通過某些其他類對其進行訪問
          java.util.jar.Attributes
          javax.print.attribute.standard.PrinterStateReasons
          java.security.Provider
          java.awt.RenderingHints
          javax.swing.UIDefaults
          一個用于幫助實現您自己的 Map 類的抽象類
          AbstractMap

          內部哈希: 哈希映射技術

          幾乎所有通用 Map 都使用哈希映射。 這是一種將元素映射到數組的非常簡單的機制,您應了解哈希映射的工作原理,以便充分利用 Map。

          哈希映射結構由一個存儲元素的內部數組組成。 由于內部采用數組存儲,因此必然存在一個用于確定任意鍵訪問數組的索引機制。 實際上,該機制需要提供一個小于數組大小的整數索引值。 該機制稱作哈希函數。 在 Java 基于哈希的 Map 中,哈希函數將對象轉換為一個適合內部數組的整數。 您不必為尋找一個易于使用的哈希函數而大傷腦筋: 每個對象都包含一個返回整數值的 hashCode() 方法。 要將該值映射到數組,只需將其轉換為一個正值,然后在將該值除以數組大小后取余數即可。 以下是一個簡單的、適用于任何對象的 Java 哈希函數


          int hashvalue = Maths.abs(key.hashCode()) % table.length;


          (% 二進制運算符(稱作模)將左側的值除以右側的值,然后返回整數形式的余數。)

          實際上,在 1.4 版發布之前,這就是各種基于哈希的 Map 類所使用的哈希函數。 但如果您查看一下代碼,您將看到


          int hashvalue = (key.hashCode() & 0x7FFFFFFF) % table.length;


          它實際上是使用更快機制獲取正值的同一函數。 在 1.4 版中,HashMap 類實現使用一個不同且更復雜的哈希函數,該函數基于 Doug Lea 的 util.concurrent 程序包(稍后我將更詳細地再次介紹 Doug Lea 的類)。


          圖 3: 哈希工作原理



          該圖介紹了哈希映射的基本原理,但我們還沒有對其進行詳細介紹。 我們的哈希函數將任意對象映射到一個數組位置,但如果兩個不同的鍵映射到相同的位置,情況將會如何? 這是一種必然發生的情況。 在哈希映射的術語中,這稱作沖突。 Map 處理這些沖突的方法是在索引位置處插入一個鏈接列表,并簡單地將元素添加到此鏈接列表。 因此,一個基于哈希的 Map 的基本 put() 方法可能如下所示


          public Object put(Object key, Object value) {
          //我們的內部數組是一個 Entry 對象數組
          //Entry[] table;

          //獲取哈希碼,并映射到一個索引
          int hash = key.hashCode();
          int index = (hash & 0x7FFFFFFF) % table.length;

          //循環遍歷位于 table[index] 處的鏈接列表,以查明
          //我們是否擁有此鍵項 — 如果擁有,則覆蓋它
          for (Entry e = table[index] ; e != null ; e = e.next) {
          //必須檢查鍵是否相等,原因是不同的鍵對象
          //可能擁有相同的哈希
          if ((e.hash == hash) && e.key.equals(key)) {
          //這是相同鍵,覆蓋該值
          //并從該方法返回 old 值
          Object old = e.value;
          e.value = value;
          return old;
          }
          }

          //仍然在此處,因此它是一個新鍵,只需添加一個新 Entry
          //Entry 對象包含 key 對象、 value 對象、一個整型的 hash、
          //和一個指向列表中的下一個 Entry 的 next Entry

          //創建一個指向上一個列表開頭的新 Entry,
          //并將此新 Entry 插入表中
          Entry e = new Entry(hash, key, value, table[index]);
          table[index] = e;

          return null;
          }



          如果看一下各種基于哈希的 Map 的源代碼,您將發現這基本上就是它們的工作原理。 此外,還有一些需要進一步考慮的事項,如處理空鍵和值以及調整內部數組。 此處定義的 put() 方法還包含相應 get() 的算法,這是因為插入包括搜索映射索引處的項以查明該鍵是否已經存在。 (即 get() 方法與 put() 方法具有相同的算法,但 get() 不包含插入和覆蓋代碼。) 使用鏈接列表并不是解決沖突的唯一方法,某些哈希映射使用另一種“開放式尋址”方案,本文對其不予介紹。

          優化 Hasmap

          如果哈希映射的內部數組只包含一個元素,則所有項將映射到此數組位置,從而構成一個較長的鏈接列表。 由于我們的更新和訪問使用了對鏈接列表的線性搜索,而這要比 Map 中的每個數組索引只包含一個對象的情形要慢得多,因此這樣做的效率很低。 訪問或更新鏈接列表的時間與列表的大小線性相關,而使用哈希函數訪問或更新數組中的單個元素則與數組大小無關 — 就漸進性質(Big-O 表示法)而言,前者為 O(n),而后者為 O(1)。 因此,使用一個較大的數組而不是讓太多的項聚集在太少的數組位置中是有意義的。

          調整 Map 實現的大小

          在哈希術語中,內部數組中的每個位置稱作“存儲桶”(bucket),而可用的存儲桶數(即內部數組的大小)稱作容量 (capacity)。 為使 Map 對象有效地處理任意數目的項,Map 實現可以調整自身的大小。 但調整大小的開銷很大。 調整大小需要將所有元素重新插入到新數組中,這是因為不同的數組大小意味著對象現在映射到不同的索引值。 先前沖突的鍵可能不再沖突,而先前不沖突的其他鍵現在可能沖突。 這顯然表明,如果將 Map 調整得足夠大,則可以減少甚至不再需要重新調整大小,這很有可能顯著提高速度。

          使用 1.4.2 JVM 運行一個簡單的測試,即用大量的項(數目超過一百萬)填充 HashMap。 表 5 顯示了結果,并將所有時間標準化為已預先設置大小的服務器模式(關聯文件中的 Test3)。 對于已預先設置大小的 JVM,客戶端和服務器模式 JVM 運行時間幾乎相同(在放棄 JIT 編譯階段后)。 但使用 Map 的默認大小將引發多次調整大小操作,開銷很大,在服務器模式下要多用 50% 的時間,而在客戶端模式下幾乎要多用兩倍的時間!

          表 5: 填充已預先設置大小的 HashMap 與填充默認大小的 HashMap 所需時間的比較 客戶端模式 服務器模式
          預先設置的大小 100% 100%
          默認大小 294% 157%



          使用負載因子

          為確定何時調整大小,而不是對每個存儲桶中的鏈接列表的深度進行記數,基于哈希的 Map 使用一個額外參數并粗略計算存儲桶的密度。 Map 在調整大小之前,使用名為“負載因子”的參數指示 Map 將承擔的“負載”量,即它的負載程度。 負載因子、項數(Map 大小)與容量之間的關系簡單明了:


          如果(負載因子)x(容量)>(Map 大小),則調整 Map 大小

          例如,如果默認負載因子為 0.75,默認容量為 11,則 11 x 0.75 = 8.25,該值向下取整為 8 個元素。 因此,如果將第 8 個項添加到此 Map,則該 Map 將自身的大小調整為一個更大的值。 相反,要計算避免調整大小所需的初始容量,用將要添加的項數除以負載因子,并向上取整,例如,


          對于負載因子為 0.75 的 100 個項,應將容量設置為 100/0.75 = 133.33,并將結果向上取整為 134(或取整為 135 以使用奇數)

          奇數個存儲桶使 map 能夠通過減少沖突數來提高執行效率。 雖然我所做的測試(關聯文件中的Test4)并未表明質數可以始終獲得更好的效率,但理想情形是容量取質數。 1.4 版后的某些 Map(如 HashMap 和 LinkedHashMap,而非 Hashtable 或 IdentityHashMap)使用需要 2 的冪容量的哈希函數,但下一個最高 2 的冪容量由這些 Map 計算,因此您不必親自計算。

          負載因子本身是空間和時間之間的調整折衷。 較小的負載因子將占用更多的空間,但將降低沖突的可能性,從而將加快訪問和更新的速度。 使用大于 0.75 的負載因子可能是不明智的,而使用大于 1.0 的負載因子肯定是不明知的,這是因為這必定會引發一次沖突。 使用小于 0.50 的負載因子好處并不大,但只要您有效地調整 Map 的大小,應不會對小負載因子造成性能開銷,而只會造成內存開銷。 但較小的負載因子將意味著如果您未預先調整 Map 的大小,則導致更頻繁的調整大小,從而降低性能,因此在調整負載因子時一定要注意這個問題。

          選擇適當的 Map

          應使用哪種 Map? 它是否需要同步? 要獲得應用程序的最佳性能,這可能是所面臨的兩個最重要的問題。 當使用通用 Map 時,調整 Map 大小和選擇負載因子涵蓋了 Map 調整選項。

          以下是一個用于獲得最佳 Map 性能的簡單方法

          將您的所有 Map 變量聲明為 Map,而不是任何具體實現,即不要聲明為 HashMap 或 Hashtable,或任何其他 Map 類實現。


          Map criticalMap = new HashMap(); //好

          HashMap criticalMap = new HashMap(); //差


          這使您能夠只更改一行代碼即可非常輕松地替換任何特定的 Map 實例。

          下載 Doug Lea 的 util.concurrent 程序包 (http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)。 將 ConcurrentHashMap 用作默認 Map。 當移植到 1.5 版時,將 java.util.concurrent.ConcurrentHashMap 用作您的默認 Map。 不要將 ConcurrentHashMap 包裝在同步的包裝器中,即使它將用于多個線程。 使用默認大小和負載因子。
          監測您的應用程序。 如果發現某個 Map 造成瓶頸,則分析造成瓶頸的原因,并部分或全部更改該 Map 的以下內容: Map 類;Map 大小;負載因子;關鍵對象 equals() 方法實現。 專用的 Map 的基本上都需要特殊用途的定制 Map 實現,否則通用 Map 將實現您所需的性能目標。

          Map 選擇

          也許您曾期望更復雜的考量,而這實際上是否顯得太容易? 好的,讓我們慢慢來。 首先,您應使用哪種 Map? 答案很簡單: 不要為您的設計選擇任何特定的 Map,除非實際的設計需要指定一個特殊類型的 Map。 設計時通常不需要選擇具體的 Map 實現。 您可能知道自己需要一個 Map,但不知道使用哪種。 而這恰恰就是使用 Map 接口的意義所在。 直到需要時再選擇 Map 實現 — 如果隨處使用“Map”聲明的變量,則更改應用程序中任何特殊 Map 的 Map 實現只需要更改一行,這是一種開銷很少的調整選擇。 是否要使用默認的 Map 實現? 我很快將談到這個問題。

          同步 Map

          同步與否有何差別? (對于同步,您既可以使用同步的 Map,也可以使用 Collections.synchronizedMap() 將未同步的 Map 轉換為同步的 Map。 后者使用“同步的包裝器”)這是一個異常復雜的選擇,完全取決于您如何根據多線程并發訪問和更新使用 Map,同時還需要進行維護方面的考慮。 例如,如果您開始時未并發更新特定 Map,但它后來更改為并發更新,情況將如何? 在這種情況下,很容易在開始時使用一個未同步的 Map,并在后來向應用程序中添加并發更新線程時忘記將此未同步的 Map 更改為同步的 Map。 這將使您的應用程序容易崩潰(一種要確定和跟蹤的最糟糕的錯誤)。 但如果默認為同步,則將因隨之而來的可怕性能而序列化執行多線程應用程序。 看起來,我們需要某種決策樹來幫助我們正確選擇。

          Doug Lea 是紐約州立大學奧斯威戈分校計算機科學系的教授。 他創建了一組公共領域的程序包(統稱 util.concurrent),該程序包包含許多可以簡化高性能并行編程的實用程序類。 這些類中包含兩個 Map,即 ConcurrentReaderHashMap 和 ConcurrentHashMap。 這些 Map 實現是線程安全的,并且不需要對并發訪問或更新進行同步,同時還適用于大多數需要 Map 的情況。 它們還遠比同步的 Map(如 Hashtable)或使用同步的包裝器更具伸縮性,并且與 HashMap 相比,它們對性能的破壞很小。 util.concurrent 程序包構成了 JSR166 的基礎;JSR166 已經開發了一個包含在 Java 1.5 版中的并發實用程序,而 Java 1.5 版將把這些 Map 包含在一個新的 java.util.concurrent 程序包中。

          所有這一切意味著您不需要一個決策樹來決定是使用同步的 Map 還是使用非同步的 Map, 而只需使用 ConcurrentHashMap。 當然,在某些情況下,使用 ConcurrentHashMap 并不合適。 但這些情況很少見,并且應具體情況具體處理。 這就是監測的用途。


          結束語

          通過 Oracle JDeveloper 可以非常輕松地創建一個用于比較各種 Map 性能的測試類。 更重要的是,集成良好的監測器可以在開發過程中快速、輕松地識別性能瓶頸 - 集成到 IDE 中的監測器通常被較頻繁地使用,以便幫助構建一個成功的工程。 現在,您已經擁有了一個監測器并了解了有關通用 Map 及其性能的基礎知識,可以開始運行您自己的測試,以查明您的應用程序是否因 Map 而存在瓶頸以及在何處需要更改所使用的 Map。

          以上內容介紹了通用 Map 及其性能的基礎知識。 當然,有關特定 Map 實現以及如何根據不同的需求使用它們還存在更多復雜和值得關注的事項,這些將在本文第 2 部分中介紹。


          --------------------------------------------------------------------------------
          Jack Shirazi 是 O''Reilly 的“Java 性能調整”的作者,以及受歡迎的 JavaPerformanceTuning.com 網站(提供 Java 性能信息的全球知名站點)的總監。 Jack 在 Java 性能領域提供咨詢并著書立說。 他還監督 JavaPerformanceTuning.com 提供的信息,其中包括每年大約發布 1000 條性能技巧以及許多有關性能工具、討論組等內容的文章。 Jack 早年還曾發布有關蛋白質結構預測以及黑洞熱力學方面的文章,而且在其空閑時還對某些 Perl5 核心模塊作出了貢獻。


          摘自http://www.5ivb.net/Info/121/Info36849/
          posted @ 2006-04-03 11:09 hopeshared 閱讀(702) | 評論 (0)編輯 收藏

          如果想要取得系統的時間,可以使用System.currentTimeMillis()方法,例如:


          • DateDemo.java

          public class DateDemo {
          ????public static void main(String[]
          args) {
          ????????System.out.println(System.currentTimeMillis());
          ????}
          }



          執行結果會顯示從1970年1月1日開始到取得系統時間為止所經過的毫秒數,例如1115346430703這個數字,但這樣的數字沒有人確切了解它的意 義是什么,您可以使用Date類別來讓這個數字變的更有意義一些,例如:


          • DateDemo.java

          import java.util.Date;

          public class DateDemo {
          ????public static void main(String[]
          args) {
          ????????Date date = new Date();
          ????????
          ????????System.out.println(date.toString());
          ????????System.out.println(date.getTime());
          ????}
          }



          執行的結果如下:

          ??

          Fri May 06 10:31:13 GMT+08:00 2005
          ??1115346673531

          ??

          當您生成Date對象時,實際上它會使用System.currentTimeMillis()來取得系統時間,而您使用
          toString()方法時,會將取得的1970年1月1日至今的毫秒數轉為dow mon dd hh:mm:ss zzz yyyy的格式,分別是:「星期 月 日 時:分:秒 公元」;使用Date的getTime()方法則可以取得毫秒數。

          如果您想要對日期時間作格式設定,則可以使用DateFormat來作格式化,先來看看它的子類SimpleDateFormat如何使用:
          • DateDemo.java

          import java.text.DateFormat;
          import java.text.SimpleDateFormat;
          import java.util.Date;

          public class DateDemo {
          ????public static void main(String[]
          args) {
          ????????Date date = new Date();

          ????????DateFormat dateFormat =
          ????????????new
          SimpleDateFormat("EE-MM-dd-yyyy");
          ????????
          ????????System.out.println(dateFormat.format(date));
          ????}
          }



          執行結果:

          ??

          星期五-05-06-2005

          ??

          DateFormat會依計算機上的區域設定顯示時間格式,EE表示星期,MM表示月份、dd表示日期,而yyyy是公元,每個字符的設定都各有其意義,您
          可以參考 SimpleDateFormat 的API說明了解每個字符設定的意義。

          您也可以直接從DateFormat指定格式生成DateFormat的實例,例如:
          • DateDemo.java

          import java.text.DateFormat;
          import java.util.Date;

          public class DateDemo {
          ????public static void main(String[]
          args) {
          ????????Date date = new Date();

          ????????DateFormat shortFormat =
          ????????????DateFormat.getDateTimeInstance(
          ????????????????DateFormat.SHORT,
          DateFormat.SHORT);

          ????????DateFormat mediumFormat =
          ????????????DateFormat.getDateTimeInstance(
          ????????????????DateFormat.MEDIUM,
          DateFormat.MEDIUM);

          ????????DateFormat longFormat =
          ????????????DateFormat.getDateTimeInstance(
          ????????????????DateFormat.LONG, DateFormat.LONG);

          ????????DateFormat fullFormat =
          ????????????DateFormat.getDateTimeInstance(
          ????????????????DateFormat.FULL,
          DateFormat.FULL);

          ????????System.out.println(shortFormat.format(date));
          ????????System.out.println(mediumFormat.format(date));
          ????????System.out.println(longFormat.format(date));
          ????????System.out.println(fullFormat.format(date));
          ????}
          }



          在使用getDateTimeInstance()取得DateFormat實例時,可以指定的參數是日期格式與時間格式,以上所指定的格式依訊息詳細度 區分,執行結果如下:

          2005/5/6 上午 10:45
          2005/5/6 上午 10:45:25
          2005年5月6日 上午10時45分25秒
          2005年5月6日 星期五 上午10時45分25秒 GMT+08:00??


          您也可以使用getDateInstance()取得DateFormat實 例,并同時指定日期的區域顯示方式,例如:


          • DateDemo.java????


          import java.text.DateFormat;
          import java.util.Date;
          import java.util.Locale;

          public class DateDemo {
          ????public static void main(String[]
          args) {
          ????????Date date = new Date();

          ????????Locale locale = new
          Locale("en", "US");
          ????????DateFormat shortFormat =
          ????????????DateFormat.getDateInstance(
          ????????????????DateFormat.SHORT,
          locale);

          ????????DateFormat mediumFormat =
          ????????????DateFormat.getDateInstance(
          ????????????????DateFormat.MEDIUM,
          locale);

          ????????DateFormat longFormat =
          ????????????DateFormat.getDateInstance(
          ????????????????DateFormat.LONG, locale);

          ????????DateFormat fullFormat =
          ????????????DateFormat.getDateInstance(
          ????????????????DateFormat.FULL, locale);

          ????????System.out.println(shortFormat.format(date));
          ????????System.out.println(mediumFormat.format(date));
          ????????System.out.println(longFormat.format(date));
          ????????System.out.println(fullFormat.format(date));
          ????}
          }



          這邊指定了美國的時間顯示方式,執行結果如下:

          ??

          5/6/05
          ??May 6, 2005
          ??May 6, 2005
          ??Friday, May 6, 2005


          -------------------------------------------------------------------------------------------------

          1. 怎樣計算兩個時間之間的間隔?

          間隔=Date1.getTime()-Date2.getTime();得出來的是毫秒數.
          除1000是秒,再除60是分,再除60是小時..............................

          記住java標準庫中所有時間類都以此為基礎轉化的,只是他寫好了一些
          轉化的方法給你用而已.但都離不開這個毫秒數為基礎.

          2. t=Calendar.getInstance();m=t.get(t.MONTH)+1;這里為什么要加一?

          在java語言里,date的month的取值范圍是:0~11,與人們的自然表達上相差1。

          3. 系統時間與當前日期的區別?

          系統時間確切的說應該是
          System.currentTimeMillis();
          new Date()是當前日期,雖然它getTime();和System.currentTimeMillis();
          一樣,但System.currentTimeMillis();

          4. 如何計算兩個日期的天數差值?

          long beginTime = beginDate.getTime();
          long endTime2 = endDate.getTime();
          long betweenDays = (long)((endTime - beginTime) / (1000 * 60 * 60 *24) + 0.5);

          5. 如何比較日期時間大小?

          第一種方法:
          use Calendar object to compare
          java.util.Calendar class can be used to compare date. In order to do this,
          you guy should parse that string into int year, month, day and construct a
          Calendar object, and then do comparison.

          Below is a sample

          StringTokenizer token = new StringTokenizer(your string,"-");
          int year = Integer.parseInt(token.nextToken());
          int month = Integer.parseInt(token.nextToken());
          int day = Integer.parseInt(token.nextToken());
          Calendar date = Calendar.getInstance();
          date.set(year,month,day);
          Calendar today = Calendar.getInstacne();
          if(date.after(today)){
          //......
          }
          第二種方法
          Date nowDate=new Date();//當前時間\r
          long nowTime=nowDate.getTime;
          long lastTime=userTime.longValue();//以前的時間\r
          long time=nowTime-lastTime;//時間相減比較。
          if(time>(long)60000)//1分鐘{}

          另外可用以下參考
          用時間戳,Date.getTime()可以把當前時間改成時間戳,
          用CompareTo();
          用before(),after(),equals();

          6. 格式化日期的問題\r

          目的:
          第一次求日期
          java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
          String riqi=formatter.format(currentTime_1);
          第二次求時間\r
          java.text.DateFormat format1 = new java.text.SimpleDateFormat("hhmmss");
          java.util.Date currentTime_2 = new java.util.Date();
          String shijian=format1.format(currentTime_2);
          得到的結果是
          2002-02-19和115324(11點53分24秒)

          實現:
          java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd-H-mm-ss");
          java.util.Date currentTime_1 = new java.util.Date();
          String str_date = formatter.format(currentTime_1);
          StringTokenizer token = new StringTokenizer(str_date,"-");
          String year = token.nextToken();
          String month= token.nextToken();
          String day = token.nextToken();
          String hh = token.nextToken();
          String mm = token.nextToken();
          String ss = token.nextToken();
          String riqi=year+"年\\"+month+"月"+day+"日"+" "+hh+"點\\"+mm+"分"+ss+"秒\\";
          String newdir=year+month+day;
          String wenjian = hh+mm+ss;

          7. 怎么得到一個月的天數?

          java.util.Calendar date = java.util.Calendar.getInstance();
          System.out.println(date.getActualMaximum(date.DAY_OF_MONTH));


          ------------------------------------------------------------------------------------------------

          ?
          取得指定月份的第一天與取得指定月份的最后一天??
          ?????? /**??
          ???????? *??取得指定月份的第一天??
          ???????? *??
          ???????? *??@param??strdate??String??
          ???????? *??@return??String??
          ???????? */??
          ?????? public??String??getMonthBegin(String??strdate)??
          ?????? {??
          ?????????????? java.util.Date??date??=??parseFormatDate(strdate);??
          ?????????????? return??formatDateByFormat(date,"yyyy-MM")??+??"-01";??
          ?????? }??

          ?????? /**??
          ???????? *??取得指定月份的最后一天??
          ???????? *??
          ???????? *??@param??strdate??String??
          ???????? *??@return??String??
          ???????? */??
          ?????? public??String??getMonthEnd(String??strdate)??
          ?????? {??
          ?????????????? java.util.Date??date??=??parseFormatDate(getMonthBegin(strdate));??
          ?????????????? Calendar??calendar??=??Calendar.getInstance();??
          ?????????????? calendar.setTime(date);??
          ?????????????? calendar.add(Calendar.MONTH,1);??
          ?????????????? calendar.add(Calendar.DAY_OF_YEAR,??-1);??
          ?????????????? return??formatDate(calendar.getTime());??
          ?????? }??

          ?????? /**??
          ???????? *??常用的格式化日期??
          ???????? *??
          ???????? *??@param??date??Date??
          ???????? *??@return??String??
          ???????? */??
          ?????? public??String??formatDate(java.util.Date??date)??
          ?????? {??
          ?????????????? return??formatDateByFormat(date,"yyyy-MM-dd");??
          ?????? }??

          ?????? /**??
          ???????? *??以指定的格式來格式化日期??
          ???????? *??
          ???????? *??@param??date??Date??
          ???????? *??@param??format??String??
          ???????? *??@return??String??
          ???????? */??
          ?????? public??String??formatDateByFormat(java.util.Date??date,String??format)??
          ?????? {??
          ?????????????? String??result??=??"";??
          ?????????????? if(date??!=??null)??
          ?????????????? {??
          ?????????????????????? try??
          ?????????????????????? {??
          ?????????????????????????????? SimpleDateFormat??sdf??=??new??SimpleDateFormat(format);??
          ?????????????????????????????? result??=??sdf.format(date);??
          ?????????????????????? }??
          ?????????????????????? catch(Exception??ex)??
          ?????????????????????? {??
          ?????????????????????????????? LOGGER.info("date:"??+??date);??
          ?????????????????????????????? ex.printStackTrace();??
          ?????????????????????? }??
          ?????????????? }??
          ?????????????? return??result;??
          ?????? }??
          ---------------------------------------------------------------??

          package??com.app.util;??

          /**??
          *??日期操作??
          *????
          *??@author??xxx??
          *??@version??2.0??jdk1.4.0??tomcat5.1.0??*??Updated??Date:2005/03/10??
          */??
          public??class??DateUtil??{??
          ?????????? /**??
          ???????????? *??格式化日期??
          ???????????? *????
          ???????????? *??@param??dateStr??
          ???????????? *????????????????????????字符型日期??
          ???????????? *??@param??format??
          ???????????? *????????????????????????格式??
          ???????????? *??@return??返回日期??
          ???????????? */??
          ?????????? public??static??java.util.Date??parseDate(String??dateStr,??String??format)??{??
          ?????????????????????? java.util.Date??date??=??null;??
          ?????????????????????? try??{??
          ?????????????????????????????????? java.text.DateFormat??df??=??new??java.text.SimpleDateFormat(format);??
          ?????????????????????????????????? String??dt=Normal.parse(dateStr).replaceAll(??
          ?????????????????????????????????????????????????????????? "-",??"/");??
          ?????????????????????????????????? if((!dt.equals(""))&&(dt.length()<format.length())){??
          ?????????????????????????????????????????????? dt+=format.substring(dt.length()).replaceAll("[YyMmDdHhSs]","0");??
          ?????????????????????????????????? }??
          ?????????????????????????????????? date??=??(java.util.Date)??df.parse(dt);??
          ?????????????????????? }??catch??(Exception??e)??{??
          ?????????????????????? }??
          ?????????????????????? return??date;??
          ?????????? }??

          ?????????? public??static??java.util.Date??parseDate(String??dateStr)??{??
          ?????????????????????? return??parseDate(dateStr,??"yyyy/MM/dd");??
          ?????????? }??

          ?????????? public??static??java.util.Date??parseDate(java.sql.Date??date)??{??
          ?????????????????????? return??date;??
          ?????????? }??
          ????????????
          ?????????? public??static??java.sql.Date??parseSqlDate(java.util.Date??date)??{??
          ?????????????????????? if??(date??!=??null)??
          ?????????????????????????????????? return??new??java.sql.Date(date.getTime());??
          ?????????????????????? else??
          ?????????????????????????????????? return??null;??
          ?????????? }??

          ?????????? public??static??java.sql.Date??parseSqlDate(String??dateStr,??String??format)??{??
          ?????????????????????? java.util.Date??date??=??parseDate(dateStr,??format);??
          ?????????????????????? return??parseSqlDate(date);??
          ?????????? }??

          ?????????? public??static??java.sql.Date??parseSqlDate(String??dateStr)??{??
          ?????????????????????? return??parseSqlDate(dateStr,??"yyyy/MM/dd");??
          ?????????? }??

          ????????????
          ?????????? public??static??java.sql.Timestamp??parseTimestamp(String??dateStr,??
          ?????????????????????????????????? String??format)??{??
          ?????????????????????? java.util.Date??date??=??parseDate(dateStr,??format);??
          ?????????????????????? if??(date??!=??null)??{??
          ?????????????????????????????????? long??t??=??date.getTime();??
          ?????????????????????????????????? return??new??java.sql.Timestamp(t);??
          ?????????????????????? }??else??
          ?????????????????????????????????? return??null;??
          ?????????? }??

          ?????????? public??static??java.sql.Timestamp??parseTimestamp(String??dateStr)??{??
          ?????????????????????? return??parseTimestamp(dateStr,??"yyyy/MM/dd??HH:mm:ss");??
          ?????????? }??

          ?????????? /**??
          ???????????? *??格式化輸出日期??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@param??format??
          ???????????? *????????????????????????格式??
          ???????????? *??@return??返回字符型日期??
          ???????????? */??
          ?????????? public??static??String??format(java.util.Date??date,??String??format)??{??
          ?????????????????????? String??result??=??"";??
          ?????????????????????? try??{??
          ?????????????????????????????????? if??(date??!=??null)??{??
          ?????????????????????????????????????????????? java.text.DateFormat??df??=??new??java.text.SimpleDateFormat(format);??
          ?????????????????????????????????????????????? result??=??df.format(date);??
          ?????????????????????????????????? }??
          ?????????????????????? }??catch??(Exception??e)??{??
          ?????????????????????? }??
          ?????????????????????? return??result;??
          ?????????? }??

          ?????????? public??static??String??format(java.util.Date??date)??{??
          ?????????????????????? return??format(date,??"yyyy/MM/dd");??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回年份??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回年份??
          ???????????? */??
          ?????????? public??static??int??getYear(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.YEAR);??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回月份??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回月份??
          ???????????? */??
          ?????????? public??static??int??getMonth(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.MONTH)??+??1;??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回日份??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回日份??
          ???????????? */??
          ?????????? public??static??int??getDay(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.DAY_OF_MONTH);??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回小時??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回小時??
          ???????????? */??
          ?????????? public??static??int??getHour(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.HOUR_OF_DAY);??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回分鐘??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回分鐘??
          ???????????? */??
          ?????????? public??static??int??getMinute(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.MINUTE);??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回秒鐘??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回秒鐘??
          ???????????? */??
          ?????????? public??static??int??getSecond(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.get(java.util.Calendar.SECOND);??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回毫秒??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回毫秒??
          ???????????? */??
          ?????????? public??static??long??getMillis(java.util.Date??date)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTime(date);??
          ?????????????????????? return??c.getTimeInMillis();??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回字符型日期??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回字符型日期??
          ???????????? */??
          ?????????? public??static??String??getDate(java.util.Date??date)??{??
          ?????????????????????? return??format(date,??"yyyy/MM/dd");??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回字符型時間??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回字符型時間??
          ???????????? */??
          ?????????? public??static??String??getTime(java.util.Date??date)??{??
          ?????????????????????? return??format(date,??"HH:mm:ss");??
          ?????????? }??

          ?????????? /**??
          ???????????? *??返回字符型日期時間??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回字符型日期時間??
          ???????????? */??
          ?????????? public??static??String??getDateTime(java.util.Date??date)??{??
          ?????????????????????? return??format(date,??"yyyy/MM/dd??HH:mm:ss");??
          ?????????? }??

          ?????????? /**??
          ???????????? *??日期相加??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@param??day??
          ???????????? *????????????????????????天數??
          ???????????? *??@return??返回相加后的日期??
          ???????????? */??
          ?????????? public??static??java.util.Date??addDate(java.util.Date??date,??int??day)??{??
          ?????????????????????? java.util.Calendar??c??=??java.util.Calendar.getInstance();??
          ?????????????????????? c.setTimeInMillis(getMillis(date)??+??((long)??day)??*??24??*??3600??*??1000);??
          ?????????????????????? return??c.getTime();??
          ?????????? }??

          ?????????? /**??
          ???????????? *??日期相減??
          ???????????? *????
          ???????????? *??@param??date??
          ???????????? *????????????????????????日期??
          ???????????? *??@param??date1??
          ???????????? *????????????????????????日期??
          ???????????? *??@return??返回相減后的日期??
          ???????????? */??
          ?????????? public??static??int??diffDate(java.util.Date??date,??java.util.Date??date1)??{??
          ?????????????????????? return??(int)??((getMillis(date)??-??getMillis(date1))??/??(24??*??3600??*??1000));??
          ?????????? }??????????????
          }??
          ------------------------------------------------------------------------------------------------------------------

          來個簡單點的,也許有點用??

          Calendar??now??=??Calendar.getInstance();??
          int??year??=??now.get(Calendar.YEAR);??
          int??date??=??now.get(Calendar.DAY_OF_MONTH);??
          int??month??=??now.get(Calendar.MONTH)??+??1;??
          int??hour??=??now.get(Calendar.HOUR);??
          int??min??=??now.get(Calendar.MINUTE);??
          int??sec??=??now.get(Calendar.SECOND);??



          轉自http://www.513pc.net/bbs/dispbbs.asp?boardid=4&id=239&star=1&page=1

          posted @ 2006-04-03 10:56 hopeshared 閱讀(2469) | 評論 (1)編輯 收藏

          kXML is a small XML pull parser, specially designed for constrained environments such as Applets, Personal Java or MIDP devices.

          最小的版本只有11k,比那些龐大的xml解析起確實小好多。當你對xml解析不需要很嚴格時可以使用它。

          下面是使用kxml的一段示例代碼:
          import org.xmlpull.v1.*;
          
          import java.util.*;
          import java.io.*;
          import java.net.*;
          
          /** 
           * A simple example illustrationg some differences of the XmlPull API 
           * and SAX. For the corresponding SAX based implementation, please refer to 
           * http://www.cafeconleche.org/slides/sd2001east/xmlandjava/81.html ff. */publicclassWeblogs {
          
              static List listChannels()
                  throws IOException, XmlPullParserException {
                  return listChannels("http://static.userland.com/weblogMonitor/logs.xml");
              }
          
              static List listChannels(String uri)
                  throws IOException, XmlPullParserException {
          
                  Vector result = new Vector();
          
                  InputStream is = new URL(uri).openStream();
                  XmlPullParser parser =
                      XmlPullParserFactory.newInstance().newPullParser();
          
                  parser.setInput(is, null);
          
                  parser.nextTag();
                  parser.require(XmlPullParser.START_TAG, "", "weblogs");
          
                  while (parser.nextTag() == XmlPullParser.START_TAG) {
                      String url = readSingle(parser);
                      if (url != null)
                          result.addElement(url);
                  }
                  parser.require(XmlPullParser.END_TAG, "", "weblogs");
          
                  parser.next();
                  parser.require(XmlPullParser.END_DOCUMENT, null, null);
          
          		is.close ();
          		parser.setInput (null);
          
                  return result;
              }
          
              publicstatic String readSingle(XmlPullParser parser)
                  throws IOException, XmlPullParserException {
          
                  String url = null;
                  parser.require(XmlPullParser.START_TAG, "", "log");
          
                  while (parser.nextTag() == XmlPullParser.START_TAG) {
                      String name = parser.getName();
                      String content = parser.nextText();
                      if (name.equals("url"))
                          url = content;
                      parser.require(XmlPullParser.END_TAG, "", name);
                  }
                  parser.require(XmlPullParser.END_TAG, "", "log");
                  return url;
              }
          
              publicstaticvoid main(String[] args)
                  throws IOException, XmlPullParserException {
          
                  List urls =
                      args.length > 0
                          ? listChannels(args[0])
                          : listChannels();
          
                  for (Iterator i = urls.iterator(); i.hasNext();)
                      System.out.println(i.next());
              }
          }
          



          摘自http://www.21tx.com/dev/2004/11/27/11901.html
          posted @ 2006-04-03 10:34 hopeshared 閱讀(693) | 評論 (0)編輯 收藏

          在本系列的第一篇文章中,我研究了一些用 Java 編寫的主要的 XML 文檔模型的性能。但是,在開始選擇這種類型的技術時,性能只是問題的一部分。使用方便至少是同樣重要的,并且它已是一個主要理由,來支持使用 Java 特定的模型,而不是與語言無關的 DOM 。

          為切實了解哪個模型真正的作用,您需要知道它們在可用性程度上是如何排名的。本文中,我將嘗試進行這個工作,從樣本代碼開始,來演示如何在每個模型中編碼公共類型的操作。并對結果進行總結來結束本文,而且提出了促使一種表示比另一種更容易使用的一些其它因素。

          請參閱以前的文章(請參閱參考資料或本文“內容”下的便捷鏈接)來獲取這個對比中使用的各個模型的背景資料,包含實際的版本號。還可以參閱“參考資料”一節中關于源代碼下載、到模型主頁的鏈接以及其它相關信息。

          代碼對比
          在對不同文檔表示中用法技術的這些對比中,我將顯示如何在每種模型中實現三種基本操作:

          根據輸入流構建文檔
          遍歷元素和內容,并做一些更改:
          從文本內容中除去前導和尾隨的空白。
          如果結果文本內容為空,就刪除它。
          否則,將它包裝到父元素的名稱空間中一個名為“text”的新元素中。
          將已修改的文檔寫入輸出流

          這些示例的代碼是以我在上篇文章中使用的基準程序為基礎的,并進行了一些簡化。基準程序的焦點是為了顯示每個模型的最佳性能;對于本文,我將嘗試顯示在每種模型中實現操作的最簡便方法。

          我已經將每個模型的示例結構化為兩個獨立的代碼段。第一段是讀取文檔、調用修改代碼和編寫已修改文檔的代碼。第二段是真正遍歷文檔表示和執行修改的遞歸方法。為避免分散注意力,我已在代碼中忽略了異常處理。

          您可以從本頁底部參考資料一節鏈接到下載頁,以獲取所有樣本的完整代碼。樣本的下載版本包括一個測試驅動程序,還有一些添加的代碼用于通過計算元素、刪除和添加的個數來檢查不同模型的操作。

          即使您不想使用 DOM 實現,但還是值得瀏覽下面對 DOM 用法的描述。因為 DOM 示例是第一個示例,所以與后面的模型相比,我用它來探究有關該示例的一些問題和結構的更詳細信息。瀏覽這些內容可以補充您想知道的一些細節,如果直接閱讀其它模型之一,那么將錯過這些細節。

          DOM
          DOM 規范涵蓋了文檔表示的所有類型的操作,但是它沒有涉及例如對文檔的語法分析和生成文本輸出這樣的問題。包括在性能測試中的兩種 DOM 實現,Xerces 和 Crimson,對這些操作使用不同的技術。清單 1 顯示了 Xerces 的頂級代碼的一種形式。

          清單 1. Xerces DOM 頂級代碼
          1 // parse the document from input stream ("in")
          2 DOMParser parser = new DOMParser();
          3 parser.setFeature("http://xml.org/sax/features/namespaces", true);
          4 parser.parse(new InputSource(in));
          5 Document doc = parser.getDocument();

          6 // recursively walk and modify document
          7 modifyElement(doc.getDocumentElement());

          8 // write the document to output stream ("out")
          9 OutputFormat format = new OutputFormat(doc);
          10 XMLSerializer serializer = new XMLSerializer(out, format);
          11 serializer.serialize(doc.getDocumentElement());



          正如我在注釋中指出的,清單 1 中的第一塊代碼(第 1-5 行)處理對輸入流的語法分析,以構建文檔表示。Xerces 定義了 DOMParser 類,以便從 Xerces 語法分析器的輸出構建文檔。InputSource 類是 SAX 規范的一部分,它能適應供 SAX 分析器使用的幾種輸入形式的任何之一。通過單一調用進行實際的語法分析和文檔構造,如果成功完成了這一操作,那么應用程序就可以檢索并使用已構造的 Document。

          第二個代碼塊(第 6-7 行)只是將文檔的根元素傳遞給我馬上要談到的遞歸修改方法。這些代碼與本文中所有文檔模型的代碼在本質上是相同的,所以在剩余的示例中我將跳過它,不再做任何討論。

          第三個代碼塊(第 8-11 行)處理將文檔作為文本寫入輸出流。這里,OutputFormat 類包裝文檔,并為格式化生成的文本提供了多種選項。XMLSerializer 類處理輸出文本的實際生成。

          Xerces 的 modify 方法只使用標準 DOM 接口,所以它還與任何其它 DOM 實現兼容。清單 2 顯示了代碼。

          清單 2. DOM Modify 方法
          1 protected void modifyElement(Element element) {

          2 // loop through child nodes
          3 Node child;
          4 Node next = (Node)element.getFirstChild();
          5 while ((child = next) != null) {

          6 // set next before we change anything
          7 next = child.getNextSibling();

          8 // handle child by node type
          9 if (child.getNodeType() == Node.TEXT_NODE) {

          10 // trim whitespace from content text
          11 String trimmed = child.getNodeValue().trim();
          12 if (trimmed.length() == 0) {

          13 // delete child if nothing but whitespace
          14 element.removeChild(child);

          15 } else {

          16 // create a "text" element matching parent namespace
          17 Document doc = element.getOwnerDocument();
          18 String prefix = element.getPrefix();
          19 String name = (prefix == null) ? "text" : (prefix + ":text");
          20 Element text =
          21 doc.createElementNS(element.getNamespaceURI(), name);

          22 // wrap the trimmed content with new element
          23 text.appendChild(doc.createTextNode(trimmed));
          24 element.replaceChild(text, child);

          25 }
          26 } else if (child.getNodeType() == Node.ELEMENT_NODE) {

          27 // handle child elements with recursive call
          28 modifyElement((Element)child);

          29 }
          30 }
          31 }



          清單 2 中顯示的方法所使用的基本方法與所有文檔表示的方法相同。 通過一個元素調用它,它就依次遍歷那個元素的子元素。如果找到文本內容子元素,要么刪除文本(如果它只是由空格組成的),要么通過與包含元素相同的名稱空間中名為“text”的新元素來包裝文本(如果有非空格的字符)。如果找到一個子元素,那么這個方法就使用這個子元素,遞歸地調用它本身。

          對于 DOM 實現,我使用一對引用:child 和 next 來跟蹤子元素排序列表中我所處的位置。在對當前子節點進行任何其它處理之前,先裝入下個子節點的引用(第 7 行)。這樣做使得我能夠刪除或替代當前的子節點,而不丟失我在列表中的蹤跡。

          當我創建一個新元素來包裝非空白的文本內容(第 16-24 行)時,DOM 接口開始有點雜亂。用來創建元素的方法與文檔關聯并成為一個整體,所以我需要在所有者文檔中檢索當前我正在處理的元素(第 17 行)。我想將這個新元素放置在與現有的父元素相同的名稱空間中,并且在 DOM 中,這意味著我需要構造元素的限定名稱。根據是否有名稱空間的前綴,這個操作會有所不同(第 18-19 行)。利用新元素的限定名稱,以及現有元素中的名稱空間 URI,我就能創建新元素(第 20-21 行)。

          一旦創建了新元素,我只要創建和添加文本節點來包裝內容 String,然后用新創建的元素來替代原始文本節點(第 22-24 行)。

          清單 3. Crimson DOM 頂級代碼
          1 // parse the document from input stream
          2 System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
          3 "org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
          4 DocumentBuilderFactory dbf = DocumentBuilderFactoryImpl.newInstance();
          5 dbf.setNamespaceAware(true);
          6 DocumentBuilder builder = dbf.newDocumentBuilder();
          7 Document doc = builder.parse(in);

          8 // recursively walk and modify document
          9 modifyElement(doc.getDocumentElement());

          10 // write the document to output stream
          11 ((XmlDocument)doc).write(out);



          清單 3 中的 Crimson DOM 示例代碼使用了用于語法分析的 JAXP 接口。JAXP 為語法分析和轉換 XML 文檔提供了一個標準化的接口。本示例中的語法分析代碼還可以用于 Xerces(對文檔構建器類名稱的特性設置有適當的更改)來替代較早給定的 Xerces 特定的示例代碼。

          在本示例中,我首先在第 2 行到第 3 行中設置系統特性來選擇要構造的 DOM 表示的構建器工廠類(JAXP 僅直接支持構建 DOM 表示,不支持構建本文中討論的任何其它表示)。僅當想選擇一個要由 JAXP 使用的特定 DOM 時,才需要這一步;否則,它使用缺省實現。出于完整性起見,我在代碼中包含了設置這個特性,但是更普遍的是將它設置成一個 JVM 命令行參數。

          接著我在第 4 行到第 6 行中創建構建器工廠的實例,對使用那個工廠實例構造的構建器啟用名稱空間支持,并從構建器工廠創建文檔構建器。最后(第 7 行),我使用文檔構建器來對輸入流進行語法分析并構造文檔表示。

          為了寫出文檔,我使用 Crimson 中內部定義的基本方法。不保證在 Crimson 未來版本中支持這個方法,但是使用 JAXP 轉換代碼來將文檔作為文本輸出的替代方法需要諸如 Xalan 那樣的 XSL 處理器的。那超出了本文的范圍,但是要獲取詳細信息,可以查閱 Sun 中的 JAXP 教程。

          JDOM
          使用 JDOM 的頂級代碼比使用 DOM 實現的代碼稍微簡單一點。為構建文檔表示(第 1-3 行),我使用帶有由參數值禁止驗證的 SAXBuilder。通過使用提供的 XMLOutputter 類,將已修改的文檔寫入輸出流同樣簡單(第 6-8 行)。

          清單 4. JDOM 頂級代碼
          1 // parse the document from input stream
          2 SAXBuilder builder = new SAXBuilder(false);
          3 Document doc = builder.build(in);

          4 // recursively walk and modify document
          5 modifyElement(doc.getRootElement());

          6 // write the document to output stream
          7 XMLOutputter outer = new XMLOutputter();
          8 outer.output(doc, out);



          清單 5 中 JDOM 的 modify 方法也比 DOM 的同一方法簡單。我獲取包含元素所有內容的列表并掃描了這張列表,檢查文本(象 String 對象那樣的內容)和元素。這張列表是“活的”,所以我能直接對它進行更改,而不必調用父元素上的方法。

          清單 5. JDOM modify 方法
          1 protected void modifyElement(Element element) {

          2 // loop through child nodes
          3 List children = element.getContent();
          4 for (int i = 0; i < children.size(); i++) {

          5 // handle child by node type
          6 Object child = children.get(i);
          7 if (child instanceof String) {

          8 // trim whitespace from content text
          9 String trimmed = child.toString().trim();
          10 if (trimmed.length() == 0) {

          11 // delete child if only whitespace (adjusting index)
          12 children.remove(i--);

          13 } else {

          14 // wrap the trimmed content with new element
          15 Element text = new Element("text", element.getNamespace());
          16 text.setText(trimmed);
          17 children.set(i, text);

          18 }
          19 } else if (child instanceof Element) {

          20 // handle child elements with recursive call
          21 modifyElement((Element)child);

          22 }
          23 }
          24 }



          創建新元素的技術(第 14-17 行)非常簡單,而且與 DOM 版本不同,它不需要訪問父文檔。

          dom4j
          dom4j 的頂級代碼比 JDOM 的稍微復雜些,但是它們的代碼行非常類似。這里的主要區別是我保存了用來構建 dom4j 文檔表示的 DocumentFactory(第 5 行),并在輸出已修改的文檔文本之后刷新了 writer(第 10 行)。

          清單 6. dom4j 的頂級代碼
          1 // parse the document from input stream
          2 SAXReader reader = new SAXReader(false);
          3 Document doc = reader.read(in);

          4 // recursively walk and modify document
          5 m_factory = reader.getDocumentFactory();
          6 modifyElement(doc.getRootElement());

          7 // write the document to output stream
          8 XMLWriter writer = new XMLWriter(out);
          9 writer.write(doc);
          10 writer.flush();



          正如您在清單 6 中看到的,dom4j 使用一個工廠方法來構造文檔表示(從語法分析構建)中包含的對象。根據接口來定義每個組件對象,所以實現其中一個接口的任何類型的對象都能包含在表示中(與 JDOM 相反,它使用具體類:這些類在某些情況中可以劃分子類和被繼承,但是在文檔表示中使用的任何類都需要以原始 JDOM 類為基礎)。通過使用不同工廠進行 dom4j 文檔構建,您能獲取不同系列的組件中構造的文檔。

          在樣本代碼(第 5 行)中,我檢索了用于構建文檔的(缺省)文檔工廠,并將它存儲在一個實例變量(m_factory)中以供 modify 方法使用。并不嚴格需要這一步 — 可以在一個文檔中同時使用來自不同工廠的組件,或者可以繞過工廠而直接創建組件的實例 — 但在該例中,我只想創建與在文檔其余部分中使用的同一類型的組件,并且使用相同的工廠來確保完成這個步驟。

          清單 7. dom4j modify 方法
          1 protected void modifyElement(Element element) {

          2 // loop through child nodes
          3 List children = element.content();
          4 for (int i = 0; i < children.size(); i++) {

          5 // handle child by node type
          6 Node child = (Node)children.get(i);
          7 if (child.getNodeType() == Node.TEXT_NODE) {

          8 // trim whitespace from content text
          9 String trimmed = child.getText().trim();
          10 if (trimmed.length() == 0) {

          11 // delete child if only whitespace (adjusting index)
          12 children.remove(i--);

          13 } else {

          14 // wrap the trimmed content with new element
          15 Element text = m_factory.createElement
          16 (QName.get("text", element.getNamespace()));
          17 text.addText(trimmed);
          18 children.set(i, text);

          19 }
          20 } else if (child.getNodeType() == Node.ELEMENT_NODE) {

          21 // handle child elements with recursive call
          22 modifyElement((Element)child);

          23 }
          24 }
          25 }



          清單 7 中 dom4j modify 方法與 JDOM 中使用的方法非常類似。不通過使用 instanceof 運算符來檢查內容項的類型,我可以通過 Node 接口方法 getNodeType 來獲取類型代碼(也可以使用 instanceof,但類型代碼方法看起來更清晰)。通過使用 QName 對象來表示元素名稱和通過調用已保存的工廠的方法來構建元素可以區別新元素的創建技術(第 15-16 行)。

          Electric XML
          清單 8 中 Electric XML(EXML)的頂級代碼是任何這些示例中最簡單的一個,通過單一方法調用就可以讀取和編寫文檔。

          清單 8. EXML 頂級代碼
          1 // parse the document from input stream
          2 Document doc = new Document(in);

          3 // recursively walk and modify document
          4 modifyElement(doc.getRoot());

          5 // write the document to output stream
          6 doc.write(out);



          清單 9 中 EXML modify 方法盡管與 JDOM 一樣,需要使用 instanceof 檢查,但它與 DOM 方法最相似。在 EXML 中,無法創建一個帶名稱空間限定的名稱的元素,所以取而代之,我創建新元素,然后設置其名稱來達到相同的效果。

          清單 9. EXML modify 方法
          1 protected void modifyElement(Element element) {

          2 // loop through child nodes
          3 Child child;
          4 Child next = element.getChildren().first();
          5 while ((child = next) != null) {

          6 // set next before we change anything
          7 next = child.getNextSibling();

          8 // handle child by node type
          9 if (child instanceof Text) {

          10 // trim whitespace from content text
          11 String trimmed = ((Text)child).getString().trim();
          12 if (trimmed.length() == 0) {

          13 // delete child if only whitespace
          14 child.remove();

          15 } else {

          16 // wrap the trimmed content with new element
          17 Element text = new Element();
          18 text.addText(trimmed);
          19 child.replaceWith(text);
          20 text.setName(element.getPrefix(), "text");

          21 }
          22 } else if (child instanceof Element) {

          23 // handle child elements with recursive call
          24 modifyElement((Element)child);

          25 }
          26 }
          27 }



          XPP
          XPP 的頂級代碼(在清單 10 中)是所有示例中最長的一個,與其它模型相比,它需要相當多的設置。

          清單 10. XPP 頂級代碼
          1 // parse the document from input stream
          2 m_parserFactory = XmlPullParserFactory.newInstance();
          3 m_parserFactory.setNamespaceAware(true);
          4 XmlPullParser parser = m_parserFactory.newPullParser();
          5 parser.setInput(new BufferedReader(new InputStreamReader(in)));
          6 parser.next();
          7 XmlNode doc = m_parserFactory.newNode();
          8 parser.readNode(doc);

          9 // recursively walk and modify document
          10 modifyElement(doc);

          11 // write the document to output stream
          12 XmlRecorder recorder = m_parserFactory.newRecorder();
          13 Writer writer = new OutputStreamWriter(out);
          14 recorder.setOutput(writer);
          15 recorder.writeNode(doc);
          16 writer.close();



          因為使用 JAXP 接口,所以我必須首先創建分析器工廠的實例并在創建分析器實例之前啟用名稱空間處理(第 2-4 行)。一旦獲取了分析器實例,我就能將輸入設置到分析器中,并真正構建文檔表示(第 5-8 行),但是這涉及比其它模型更多的步驟。

          輸出處理(第 11-16 行)也涉及比其它模型更多的步驟,主要因為 XPP 需要 Writer 而不是直接將 Stream 作為輸出目標接受。

          清單 11 中 XPP modify 方法盡管需要更多代碼來創建新元素(第 13-21 行),但它與 JDOM 方法最類似。名稱空間處理在這里有點麻煩。我首先必須創建元素的限定名稱(第 15-16 行),然后創建元素,最后在稍后設置名稱和名稱空間 URI(第 18-21 行)。

          清單 11. XPP modify 方法
          1 protected void modifyElement(XmlNode element) throws Exception {

          2 // loop through child nodes
          3 for (int i = 0; i < element.getChildrenCount(); i++) {

          4 // handle child by node type
          5 Object child = element.getChildAt(i);
          6 if (child instanceof String) {

          7 // trim whitespace from content text
          8 String trimmed = child.toString().trim();
          9 if (trimmed.length() == 0) {

          10 // delete child if only whitespace (adjusting index)
          11 element.removeChildAt(i--);

          12 } else {

          13 // construct qualified name for wrapper element
          15 String prefix = element.getPrefix();
          16 String name = (prefix == null) ? "text" : (prefix + ":text");

          17 // wrap the trimmed content with new element
          18 XmlNode text = m_parserFactory.newNode();
          19 text.appendChild(trimmed);
          20 element.replaceChildAt(i, text);
          21 text.modifyTag(element.getNamespaceUri(), "text", name);

          22 }
          23 } else if (child instanceof XmlNode) {

          24 // handle child elements with recursive call
          25 modifyElement((XmlNode)child);

          26 }
          27 }
          28 }



          結束語
          DOM、dom4j 和 Electric XML 都得到這些幾乎同樣易于使用的代碼樣本,其中 EXML 可能最簡單,而 dom4j 受一些小條件限制而較困難。DOM 提供了與語言無關的非常實在的好處,但是如果你只使用 Java 代碼,那么通過與 Java 特定的模型相比較,它看上去有點麻煩。我認為這表明 Java 特定的模型通常成功地實現簡化 Java 代碼中的 XML 文檔處理這個目標。

          超越基礎:真實世界可用性
          代碼樣本顯示 JDOM 和 EXML 為基本文檔操作(使用元素、屬性和文本)提供了簡單和清晰的接口。根據我的經驗,它們的方法并不能很好地完成處理整個文檔表示的編程任務。要完成這些類型的任務,DOM 和 dom4j 使用的組件方法 — 其中從屬性到名稱空間的所有文檔組件實現一些公共接口 — 工作得更好。
          相關的例子是最近我為 JDOM 和 dom4j 實現的 XML 流型(XML Streaming (XMLS) )編碼。這個代碼遍歷整個文檔并編碼每個組件。JDOM 實現比 dom4j 實現復雜得多,主要是因為 JDOM 使用一些沒有公共接口的獨特類來表示每個組件。

          因為 JDOM 缺少公共接口,所以即使處理 Document 對象的代碼與處理 Element 對象的代碼都有一些諸如子組件那樣相同類型的組件,但是它們必須有所不同。還需要特殊方法來檢索與其它類型的子組件相對的 Namespace 組件。甚至當處理被認為是內容的子組件類型時,您需要在組件類型上使用多個帶 instanceof 檢查的 if 語句,而不是使用一條更清晰更快速的 switch 語句。

          具有諷刺意味的可能是 JDOM 的最初目標之一是利用 Java Collection 類,這些類本身在很大程度上以接口為基礎。庫中接口的使用增加了許多靈活性,而這是以增加了一些復雜性為代價的,并且這對于為重用而設計的代碼來說,通常是一個很好的折衷。這可能還主要歸功于 dom4j,它達到一個成熟并且穩定的狀態,比 JDOM 要快得多。

          盡管如此,對于使用多種語言的開發人員來說,DOM 仍是一個非常好的選擇。DOM 實現廣泛應用于多種編程語言。它還是許多其它與 XML 相關的標準的基礎,所以即使您使用 Java 特定的模型,也還有一個您逐步熟悉 DOM 所需要的好機會。因為它正式獲得 W3C 推薦(與基于非標準的 Java 模型相對),所以在某些類型的項目中可能也需要它。

          就使用方便這一范疇而言,在 JDOM、dom4j 和 Electric XML 這三個主要競爭者中,dom4j 與其它兩個的區別在于它使用帶有多個繼承層的基于接口的方法。這會使得遵循 API JavaDocs 更為困難些。例如,您正在尋找的一個方法(例如 content(),在我們 dom4j 的 modify 方法示例的第 3 行中使用的)可能是 Element 擴展的 Branch 接口的一部分,而不是 Element 接口本身的一部分。盡管如此,這種基于接口的設計添加了許多靈活性(請參閱側欄超越基礎:真實世界可用性)。考慮到 dom4j 的性能、穩定性和特性設置的優點,您應把它當作多數項目中的一個有力的候選者。

          在任一 Java 特定的文檔模型之中,JDOM 可能擁有最廣泛的用戶基礎,并且它的確是使用起來最簡單的模型之一。盡管如此,作為項目開發的一個選擇,它還是必須容忍 API 的不固定性和從一個版本到下一個版本的更新,在性能對比中它也表現得很糟糕。基于當前實現,我愿為著手新項目的人們推薦 dom4j,而不是 JDOM。

          除了 XPP 以外,EXML 比其它任何模型占用的資源都要少得多,并且考慮到 EXML 易于使用的優點,您應肯定會認為它適用于 jar 文件大小很重要的應用程序。但是,EXML 的 XML 支持的局限性和受限的許可證,以及在較大文件上所表現出的相對拙劣的性能,不得不在許多應用程序中放棄使用它。

          XPP 在語法分析和編寫文本文檔時需要更多步驟,并且在處理名稱空間時也需要更多步驟。如果 XPP 打算添加一些便利的方法來處理其中一些常見情況,那么在對比中它可能會更勝一籌。正如它現在所表現的,上篇文章中性能方面的領先者卻成了本文中的可用性方面的失敗者。盡管如此,因為 XPP 性能方面的優勢,所以對于需要較小的 jar 文件大小的應用程序還是值得將它作為 EXML 的替代方法。

          下一次...
          到目前為止在我寫的兩篇文章中,涉及到用 Java 編寫的 XML 文檔模型的性能和可用性。在本系列的后兩篇文章中,我將討論用 Java 技術進行 XML 數據綁定的方法。這些方法與文檔模型的方法有許多相似處,但是它們更進一步將 XML 文檔映射到實際應用程序數據結構中。我們將看到這一操作在使用的簡便性和提高性能方面是如何做得如此好的。

          回到 developerWorks,檢查 Java 代碼的 XML 數據綁定的實質。同時,您可以通過下面鏈接的論壇,給出您對本文的評論和問題。

          參考資料

          單擊本文頂部或底部的討論,參與本文的論壇。
          如果您需要背景資料,嘗試 developerWorks 的 XML Java 編程 教程、理解 SAX 教程和 理解 DOM 教程。
          從下載頁面下載本文中使用的測試程序和文檔模型庫。
          查找有關 Java APIs for XML Processing (JAXP) 或者閱讀 JAXP Tutorial。
          獲取作者有關 XML Streaming 的著作的詳細信息,它作為程序間傳送 XML 文檔的 Java 序列化的另一種選擇。
          回顧作者以前的文章:XML in Java: Document models, part 1。
          根據 Tony Darugar 的團隊對幾個大型 XML 項目的分析,參考他對 Effective DOM with Java 的建議。
          Java 的 XML 文檔模型:
          Xerces Java
          Crimson
          JDOM
          dom4j
          Electric XML
          XML Pull Parser (XPP)



          原文:http://www.daima.com.cn/Info/127/Info37907/
          posted @ 2006-04-03 10:30 hopeshared 閱讀(555) | 評論 (0)編輯 收藏

          我找到的兩種方法,希望大家補充

          第一種:利用Action
          IWorkbenchWindow window = getViewSite().getWorkbenchWindow();
          IWorkbenchAction max = ActionFactory.MAXIMIZE.create(window);
          max.run();


          這段代碼要放在何適的位置上才會起到合適的作用。

          第二種:利用Zoom
          在ApplicationWorkbenchWindowAdvisor#postWindowOpen中做到
          public void postWindowOpen() {
          ??? ?IWorkbenchWindow window = Plugin.getDefault().getWorkbench().getActiveWorkbenchWindow();
          ??? ?IViewPart part=null;
          ?????try {
          ?????????part = window.getActivePage().showView(View.ID);
          ?????} catch (PartInitException e) {
          ?????????e.printStackTrace();
          ?????}
          ??
          ?????if(part!=null){
          ????????window.getActivePage().activate(part);?
          ????????window.getActivePage().bringToTop(part);
          ??
          ????????WorkbenchPage realPage = (WorkbenchPage) window.getActivePage();

          ??????? IWorkbenchPartReference partRef = window.getActivePage().getActivePartReference();

          ??????? if (partRef != null) {
          ??????????? ((WorkbenchPage) window.getActivePage()).toggleZoom(partRef);
          ??????? }else{
          ??????? ?System.out.println("partRef is null!");
          ??????? }
          ????}
          }

          posted @ 2006-03-27 17:10 hopeshared 閱讀(1763) | 評論 (1)編輯 收藏

          僅列出標題
          共30頁: First 上一頁 14 15 16 17 18 19 20 21 22 下一頁 Last 
          主站蜘蛛池模板: 什邡市| 太谷县| 鄯善县| 合水县| 东辽县| 漾濞| 奇台县| 平山县| 略阳县| 台安县| 平果县| 太湖县| 海丰县| 家居| 河间市| 新昌县| 福建省| 嘉祥县| 化州市| 沅江市| 怀化市| 福州市| 喀什市| 南华县| 黄大仙区| 普定县| 大足县| 宜都市| 基隆市| 洛扎县| 蓬安县| 大宁县| 姚安县| 阿鲁科尔沁旗| 林州市| 灵寿县| 溆浦县| 噶尔县| 二手房| 阿鲁科尔沁旗| 宾川县|