94.HashMap

HashMap

Map接口的实现类有HashMap测试:

package cn.yzy.collection;

import java.util.HashMap;
import java.util.Map;

public class testMap {
	public static void main(String[] args) {
		
		Map<String, Integer> map = new HashMap<>();
		
		map.put("one", 1);
		map.put("two", 2);
		map.put("three", 3);
		System.out.println(map);
		
		System.out.println(map.get("one"));
		System.out.println(map.size());
		System.out.println(map.isEmpty());
		System.out.println(map.containsKey("one"));
		System.out.println(map.containsValue(2));
		System.out.println(map.containsKey("four"));
		System.out.println(map.containsValue(4));
		
		
		Map<String, Integer> map1 = new HashMap<>();
		map1.put("four", 4);
		map1.put("five", 5);
		
		map.putAll(map1);
		System.out.println(map);
		
//		map中键不能重复,如果重复新的会覆盖旧的(键的重复与否是通过equals方法判断的)
		map.put("four", 404);
		System.out.println(map);
		
		map.remove("four");
		System.out.println(map);
	}
}

测试Map的实现类HashMap存储自定义类型

package cn.yzy.collection;

import java.util.HashMap;
import java.util.Map;

/*
 * 测试Map的实现类HashMap存储自定义类型
 */
public class testMap2 {
	public static void main(String[] args) {
		Employee e1 = new Employee(202007171, "入门", 8000);
		Employee e2 = new Employee(202007172, "专业", 12000);
		Employee e3 = new Employee(202007173, "大神", 30000);
		
		Map<Integer, Employee> map = new HashMap<Integer, Employee>();
		map.put(1, e1);
		map.put(2, e2);
		map.put(3, e3);
		
		System.out.println(map.get(1).toString());
		System.out.println(map);
	}
}

//雇员信息类
class Employee{
	private int id;
	private String name;
	private double salary;
	
	public Employee(int id, String name, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
	
	public String toString() {
		return "id:"+getId()+" name:"+getName()+" salary"+getSalary();
	}
}

(1) 数组:占用空间连续。寻址容易,查询速度快。但是,增加和删除效率非常低。

(2) 链表:占用空间不连续。寻址困难,查询速度慢。但是,增加和删除效率非常高。

HashMap底层实现采用了哈希算法哈希表1,哈希表的本质就是“数组+链表, 因此HashMap在查找、删除、修改方面都有非常高的效率,HashMap是Map接口最常用的实现类。

由于底层采用了哈希表存储数据,我们要求键不能重复,如果发生重复,新的键值对会替换旧的键值对。

HashMap与HashTable的区别:

  1. HashMap: 线程不安全,效率高。允许key或value为null。

  2. HashTable: 线程安全,效率低。不允许key或value为null。

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。

Entry数组存储结构图
存储数据过程put(key,value)

计算哈希值:

简单和常用的算法是(相除取余算法):

hash值 = hashcode%数组长度

“除法”,效率低下。JDK后来改进了算法。首先约定数组长度必须为2的整数幂,这样采用位运算即可实现取余的效果:hash值 = hashcode&(数组长度-1)

JDK8中,当链表长度大于8时,链表就转换为红黑树,这样又大大提高了查找的效率。

模拟实现HashMap

实现节点:

package mycollection;

public class sEntry<K, V> {
	int hash; //哈希值
	K keyK;
	V vaV;
	sEntry<K, V> nextEntry;
	
	public sEntry() {
		super();
	}

	public sEntry(int hash, K keyK, V vaV, sEntry<K, V> nextEntry) {
		this.hash = hash;
		this.keyK = keyK;
		this.vaV = vaV;
		this.nextEntry = nextEntry;
	}
}

实现HashMap

package mycollection;

public class sHashMap<K, V> {
	@SuppressWarnings("rawtypes")
	sEntry[] table; //位桶数组 bucket array
	int size; //存放的键值对个数
	
	public sHashMap() {
		table = new sEntry[16]; //长度一般定义成2的整数幂
	}
	
	@SuppressWarnings("unchecked")
	public V get(K key) {
		int hash = myHash(key.hashCode(), table.length);
		sEntry<K, V> findEntry = table[hash];
		while(findEntry != null) {
			if(findEntry.keyK.equals(key))
				return (V)findEntry.vaV;
			findEntry = findEntry.nextEntry;
		}return null;
	}
	
	public int size() {
		return size;
	}
	
	public void put(K key, V value) {
		//是否需要扩容:
		
		//定义新的节点对象
		sEntry<K, V> newSEntry = new sEntry<K, V>();
		newSEntry.hash = myHash(key.hashCode(), table.length);
		newSEntry.keyK = key;
		newSEntry.vaV = value;
		newSEntry.nextEntry = null;
		
		@SuppressWarnings("unchecked")
		sEntry<K, V> tmpEntry = table[newSEntry.hash];
		sEntry<K, V> iSEntry = tmpEntry;
		if(tmpEntry == null)
			table[newSEntry.hash] = newSEntry;
		else {
			while(tmpEntry != null) {
				//判断键值是否重复,重复就覆盖并结束
				if(newSEntry.keyK.equals(tmpEntry.keyK)) {
					tmpEntry.vaV = newSEntry.vaV;
					return;
				}
				iSEntry = tmpEntry;
				tmpEntry = tmpEntry.nextEntry;
			}
			//键值不重复尾插
			iSEntry.nextEntry = newSEntry;
		}
		size++;
	}
	
	public int myHash(int k, int length) {
//		System.out.println(v&(length-1));
//		System.out.println(v%(length-1));
		return k&(length-1);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("{");
		for(int i=0; i<table.length; ++i) {
			sb.append(i + ":");
			sEntry<K, V> tmpEntry = table[i];
			while(tmpEntry != null) {
				sb.append("["+tmpEntry.keyK+","+tmpEntry.vaV+"]-->");
				tmpEntry = tmpEntry.nextEntry;
			}sb.append("null\n");
		}sb.setCharAt(sb.length()-1, '}');
		return sb.toString();
	}
	
	public void remove(K key) {
		@SuppressWarnings("unchecked")
		sEntry<K, V> tmpEntry = table[myHash(key.hashCode(), table.length)];
		sEntry<K, V> preEntry = tmpEntry;
		sEntry<K, V> curEntry = tmpEntry;
		if(curEntry.nextEntry == null) {
			table[myHash(key.hashCode(), table.length)] = null;
			size--;
			return ;
		}
		while(curEntry != null) {
			if(curEntry.keyK.equals(key)) {
				preEntry.nextEntry = curEntry.nextEntry;
				curEntry = null; //???????????
				size--;
				return ;
			}
			preEntry = curEntry;
			curEntry = curEntry.nextEntry;
		}
	}
	
	public static void main(String[] args) {
		sHashMap<Integer, String> sm = new sHashMap<Integer, String>();
		sm.put(10, "aa");
		sm.put(20, "bb");
		sm.put(30, "dd");
		sm.put(30, "cc");
		sm.put(46, "ee");
		sm.put(62, "ff");
		System.out.println(sm);
		
		System.out.println(sm.get(10));
		System.out.println(sm.get(20));
		System.out.println(sm.get(30));
		System.out.println(sm.get(46));
		System.out.println(sm.get(62));
		System.out.println(sm.size());
		
		sm.remove(46);
		System.out.println(sm.size());
//		System.out.println(sm);
		
		sm.remove(62);
		System.out.println(sm.size());
//		System.out.println(sm);
		
		sm.remove(30);
		System.out.println(sm.size());
		System.out.println(sm);
	}
}

思考以上模拟实现的HashMap的remove方法是否存在内存泄露问题?


  1. 哈希表,这是一种非常重要的数据结构。对于我们以后理解很多技术都非常有帮助(比如:redis数据库的核心技术和HashMap一样)
    结合数组和链表的优点(即查询快,增删效率也高) 就是“哈希表”,哈希表的本质就是“数组+链表 ↩︎

猜你喜欢

转载自blog.csdn.net/qq_43808700/article/details/107433567
94
L94