HashMap是HashTable的兄弟,继承AbstractMap,实现Cloneable/Serializable接口。AbstractMap又是Map的实现。
HashMap是通过计算存储对象的哈希值,以键值Entry数组来散列存储对象。
如果存储遇到哈希碰撞,新建一个Node类,以旧键值对为头部,从尾部插入新键值对。
如果链表的长度大于8,则该链表转换成红黑树存储碰撞键值对。(JDK 1.8)
HashMap的容量总是2的次幂,在能估算需要容量的情况下,赋予接近并大于(容量*1.333)的2次幂大小为优
本练习实现:
创建HashMap插入数据,检查长度,清除数据
遍历HashMap的4种方法
1.for each entry:entrySet
2.while iterator of entrySet hasNext
3.for each key:keySet
4.for each value:values
浅拷贝
1.HashMap.clone()
2.HashMap.putAll(Map<K,T>)
深拷贝
1.HashMapPrac.cloneDeep()
2.for each new entry put(map.entry.getKey(),new List(listOrigin))
结论:
- HashMap的浅拷贝里Copied EntrySet()地址与Original EntrySet()不同
所以直接修改EntrySet里的key或者value指向是没问题的。 - 如果Map存的是基本类型的包装类或者String,浅拷贝和深拷贝没区别
- 浅拷贝的真正的问题在于修改value或者key指向的,对象(比如一个List)的指向地址,会改变前者指向的地址从而产生意想不到的结果。
import java.util.*;
import java.io.*;
/*
* This class is meant to practise implementing the API of HashMap
*/
public class HashMapPrac{
public static void main(String[] args){
//1. instantiate a hashmap with default constructor
// then put some data
print("==========1==========");
var hashMap = new HashMap<String,String>();
print(hashMap.isEmpty());//true
hashMap.put("No.1","Oliver");//add data
hashMap.put("No.2","Kimiko");
hashMap.put("No.3","Matthew");
hashMap.put("No.4","Andrew");
print(hashMap.isEmpty());//false
print(hashMap.size()); //4
print(hashMap); //@override AbstractMap.toString()
//2. get a shallow copy of this hashmap
// then remove all of the mappings
print("==========2==========");
var hashMapCopyS = (HashMap)hashMap.clone();
print(hashMapCopyS.size());//4
print(hashMapCopyS);
hashMapCopyS.clear();
print(hashMapCopyS.size());//0
//3. is there a mapping value for No.1? Is there a key mapping to Oliver?
print("==========3==========");
print(hashMap.containsKey("No.1"));//true
print(hashMap.containsValue("Oliver"));//true
//4. create a new hashMap, add 10 Integer pair to it
//make sure value = 10*key
//print the pairs where values < 50
print("==========4==========");
var map = new HashMap<Integer,Integer>();
for(int i=1;i<=10;i++){
map.put(i,i*10);
}
//for each entry of entrySet()
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
if (entry.getValue()<50){
print("Key: "+entry.getKey()+" Value: "+entry.getValue());
}
}
print("");
//while iterator of entrySet hasNext()
Iterator iterator = map.entrySet().iterator();
while(iterator.hasNext()){
var entry = (Map.Entry<Integer,Integer>)iterator.next();
if (entry.getValue()<50){
print("Key: "+entry.getKey()+" Value: "+entry.getValue());
}
}
print("");
//for each key of keySet()
for(Integer key:map.keySet()){
if(map.get(key)<50){
print("Key: "+key+" Value "+map.get(key));
}
}
//5.print only the value of map where value <50
print("==========5==========");
for (Integer value:map.values()){
if(value<50){
print("Value: "+value);
}
}
//6.get a deep copy of hashmap(Integer,ArrayList<T>)
print("==========6==========");
//create the original hashmap
var list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
var mapOrigin = new HashMap<Integer,ArrayList<Integer>>();
mapOrigin.put(1,list);
//copy the original hashmap
var mapCopyDeep = new HashMap<Integer,ArrayList<Integer>>();
for (Map.Entry<Integer,ArrayList<Integer>> entry:mapOrigin.entrySet()){
mapCopyDeep.put(entry.getKey(),new ArrayList<Integer>(entry.getValue()));
}
//check if the values are pointing to the same list?
print(mapOrigin.get(1)==mapCopyDeep.get(1)); //false
// try copy using HashMap.putAll(Map<? extends K,? extends V>)
var mapCopy = new HashMap<Integer,ArrayList<Integer>>();
mapCopy.putAll(mapOrigin);
print(mapOrigin.get(1)==mapCopy.get(1)); //true, another shallow copy
//7. get a deep copy of mapOrigin, assumed that you dont know the
// classes of which entry key and value have
print("==========7==========");
mapCopy = cloneDeep(mapOrigin);
print(mapCopy + "\n" + mapOrigin);
print(mapOrigin.get(1)==mapCopy.get(1)); //false
}
/*
*Dizzy Lazy Fussy Busy
*/
public static void print(Object obj){
System.out.println(obj);
}
/*
* Deep copy T of which extends Serializable
*/
public static <T extends Serializable> T cloneDeep(T obj){
T clonedObj = null;
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
clonedObj = (T)ois.readObject();
ois.close();
}catch(Exception e){
e.printStackTrace();
}
return clonedObj;
}
}
输出:
==========1==========
true
false
4
{No.2=Kimiko, No.1=Oliver, No.4=Andrew, No.3=Matthew}
==========2==========
4
{No.2=Kimiko, No.1=Oliver, No.4=Andrew, No.3=Matthew}
0
==========3==========
true
true
==========4==========
Key: 1 Value: 10
Key: 2 Value: 20
Key: 3 Value: 30
Key: 4 Value: 40
Key: 1 Value: 10
Key: 2 Value: 20
Key: 3 Value: 30
Key: 4 Value: 40
Key: 1 Value 10
Key: 2 Value 20
Key: 3 Value 30
Key: 4 Value 40
==========5==========
Value: 10
Value: 20
Value: 30
Value: 40
==========6==========
false
true
==========7==========
{1=[1, 2, 3]}
{1=[1, 2, 3]}
false