zhyiwww
          用平實的筆,記錄編程路上的點點滴滴………
          posts - 536,comments - 394,trackbacks - 0

          Set 中的元素為什么不允許重復

          ?????? 版權所有,轉載請聲明出處 zhyiwww@163.com

          ?

          為了弄清楚這個問題 , 我又看了一遍 Collection 部分 , 并且看了些其中的源碼 , 覺得對其中的實現又明白了一點 , 現在說出來和大家共享 .

          我們先看一下 Set 類的關系圖:

          hashset.png
          現在我們就從
          Set 說起。

          Set 接口為我們提供了一個 add() 方法,以讓我們添加元素。所以我們看一下在其實現類 HashSet 中是如何實現的呢?

          我們看 HashSet 中的 add() 方法實現;

          ??? public boolean add( E o ) {

          ?????? ?????? return? map.put(o, PRESENT)==null;

          }

          你可能回問怎么回出來 map 了呢?

          Map 又是一個什么變量呢?

          我們來看 map 是在在哪定義的。原來,在 HashSet 中定義了這樣的兩個變量:

          ??? private transient HashMap<E,Object> map;

          ??? private static final Object PRESENT = new Object();

          我們再看一下上面的 add() 方法。

          map.put(o, PRESENT)==null

          實際執行的是 map 的方法,并且我們添加的對象是 map 中的 key,value 是執行的同一個對象 PRESENT.

          因為map中的key是不允許重復的,所以set中的元素不能重復。

          ?

          下面我們再看一下, map 又是如何保證其 key 不重復的呢?

          ?hashmap.png

          現在我們看一下 map 中的 put() 方法的實現:

          HashMap 實現了 map ,在 HashMap 中是這樣實現的:

          ??? public V put(K key, V value) {

          ????? K k = maskNull(key);

          ??????? int hash = hash(k);

          ??????? int i = indexFor(hash, table.length);

          ??????? for (Entry<K,V> e = table[i]; e != null; e = e.next) {

          ??????????? if (e.hash == hash && eq(k, e.key)) {

          ??????????????? V oldValue = e.value;

          ??????????????? e.value = value;

          ??????????????? e.recordAccess(this);

          ??????????????? return oldValue;

          ??????????? }

          ??????? }

          ?

          ??????? modCount++;

          ??????? addEntry(hash, k, value, i);

          ??????? return null;

          ??? }

          我們我們按照方法的執行一步一步的來看一下,其實現的過程。

          K k = maskNull(key);

          這一步是要判斷當前的要添加的對象的 key 是否為空,如果為空的話,那么就生成一個新的對象作為其 key 。實現如下:

          ??? static final Object NULL_KEY = new Object();

          ???? * Returns internal representation for key. Use NULL_KEY if key is null.

          ??? static <T> T maskNull(T key) {

          ??????? return key == null ? (T)NULL_KEY : key;

          ??? }

          之后

          int hash = hash(k);

          我們看一下 hash() 方法的實現:

          ??? static int hash(Object x) {

          ??????? int h = x.hashCode();

          ??????? h += ~(h << 9);

          ??????? h ^=? (h >>> 14);

          ??????? h +=? (h << 4);

          ??????? h ^=? (h >>> 10);

          ??????? return h;

          ??? }

          這一步其實是要得到當前要添加對象的 hashcode, 方法中,首先通過 int h = x.hashCode() 取得了當前的要添加對象的 hashcode, 然后

          ??????? h += ~(h << 9);

          ??????? h ^=? (h >>> 14);

          ??????? h +=? (h << 4);

          ??????? h ^=? (h >>> 10);

          生成一個新的 hashcode.

          接著執行的是

          (從此處開始,我理解的比較膚淺,請看到此出的朋友多多指點)

          int i = indexFor(hash, table.length);

          這個方法是要 Returns index for hash code h.

          ??? static int indexFor(int h, int length) {

          ??????? return h & (length-1);

          ??? }

          ????? 下面就要根據 hashmap 中的元素逐個的比較,看是否相同,如果不同就回添加一個新的元素。是通過循環判斷來實現的。

          ??????? for (Entry<K,V> e = table[i]; e != null; e = e.next) {

          ??????????? if (e.hash == hash && eq(k, e.key)) {

          ??????????????? V oldValue = e.value;

          ??????????????? e.value = value;

          ??????????????? e.recordAccess(this);

          這句我的理解是:在內存中的可以訪問元素又多了一個。也就是說,添加之后,可以通過hashmap來訪問此元素了。

          ???????? ???????return oldValue;

          ??????????? }

          ??????? }

          通過循環判斷是否有完全相同的對象,包括 hashcode value 值。如果已經存在就返回其值,如果不存在就執行一下操作。

          ??????? modCount++;

          ??????? addEntry(hash, k, value, i);

          ??????? return null;

          對象不存在,首先修改 hashmap 的修改次數,即 modCount 1. 然后將對象添加到數組中。

          ??? void addEntry(int hash, K key, V value, int bucketIndex) {

          ????? Entry<K,V> e = table[bucketIndex];

          ??????? table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

          ??????? if (size++ >= threshold)

          ??????????? resize(2 * table.length);

          }

          仍然是數組,我們來看一下 , hashmap 中用來存放對象的數組的定義:

          ?? ?transient Entry[] table;

          至此,我想大家也許應該明白了,為什么在 set 中是不允許存放重復值的。

          通過才的分析,我們可以看到, map 的一些特征:

          1.?????? map 中也是不能存放完全相同的元素的

          2.?????? 如果你存入的對象的 key 值已經存在的話,那么新的 value 將會取代老的 value 值,但是并不會添加新的元素進去。

          我們可以通過一個測試程序來證明這一點:

          ? public void mapTest() {

          ??? Map m = new HashMap();

          ??? /**

          ???? * we? can? put? the? int? 1 and the? value? 1? into? the? map

          ???? * but? we? cannot? get? the? 1 as? int? from the map

          ???? * why ?

          ???? * we? only? get? the? 1? as? integer? from? the map,

          ???? * so? we? only? get the object? from the map,if we? put the? value? into

          ???? * the map,we? will get one? instance of? the wrapper? of? that

          ???? */

          ??? m.put(1, 1);

          ??? //int i = m.get(1);

          ??? Integer i = (Integer) m.get(1);

          ??? System.out.println(i);

          ?

          ??? m.put(1, 1);

          ??? m.put(1, 1);

          ??? System.out.println("map?? :??? " + m);

          ??? System.out.println("map? size??? :?? " + m.size());

          ??? m.put(1, 2);

          ??? System.out.println("map?? :??? " + m);

          ??? System.out.println("map? size??? :?? " + m.size());

          ? }

          運行的結果是:

          map?? :??? {1=1}

          map? size??? :?? 1

          map?? :??? {1=2}

          map? size?? ?:?? 1

          希望此文能大家有所幫助。



          |----------------------------------------------------------------------------------------|
                                     版權聲明  版權所有 @zhyiwww
                      引用請注明來源 http://www.aygfsteel.com/zhyiwww   
          |----------------------------------------------------------------------------------------|
          posted on 2006-03-30 09:41 zhyiwww 閱讀(3666) 評論(0)  編輯  收藏 所屬分類: java basic
          主站蜘蛛池模板: 邵东县| 衡南县| 资兴市| 景泰县| 武强县| 简阳市| 广东省| 牡丹江市| 讷河市| 沅陵县| 丰宁| 肥乡县| 松原市| 海伦市| 丹江口市| 客服| 偏关县| 台南市| 瓮安县| 柘城县| 利辛县| 伊通| 丹棱县| 丰县| 聂荣县| 吴江市| 万山特区| 大丰市| 绥江县| 太仆寺旗| 西和县| 铜山县| 宣汉县| 河南省| 平山县| 寿宁县| 罗定市| 改则县| 南郑县| 杂多县| 黔南|