0、前言
工作忙起來后,許久不看算法,竟然DFA敏感詞算法都要看好一陣才能理解。。。真是和三階魔方還原手法一樣,田園將蕪,非常可惜啊。
在DFA算法中,第一步是需要理解它的數據結構,在此基礎上,涉及到一些Hashmap的賦值。這里的賦值非常有趣,三個Hashmap翻來覆去賦值,就解決了敏感詞表的初始化。
里面都是屬於下文中的Hashmap“淺拷貝”,那么究竟Java中的Hashmap有哪些拷貝方法呢?
1、測試代碼
HashMap hm_source = new HashMap(); HashMap hm_clone = new HashMap(); hm_source.put("1", "1"); // hashmap deep clone method 1 hm_clone = (HashMap)hm_source.clone(); // hashmap deep clone method 2 hm_clone.putAll(hm_source);// hashmap shadow clone // hm_b = hm_a; hm_source.put("2", "2"); System.out.println("hm_source增加元素后,hm_source:"+hm_source); System.out.println("hm_source增加元素后,hm_clone:"+hm_clone); System.out.println("是否指向同一內存地址:"+(hm_source==hm_clone)); System.out.println("第一個元素是否指向同一內存地址:"+(hm_source.get(1)==hm_clone.get(1)));
上面介紹了兩種Hashmap深拷貝的方法,分別是hashmap.clone()和hashmap.putAll(hm_source),效果一樣,輸出如下:
hm_source增加元素后,hm_source:{1=1, 2=2} hm_source增加元素后,hm_clone:{1=1} 是否指向同一內存地址:false 第一個元素是否指向同一內存地址:true
那么淺拷貝呢?(代碼中注釋的那段,直接等號=賦值),輸出如下:
hm_source增加元素后,hm_source:{1=1, 2=2} hm_source增加元素后,hm_clone:{1=1, 2=2} 是否指向同一內存地址:true 第一個元素是否指向同一內存地址:true
2、輸出解析
不難發現,深淺拷貝確實如其名,
深拷貝:兩個Hashmap對象似乎徹底無關,互相增加修改元素后,都不影響對方;
淺拷貝:兩個Hashmap對象就是“軟鏈接ln”,互相牽制,你改動了,我也跟着變。
3、上述猜想是否正確?
我黨的思想路線是實事求是,預想剖析根本區別,大家可以看看JDK clone函數的源碼。
但是從現象我們得到的初步結論有幾點:
- 淺拷貝的兩個對象使用的內存地址相同,深拷貝的對象地址“另立門戶”;
- 深拷貝的兩個對象也不完全“徹底無關”,僅僅是復制了元素的引用;
關於結論3.2,我也是在 HashMap的clone方法 博文中學習到了,里面使用的Hashmap元素是Bean類型的,深拷貝下的元素修改,也會“打斷骨頭連着筋”地讓兩個Hashmap同時更新。
但是僅限於“元素修改”,如若“元素增刪”,那兩個Hashmap對象就“翻臉不認人”了,不會同步更新。
4、hashmap.clone()
JDK是個難得的學習材料,源碼還是要讀的。現在也粘貼過來,做一些記錄。
/** * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and * values themselves are not cloned. * 【咱們中文叫“深拷貝”,老外美其名曰“拷貝一份實例的'淺拷貝'”,更加嚴謹】 * @return a shallow copy of this map */ @SuppressWarnings("unchecked") @Override public Object clone() { HashMap<K,V> result; try { result = (HashMap<K,V>)super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } result.reinitialize(); result.putMapEntries(this, false); return result; }
/** * Implements Map.putAll and Map constructor * * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). */ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { int s = m.size(); if (s > 0) { if (table == null) { // pre-size float ft = ((float)s / loadFactor) + 1.0F; int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); if (t > threshold) threshold = tableSizeFor(t); } else if (s > threshold) resize(); for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); //【putVal方法里面我初步掃了一下,也未涉及Hashmap instance對象的新建,是一些Hashmap結構中的Node的新建】 putVal(hash(key), key, value, false, evict); } } }
以代碼最終解釋權由JDK1.8.x所有。