HashMap快速的原因

使用自定义的类作为HashMap的键,必须重载hashCode()equals()方法,因为这两个方法都是继承自Object类,默认是基于对象地址计算hashCode()equals()方法。

hashCode()并不需要总是返回唯一的标识码。

HashMap或者HashSet快的原因:

其他查询慢的原因是对于键或者值查询上,因为键或者只没有按特定顺序保存,所以只能采用简单的线性查询,而线性查询是最慢的查询方式。

散列的价值在于速度。由于瓶颈位于键或值的查询速度,因此解决方案之一就是保持键或值的排序状态,然后使用Collections.binarySearch()进行查询。

散列则更进一步,它将键保存在某处,以便能够快速找到。存储一组元素最快的数据结构是数组,所以使用它来表示键的信息,但是数组是固定大小的,我们需要能够保存可变大小的数据。因此数组必须不保存键本身,而是通过键生成一个数字作为数组的下标,这个数字就是散列码,由定义在Object中的、或者是自定义的类覆盖的hashCode()方法生成。

为解决数组容量被固定的问题,不同的键可以生成相同的下标,也就可能产生冲突。因此数组多大就不重要了,任何键在数组中总能找到自己的位置。

于是查询一个值的过程首先就是计算散列码,然后使用散列码查询数组。如果能够保证数组下标没有冲突,那就有了一个完美的散列函数,但这种情况是特例。通常,冲突由外部链接处理:数组并不直接保存只,而是保存值的list,然后对list中的值使用equals()方法进行线性的查询,这也就是为什么HashSetHashMap存放自定义类需要重写hashCode()equals()方法的原因。这部分的查询自然会比较慢,但是,如果散列函数好的话,数组的每个位置就会有较少的值。这样就不是查询整个list,而是快速跳转到数组的某个位置,只对较少的元素进行比较。这便是HashMap如此快的原因。

下面实现一个简单的散列Map

import java.util.Map;

public class MapEntry<K,V> implements Map.Entry<K, V>
{
    private K key;
    private V value;
    public MapEntry(K key,V value)
    {
        this.key=key;
        this.value=value;
    }
    public K getKey(){
        return key;
    }
    public V getValue(){
        return value;
    }
    public V setValue(V v){
        V result=value;
        value=v;
        return result;
    }
    public int hashCode(){
        return (key==null?0:key.hashCode())^(value==null?0:value.hashCode());
    }
    public boolean equals(Object o){
        if (!(o instanceof MapEntry))
        {
            return false;
        }
        MapEntry map=(MapEntry)o;
        return (key==null?map.getKey()==null:key.equals(map.getKey()))
            &&(value==null?map.getValue()==null:value.equals(map.getValue()));
    }
    @Override
    public String toString()
    {
        return key+"="+value;
    }
    
}
import java.util.*;

public class SimpleHashMap<K,V> extends AbstractMap<K, V>
{
    static final int SIZE=997;
    LinkedList<MapEntry<K,V>>[] buckets=new LinkedList[SIZE];
    public V put(K key,V value){
        V oldValue=null;
        int index=Math.abs(key.hashCode())%SIZE;
        if (buckets[index]==null)
        {
            buckets[index]=new LinkedList<MapEntry<K, V>>();
        }
        LinkedList<MapEntry<K, V>> bucket=buckets[index];
        MapEntry<K, V> pair=new MapEntry<K, V>(key, value);
        boolean found=false;
        ListIterator<MapEntry<K, V>> it=bucket.listIterator();
        while (it.hasNext())
        {
            MapEntry<K, V> iPair=it.next();
            if (iPair.getKey().equals(key))
            {
                oldValue=iPair.getValue();
                it.set(pair);
                found=true;
                break;
            }
        }
        if(!found){
            buckets[index].add(pair);
        }
        return oldValue;
    }
    public Set<Map.Entry<K, V>> entrySet(){
        Set<Map.Entry<K, V>> set=new HashSet<Map.Entry<K,V>>();
        for (LinkedList<MapEntry<K, V>> bucket:buckets)
        {
            if(bucket==null) continue;
            for(MapEntry<K, V> mpair:bucket)
                set.add(mpair);
        }
        return set;
    }
    public static void main(String[] args)
    {
        SimpleHashMap<String, String> m=new SimpleHashMap<String,String>();
        m.put("吉林", "长春");
        m.put("山西", "太原");
        m.put("湖北", "武汉");
        m.put("江苏", "南京");
        m.put("浙江", "杭州");
        m.put("四川", "成都");
        System.out.println(m);
        System.out.println(m.get("浙江"));
        System.out.println(m.entrySet());
    }
}
执行结果如下:

{江苏=南京,山西=太原, 湖北=武汉, 吉林=长春, 四川=成都, 浙江=杭州}

杭州

[江苏=南京, 山西=太原, 湖北=武汉, 吉林=长春, 四川=成都, 浙江=杭州]

以上内容摘自《Think In Java Edition4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值