java

 

       这几天研究hashmaphashtable,决定自己手动实现一个类似系统hashmaphashtable的东西。真正写的时候却问题重重。哎,真是苦不堪言呐。下面我就来回顾一下我走的一些弯路。

       开始搞这个的时候,为了能更加深的了解hashmaphashtable我特意去看了系统实现的hashmap,让我万万没想到的是,这恰恰成了我后来写代码的思想包袱。不知道看了多少遍源代码,反正有几个关键的地方没看懂。第一,就是hash算法。第二就是rehash

       系统给的hash方法很有意思,它先通过从object继承过来的hashCode方法计算key的值,然后在此基础上有进行各种移位操作。之后就是系统计算出来的哈希码了。现在想起来当时有点死脑筋了,反正不就是一个哈希码,为什么非得弄清楚系统那些移位操作的意义呢。

 

 

static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
 

 

系统的rehash更加难懂,它注释倒是写的很到位,密密麻麻写了八九行。但是由于英文水平有限,我不懂它的意思。所以我就直接看代码了。

 

 

void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }
  void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
}

 

 

我就知道它的大概意思是先扩容,然后重新把里面的值在进行一次hash来确定扩容后的位置是正确的。

后面越看原码我反而感觉我越不会写hashmap了,没办法就逼着自己新建一个类来实现它。

 

第一步:我知道hashmap是继承map接口的。

 

 

public class JHashTable<K, V> implements Map<K, V>

 

 

第二步:当时思路很乱,感觉对源码的依赖性十分严重。我把原码所有的方法都看了一遍之后,才发现一共需要6个方法,分别是增删改查 hash  rehash

 

 

public synchronized int hash(Object k)
	private synchronized void rehash()
	public synchronized  int size()
public synchronized V get(Object key)
	public synchronized V put(K key, V value)
public synchronized V remove(Object key)

 

 

第三步:我发现需要用一种数据结构来存放key value的映射关系。

 

 

	static class Entry<K, V> {
		int hash;
		K key;
		V value;
		Entry<K, V> next;
		Entry() {
		}
		Entry(int h, K k, V v) {
			this.key = k;
			this.value = v;
			this.hash = h;
		}
}

 

 

第四步:实现方法。

 

 

public synchronized int hash(Object k) {
		int t = k.hashCode();
		t = t % capacity;
		return t;
	}

	private synchronized void rehash() {

		int oldCapacity = table.length;
		Entry<K, V>[] oldMap = table;

		int newCapacity = oldCapacity * 2;
		Entry<K, V>[] newMap = new Entry[newCapacity];
		table = newMap;
		for (int i = 0; i < oldCapacity; i++) {
			for (Entry<K, V> e = oldMap[i]; e != null; e = e.next) {
				e.hash = hash(e.key);
				Entry<K, V> prev = new Entry<K, V>();
				for (Entry<K, V> elem = newMap[e.hash]; elem != null; prev = elem, elem = elem.next) {
				}
				prev.next = e;
			}
		}
	}

	@Override
	public synchronized  int size() {
		// TODO Auto-generated method stub
		return size;
	}
@Override
	public synchronized V get(Object key) {
		// TODO Auto-generated method stub
		if (key == null)
			throw new RuntimeException("key 的值不能为空");
		int t = hash(key);
		for (Entry<K, V> e = table[t]; e != null; e = e.next) {
			if (e.key.equals(key))
				return e.value;
		}
		return null;
	}

	@Override
	public synchronized V put(K key, V value) {
		// TODO Auto-generated method stub
		if (key == null || value == null)
			throw new RuntimeException("key 和value的值不能为空");
		int h = hash(key);
		if (table[h] == null) {// 头结点为空
			size++;
			table[h] = new Entry<K, V>(h, key, value);
			return null;
		}
		Entry<K, V> eloc = new Entry<K, V>();
		eloc = table[h];

		// 覆盖已有元素
		for (Entry<K, V> e = table[h]; e != null; e = e.next) {
			if (h == e.hash && e.key.equals(key)) {
				V oldValue = e.value;
				e.value = value;
				return oldValue;
			}
			eloc = e;
		}
		// 带插入元素是一个新的元素
		size++;
		Entry<K, V> e = new Entry<K, V>(h, key, value);
		eloc.next = e;
		return null;
	}

	@Override
	public synchronized V remove(Object key) {
		// TODO Auto-generated method stub
		if (key == null)
			throw new RuntimeException("key的值不能为空");
		int h = hash(key);
		Entry<K, V> prev = table[h];
		for (Entry<K, V> e = table[h]; e != null; prev = e, e = e.next) {
			if (e.hash == h && e.key.equals(key)) {
				prev.next = e.next;
				V t = e.value;
				e = null;
				size--;
				return t;
			}
		}
		return null;
	}

 

 

过程看上去是很简单的。其实实际上要写的话也没有太大的问题。但是好像我心境上出了点问题,我感觉实现第四步的时候我很纠结。看了源代码之后,总感觉我写起代码来畏畏缩缩,每写几句都感觉好像有什么地方不对劲。写着写着就发现好像有什么地方拉掉了,有什么情况没有考虑到。这时候我又回头去看源代码,看懂之后发现源代码结构写法什么的都很合理。比我自己写的感觉要高不知多少个档次。然后又回头写代码要想老半天,想找一个目前我觉得比较高端的方法,但发现好像搞不成。就用最笨的方法去实现它。带给我心理上的落差很大。我自己写hashmap的时候没有考虑到可以存空的keyvalue的情况,还有equals方法的问题。晚上写到1点多了,实在想睡觉了,就先把它改成实现hashtable了。

这种学习状态,不大对头呀。增删改查,都是比较简单的,而且hashrehash在不考虑性能的前提下,也不难。怎么造成我效率这么低呢。那个源代码真的不该在我自己实现hashmap的时候看呐。把我的节奏全部打乱了,差点迷失了方向。不过现在回想起来,这也是值得的。

哎不说了,一个技术博客被我写的这么没技术。以后好好努力吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值