Java HashMap的实现

HashMap是Java中经常使用的集合类。HashMap的每个元素是一个<Key,Value>键值对,在内部用数组来保存每个元素,hash函数将Key作为参数,计算出Value的存储位置,即数组的下标。实现HashMap,关键在于找到一个hash函数,使其尽量少的发生冲突。那么,何为冲突?如下图所示,


当值“F"插入HashMap集合时,发现他所对应的位置已有数据插入,此时便发生了冲突。解决冲突的办法这里列举三种:linear probing, double hashing, 和chaining。


linear probing方法

linear probing解决冲突的方式是:当冲突发生,后来的数据依次往后寻找空闲位置,一旦找到,便插入。如下图所示:



当值"F"插入时,因为原有位置已被"D"占领,则依次往后查找,在值"C"之后发现空闲位置并插入。查找到数组的尾部时,返回到头部继续查找,如下图所示:


如果查找到本应输入的位置,依旧没有可插入的位置,说明hashmap存满,如下图所示:


类HashMapNode表示HashMap中的每个元素,它主要有两个成员:Key和Value。实现如下:

public class HashMapNode {
	private Object key;
	private Object value;
	// construction
	public HashMapNode(Object key, Object value){
		this.key = key;
		this.value = value;
	}
	// get methods
	public Object getKey(){
		return key;
	}
	public Object getValue(){
		return value;
	}
	// set method
	public void setValue(Object newValue){
		this.value = newValue;
	}
}


类HashMap是采用linear probing方式的hashmap实现,具体如下:

import java.util.ArrayList;
import java.util.List;

public class HashMap<K, V> {
  private int multiplier;
  private int modulus;
  private int hashMapSize;//HashMap容量
  private HashMapNode[] array;
  private int size = 0;//HashMap现有元素个数

  
  // construction
  // construct a HashMap with 4000 places and given hash paarameters
  public HashMap(int multiplier, int modulus){
    this.multiplier = multiplier;
    this.modulus = modulus;
    this.hashMapSize = 4000;
    this.array = new HashMapNode[this.hashMapSize];
  }
  
   // construct a HashMap with given capacity and given hash parameters
  public HashMap(int hashMapSize, int multiplier, int modulus){
    this.multiplier = multiplier;
    this.modulus = modulus;
    this.hashMapSize = hashMapSize;
    this.array = new HashMapNode[this.hashMapSize];
  }
 
  // hashing
  public int hash(K key){
    return Math.abs(multiplier * key.hashCode()) % modulus % this.hashMapSize;
  }
  
  // size
  public int size(){
    return size;
  }
  
  // return the number of nodes currently stored in the map
  public boolean isEmpty(){
    return size == 0;
  }
  
  // interface methods
  @SuppressWarnings("unchecked")
  public List<K> keys(){
    List<K> list = new ArrayList<K>();
    for(int i = 0; i < this.hashMapSize; i++){
      if(array[i] != null){
        list.add((K)array[i].getKey());
      }
    }
    return list;
  }
  
  @SuppressWarnings("unchecked")
  public V put(K key, V value){
    int hashCode = hash(key);//hashCode是value本应存放的位置
    if(array[hashCode] == null){//该位置是空的,则直接放入,不需要处理冲突
      HashMapNode node = new HashMapNode(key,value);
      array[hashCode] = node;
      size++;
      return null;
    }else if(key.equals((K)array[hashCode].getKey())){//该主键已存在,则更新对应的值
      V oldValue = (V)array[hashCode].getValue();
      array[hashCode].setValue(value);
      return oldValue;
    }
    int probe = (hashCode + 1) % this.hashMapSize;//开始处理冲突
    while(probe != hashCode){
      if(array[probe] == null){//找到空闲位置
        HashMapNode node = new HashMapNode(key,value);
        array[probe] = node;
        size++;
        return null;
      }else if(key.equals((K)array[probe].getKey())){//该主键已存在,则更新对应的值
        V oldValue = (V)array[probe].getValue();
        array[probe].setValue(value);
        return oldValue;
      }else{
        probe = (probe + 1) % this.hashMapSize;
      }
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  public V get(K key){
    int hashCode = hash(key);
    if(array[hashCode] != null && key.equals((K)array[hashCode].getKey())){
      return (V)array[hashCode].getValue();
    }
    int probe = (hashCode + 1) % this.hashMapSize;
    while(probe != hashCode){//依次往后查找,直到回到原点
      if(array[probe] == null){
        probe = (probe + 1) % this.hashMapSize;
        continue;
      }else if(key.equals((K)array[probe].getKey())){
        return (V)array[probe].getValue();
      }else{
        probe = (probe + 1) % this.hashMapSize;
      }
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  public V remove(K key){
    int hashCode = hash(key);
    if(array[hashCode] != null && key.equals((K)array[hashCode].getKey())){
        V oldValue = (V)array[hashCode].getValue();
        array[hashCode] = null;//该位置设置为null,表示被删除
        size--;
        return oldValue;
    }
    int probe = (hashCode + 1) % this.hashMapSize;
    while(probe != hashCode){
      if(array[probe] == null){
        probe = (probe + 1) % this.hashMapSize;
        continue;
      }else if(key.equals((K)array[probe].getKey())){
        V oldValue = (V)array[probe].getValue();
        array[probe] = null;
        size--;
        return oldValue;
      }else{
        probe = (probe + 1) % this.hashMapSize;
      }
    }
    return null;
  }
}

double hashing 方法

double hashing方法,思想是当发生冲突时,结合第二个hash函数,新生成一个hashCode,直到不发生冲突为止。此处使用的第二个hash函数如下:secondaryModulus   (abs(hashCode(key)) mod secondaryModulus),计算元素下标值的方法如下:hash(key) + j * secondary(key)。j从0开始逐渐递增,直到不冲突为止。具体实现如下:

import java.util.ArrayList;
import java.util.List;

public class DoubleHashMap<K, V> {
	private int multiplier;
	private int modulus;
	private int secondaryModulus;
	private int hashMapSize;
	private HashMapNode[] array;
	private int size = 0;
	
	// construction
	// construct a HashMap with 4000 places and given hash paarameters
	public DoubleHashMap(int multiplier, int modulus, int secondaryModulus){
		this.multiplier = multiplier;
		this.modulus = modulus;
		this.secondaryModulus = secondaryModulus;
		this.hashMapSize = 4000;
		this.array = new HashMapNode[this.hashMapSize];
	}
	
	// construct a HashMap with given capacity and given hash parameters
	public DoubleHashMap(int hashMapSize, int multiplier, int modulus, int secondaryModulus){
		this.multiplier = multiplier;
		this.modulus = modulus;
		this.secondaryModulus = secondaryModulus;
		this.hashMapSize = hashMapSize;
		this.array = new HashMapNode[this.hashMapSize];
	}
	
	// hashing
	public int hash(K key){
		return Math.abs(multiplier * key.hashCode()) % modulus;
	}
	
	public int secondaryHash(K key){
		return this.secondaryModulus - Math.abs(key.hashCode()) % this.secondaryModulus;
	}
	
	// size
	public int size(){
		return size;
	}
	// return the number of nodes currently stored in the map
	public boolean isEmpty(){
		return size == 0;
	}
	
	// interface methods
	@SuppressWarnings("unchecked")
	public List<K> keys(){
		List<K> list = new ArrayList<K>();
		for(int i = 0; i < this.hashMapSize; i++){
			if(array[i] != null){
				list.add((K)array[i].getKey());
			}
		}
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public V put(K key, V value){
		int hashCode = hash(key);//calculating the hash code
		if(array[hashCode % this.hashMapSize] == null){//if the hash item is empty, add the data straight away
			HashMapNode node = new HashMapNode(key,value);
			array[hashCode % this.hashMapSize] = node;
			size++;
			return null;
		}else if(key.equals((K)array[hashCode % this.hashMapSize].getKey())){
			V oldValue = (V)array[hashCode % this.hashMapSize].getValue();
			array[hashCode % this.hashMapSize].setValue(value);
			return oldValue;
		}

		int secondHashCode = secondaryHash(key);
		int probe = 0;//variable to store probing location
		int j = 0;
		V retValue = null;
		while(true){
			j++;
			probe = (hashCode + j * secondHashCode) % this.hashMapSize;
			if(array[probe] == null){
				HashMapNode node = new HashMapNode(key,value);
				array[probe] = node;
				size++;
				break;
			}else if(key.equals((K)array[probe].getKey())){
				retValue = (V)array[probe].getValue();
				array[probe].setValue(value);
				break;
			}
		}
		return retValue;
	}

	@SuppressWarnings("unchecked")
	public V get(K key){
		int hashCode = hash(key) % this.hashMapSize;
		if(array[hashCode] != null && key.equals((K)array[hashCode].getKey())){
			return (V)array[hashCode].getValue();
		}
		int probe = (hashCode + 1) % this.hashMapSize;
		while(probe != hashCode){
			if(array[probe] == null){
				probe = (probe + 1) % this.hashMapSize;
				continue;
			}else if(key.equals((K)array[probe].getKey())){
				return (V)array[probe].getValue();
			}else{
				probe = (probe + 1) % this.hashMapSize;
			}
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public V remove(K key){
		int hashCode = hash(key) % this.hashMapSize;
		if(array[hashCode] != null && key.equals((K)array[hashCode].getKey())){
				V oldValue = (V)array[hashCode].getValue();
				array[hashCode] = null;
				size--;
				return oldValue;
		}
		int probe = (hashCode + 1) % this.hashMapSize;
		while(probe != hashCode){
			if(array[probe] == null){
				probe = (probe + 1) % this.hashMapSize;
				continue;
			}else if(key.equals((K)array[probe].getKey())){
				V oldValue = (V)array[probe].getValue();
				array[probe] = null;
				size--;
				return oldValue;
			}else{
				probe = (probe + 1) % this.hashMapSize;
			}
		}
		return null;
	}
	
}

chaining 方法

chaining方法的核心思想是,当发生冲突时,将冲突的元素用链表的形式连接起来,如下图所示:


ChainingHashMap类表示每个元素,具体实现如下:

public class ChainingHashMapNode {
	private Object key;
	private Object value;
	private ChainingHashMapNode next;
	// construction
	public ChainingHashMapNode(Object key, Object value){
		this.key = key;
		this.value = value;
		this.next = null;
	}
	// get methods
	public Object getKey(){
		return key;
	}
	public Object getValue(){
		return value;
	}
	public ChainingHashMapNode getNext(){
		return next;
	}
	// set methods
	public void setValue(Object newValue){
		this.value = newValue;
	}
	public void setNext(ChainingHashMapNode next){
		this.next = next;
	}
}


使用chaining方法的HashMap实现如下:

import java.util.ArrayList;
import java.util.List;

public class ChainingHashMap<K, V> {
	private int multiplier;
	private int modulus;
	private ChainingHashMapNode[] array;
	private int hashMapSize;
	private int size;
	
	// construction
	// construct a HashMap with 4000 places and given hash parameters
	public ChainingHashMap(int multiplier, int modulus){
		this.multiplier = multiplier;
		this.modulus = modulus;
		this.hashMapSize = 4000;
		this.array = new ChainingHashMapNode[this.hashMapSize];
		this.size = 0;
	}

	// construct a HashMap with given capacity and given hash parameters
	// hashing
	public ChainingHashMap(int hashMapSize, int multiplier, int modulus){
		this.multiplier = multiplier;
		this.modulus = modulus;
		this.hashMapSize = hashMapSize;
		this.array = new ChainingHashMapNode[this.hashMapSize];
		this.size = 0;
	}

	public int hash(K key){
		return Math.abs(multiplier * key.hashCode()) % modulus  % this.hashMapSize;
	}
	// size
	public int size(){
		return size;
	}
	// return the number of nodes currently stored in the map
	public boolean isEmpty(){
		return size == 0;
	}
	// interface
	
	
	@SuppressWarnings("unchecked")
	public List<K> keys(){
		List<K> list = new ArrayList<K>();
		for(int i = 0; i < this.hashMapSize; i++){
			ChainingHashMapNode node = array[i];
			while(node != null){
				list.add((K)node.getKey());
				node = node.getNext();
			}
		}
		return list;
	}
	
	@SuppressWarnings("unchecked")
	public V put(K key, V value){
		int hashCode = hash(key);
		if(array[hashCode] == null){//没有发生冲突,放入首位
			ChainingHashMapNode node = new ChainingHashMapNode(key,value);
			array[hashCode] = node;
			size++;
			return null;
		}
		ChainingHashMapNode node = array[hashCode];
		ChainingHashMapNode pre = node;
		while(node != null){
			if(key.equals((K)node.getKey())){//已存在Key,更新值
				V oldValue = (V)node.getValue();
				node.setValue(value);
				return oldValue;
			}else{
				pre = node;
				node = node.getNext();
			}
		}
		ChainingHashMapNode newNode = new ChainingHashMapNode(key,value); //在链表末尾添加冲突的元素
		pre.setNext(newNode);
		size++;
		return null;
	}

	@SuppressWarnings("unchecked")
	public V get(K key){
		int hashCode = hash(key);
		ChainingHashMapNode node = array[hashCode];
		while(node != null){
			if(key.equals((K)node.getKey())){
				return (V)node.getValue();
			}else{
				node = node.getNext();
			}
		}
		return null;
	}
	@SuppressWarnings("unchecked")
	public V remove(K key){
		int hashCode = hash(key);
		ChainingHashMapNode node = array[hashCode];
		if(node != null && key.equals((K)node.getKey())){
			array[hashCode] = node.getNext();
			size--;
			return (V)node.getValue();
		}
		ChainingHashMapNode pre = node;
		node = node.getNext();
		while(node != null){
			if(key.equals((K)node.getKey())){
				pre.setNext(node.getNext());
				size--;
				return (V)node.getValue();
			}else{
				pre = node;
				node = node.getNext();
			}
		}
		return null;
	}
}

参考: http://www.cs.rmit.edu.au/online/blackboard/chapter/05/documents/contribute/chapter/05/linear-probing.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值