您的位置:首页 > 其它

每日一省之————利用数组和链表实现一个简单的HashMap

2016-09-03 15:54 489 查看
今天本人想要复习的是哈希表(散列表)的概念及具体实现,为此用java写了一个简单的实现,但仅仅只是实现了一些简单的功能,不过通过这个简单的实现的确可以帮助我们进一步理解JDK中的HashMap,当然,想要进一步了解就直接阅读JDK中的HashMap的源码啦。

实现代码如下,有注释,本人就不进一步阐述了:

/**
* 该类是HashMap的一个简单实现。
*
* 这里,我主要采用一个元素是链表的数组来模拟JDK中的HashMap实现。 当存入一个键值对(K,V)的时候,首先计算键(K)对应的HashCode。这个HashCode的值其实就是具体实现过程中用到的数组的索引。通过计算键的HashCode值便可以知道应该将键值对(K,V)存储在第几个数组元素中。如前所述,每一个数组元素其实又是一个链表。将键值对存入散列表其实是存入底层数组元素对应的链表结构中。之所以使用链表作为数组元素是为了避免HashCode相同但又不相等的键造成冲突时候相互覆盖的问题。这样就很好的解决了可能存在的碰撞冲突问题。
*
* @author lhever
*
* @param <K>
* @param <V>
*/
public class SimpleHashMap<K, V> {
public static final int INITIAL_CAPACITY = 4;

private int N;
private int M;
private SequentialST<K, V>[] table;

/**
* 构造
*/
public SimpleHashMap() {
this(INITIAL_CAPACITY);
}

/**
* 构造,可以指定底层数组的大小
*
* @param M
*/
public SimpleHashMap(int M) {
this.M = M;
table = (SequentialST<K, V>[]) new SequentialST[M];
for (int i = 0; i < M; i++) {
table[i] = new SequentialST<K, V>();
}

}

/**
* 重新调整底层数组的大小,从实现的角度看,所有已经 存入的键值对会被从新计算散列值并调整存储的位置。
*
* @param newSize
*/
private void resize(int newSize) {
SimpleHashMap<K, V> temp = new SimpleHashMap<K, V>(newSize);
for (int i = 0; i < M; i++) {
for (K key : table[i].keys()) {
temp.put(key, table[i].get(key));
}
}
this.M = temp.M;
this.N = temp.N;
this.table = temp.table;
}

private int hash(K key) {
return (key.hashCode() & 0x7fffffff) % M;
}

public int size() {
return N;
}

public boolean isEmpty() {
return size() == 0;
}

public boolean contains(K key) {
if (key == null) {
throw new NullPointerException("参数不能为null");
}
return get(key) != null;
}

public V get(K key) {
if (key == null) {
throw new NullPointerException("参数不能为null");

}
int i = hash(key);
return table[i].get(key);
}

public void put(K key, V val) {
if (key == null) {
throw new NullPointerException("key不能为null");
}
if (val == null) {
remove(key);
return;
}

// 当链表中的平均元素个数大于10的时候增加底层数组的大小(变为两倍大)
if (N >= 10 * M) {
resize(2 * M);
}

int i = hash(key);
if (!table[i].contains(key)) {
N++;
}
table[i].put(key, val);
}

public void remove(K key) {
if (key == null) {
throw new NullPointerException("参数不能为null");
}

int i = hash(key);
if (table[i].contains(key)) {
N--;
table[i].delete(key);
}

// 如果列表中的平均元素个数小于等于2的时候,降低底层数组的大小(变为原来大小的1/2)
if (M > INITIAL_CAPACITY && N <= 2 * M) {
resize(M / 2);
}
}

// 返回一个迭代器,其中包含了所有的键
public Iterable<K> keys() {
Queue<K> queue = new LinkedList<K>();
for (int i = 0; i < M; i++) {
for (K key : table[i].keys())
queue.offer(key);
}
return (Iterable<K>) queue;
}

/**
* 测试
*
* @param args
*/
public static void main(String... args) {

SimpleHashMap map = new SimpleHashMap<String, String>();

map.put("country", "China");
map.put("level", "super");
map.put("nature", "public");

System.out.println(map.get("country"));
System.out.println("befor remove, the size is: " + map.size());
map.remove("country");
System.out.println("after remove, the size is: " + map.size());

for (String key : (Iterable<String>) map.keys()) {
System.out.println(key + " : " + map.get(key));
}

map.remove("nature");
map.remove("level");
map.remove("level");
System.out.println("after remove, the size is: " + map.size());

for (String key : (Iterable<String>) map.keys()) {
System.out.println(key + " : " + map.get(key));
}

map.put("size", "960");
System.out.println("after put, the size is: " + map.size());
}

}

////////////////////////////////////////////////////

package lhever.Hash_Map;

import java.util.LinkedList;
import java.util.Queue;

/**
* 该类是一个顺序查找的链表实现,查找元素时候会从第一个节点开始顺序查找,
* 该链表实现会被用到SimpleHashMap(JDK中HashMap的一个简单实现)的实现中
*
* @author lhever
*
* @param <K>
* @param <V>
*/
public class SequentialST<K, V> {
private int N;
private Node first;

/**
* 代表一个节点
*
* @author lhever
*/
private class Node {

private K key;
private V value;
private Node next;

public Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}

}

public SequentialST() {

}

public int size() {
return N;
}

public boolean isEmpty() {
return size() == 0;
}

public boolean contains(K key) {
if (key == null)
throw new NullPointerException("参数不能为null");
return get(key) != null;
}

/**
* 查找从第一个节点开始顺序进行
*
* @param key
* @return
*/
public V get(K key) {
if (key == null)
throw new NullPointerException("参数不能为null");
for (Node node = first; node != null; node = node.next) {
if (key.equals(node.key))
return node.value;
}
return null;
}

/**
* 增加
*
* @param key
* @param value
*/
public void put(K key, V value) {
if (key == null)
throw new NullPointerException("参数不能为null");
if (value == null) {
delete(key);
return;
}

for (Node node = first; node != null; node = node.next) {
if (key.equals(node.key)) {
node.value = value;
return;
}
}
first = new Node(key, value, first);
N++;
}

public void delete(K key) {
if (key == null)
throw new NullPointerException("参数不能为null");
first = delete(first, key);
}

private Node delete(Node node, K key) {
if (node == null)
return null;
if (key.equals(node.key)) {
N--;
return node.next;
}
node.next = delete(node.next, key);
return node;
}

public Iterable<K> keys() {
Queue<K> queue = new LinkedList<K>();
for (Node node = first; node != null; node = node.next)
queue.offer(node.key);
return queue;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: