码迷,mamicode.com
首页 > 编程语言 > 详细

Java HashMap LinkedHashMap 区别及原理

时间:2015-07-25 22:58:43      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:java   hashmap   linked   原理   散列数组   

HashMap原理
HashMap是Map的一个常用的子类实现。其实使用散列算法实现的。
HashMap内部维护着一个散列数组(就是一个存放元素的数组),我们称其为散列桶,而当我们向HashMap中存入一组键值对时,HashMap首先获取key这个对象的hashcode()方法的返回值,然后使用该值进行一个散列算法,得出一个数字,这个数字就是这组键值对要存入散列数组中的下标位置。
那么得知了下标位置后,HashMap还会查看散列数组当前位置是否包含该元素。(这里要注意的是,散列数组中每个元素并非是直接存储键值对的,而是存入了一个链表,这个链表中的每个节点才是真实保存这组键值对的。)检查是否包含该元素时根据当前要存入的key在当前散列数组对应位置中的链表里是否已经包含这个key,若不包含则将这组键值对存入链表,否则就替换value。
那么在获取元素时,HashMap同样先根据key的hashcode值进行散列算法,找到它在散列数组中的位置,然后遍历该位置的链表,找到该key所对应的value之后返回。
看到这里可能有个疑问,链表中应该只能存入一个元素,那么HashMap是如何将key-value存入链表的某个节点的呢?实际上,HashMap会将每组键值对封装为一个Entry的实例,然后将该实例存入链表。
如图所示:
技术分享

HashMap的存取是依赖于key的hashcode方法的返回值的,而hashcode方法实际上是在Object中定义的。其定义如下:
int hashCode()
代码如下:

public class HashMapDemo {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<String,Integer>();
        hashMap.put("one", 1);
        hashMap.put("two", 2);
        hashMap.put("three", 3);
        hashMap.put("four", 4);
        hashMap.put("five", 5); 
        hashMap.put("six", null);

        //遍历Map中的key的hashMap
        for(String str : hashMap.keySet()) {
            System.out.println(str +":"+ str.hashCode() );
        }

    }
}

运行结果:
six:113890
four:3149094
one:110182
two:115276
three:110339486
five:3143346

遍历每一组键值对
Set

public class HashMapDemo {
    public static void main(String[] args) {
        Map<String, Integer> hashMap = new HashMap<String,Integer>();
        hashMap.put("one", 1);
        hashMap.put("two", 2);
        hashMap.put("three", 3);
        hashMap.put("four", 4);
        hashMap.put("five", 5); 
        hashMap.put("six", null);

        //遍历键值对
        Set<Entry<String, Integer>> set = hashMap.entrySet();
        for(Entry<String, Integer> e : set) {
            System.out.print("key:"+e.getKey()+"\t");
            System.out.println("value:"+e.getValue());
        }
    }
}

运行结果:
key:six value:null
key:four value:4
key:one value:1
key:two value:2
key:three value:3
key:five value:5

重写一个类的hashcode()方法有以下注意事项:
1、若一个类重写了equals方法,那么就应当重写hashcode()方法。
2、若两个对象的equals方法比较为true,那么它们应当具有相同的hashcode值。
3、对于同一个对象而言,在内容没有发生改变的情况下,多次调用hashCode()方法应当总是返回相同的值。
4、对于两个对象equals比较为false的,并不要求其hashcode值一定不同,但是应尽量保证不同,这样可以提高散列表性能。

代码如下:

public class HashMapDemo {
    public static void main(String[] args) {
        Map<Card, Person> map = new HashMap<Card, Person>();
        Person p1 = new Person(new Card("001"),"张三");  
        Person p2 = new Person(new Card("002"),"李四");  

        map.put(p1.getCard(), p1);  
        map.put(p2.getCard(), p2);  

        System.out.println("HashMap 中存放的人员信息:\n"+map);  

        //张三改名为张山,身份证号不变。  
        Person p3 = new Person(new Card("001"),"张山");  
        map.put(p3.getCard(), p3);  

        System.out.println("张三改名为张山后 HashMap 中存放的人员信息:\n"+map);  

        //查找身份证为001 的人员信息  
        System.out.println("查找身份证为:10001 的人员信息:"+map.get(new Card("001")));
    }
}

class Card {
    private final String IdCard;

    public Card(String idCard) {
        super();
        IdCard = idCard;
    }

    public String getIdCard() {
        return IdCard;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((IdCard == null) ? 0 : IdCard.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        // 如果地址一样,则两个对象相同 
        if (this == obj) {
            return true;
        }
        // 如果两个对象是同一类型,则比较其属性值是否都相同
        //如果都相同,则说明两个对象也相同;
        //否则,说明这两个对象不相同。
        if(obj instanceof Card) {
            Card card = (Card)obj;
            return card.IdCard .equals(this.IdCard);
        }
        return false;
    }

    @Override
    public String toString() {
        return "Card [IdCard=" + IdCard + "]";
    }

}


class Person {
    private Card card;
    private String name;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((card == null) ? 0 : card.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    /** 
     * 重写equals()方法 当两个人得身份证号相同以及姓名相同时,表示这两个人是同一个人。 
     */  
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {  
            return true;  
        }  
        if (obj instanceof Person) {  
            Person p = (Person) obj;  
            return this.card.equals(p.card) && this.name.equals(p.name);  
        }  
        return false;
    }

    public Person(Card card, String name) {
        super();
        this.card = card;
        this.name = name;
    }

    public Card getCard() {
        return card;
    }
    public void setCard(Card card) {
        this.card = card;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }

}

运行结果:
HashMap 中存放的人员信息:
{Card [IdCard=001]=Person [name=张三], Card [IdCard=002]=Person [name=李四]}
张三改名为张山后 HashMap 中存放的人员信息:
{Card [IdCard=001]=Person [name=张山], Card [IdCard=002]=Person [name=李四]}
查找身份证为:10001 的人员信息:Person [name=张山]

装载因子及HashMap优化

在散列表中有一下名词需要了解:
Capacity:容量, hash表里bucket(桶)的数量, 也就是散列数组大小.
Initial capacity:初始容量, 创建hash表的时 初始bucket的数量, 默认构建容量是16. 也可以使用特定容量.
Size : 大小, 当前散列表中存储数据的数量.
Load factor:加载因子, 默认值0.75(就是75%), 向散列表增加数据时如果 size/capacity 的值大于Load factor则发生扩容并且重新散列(rehash).
那么当加载因子较小时候散列查找性能会提高, 同时也浪费了散列桶空间容量. 0.75是性能和空间相对平衡结果. 在创建散列表时候指定合理容量, 从而可以减少rehash提高性能。
例如:

Map<String, Integer> map = new HashMap<String, Integer>(10000);

存入大量数据时可以使用该方法,减少扩容次数,提高性能
如果数据量很小,不推荐使用此方法,会造成空间浪费,用默认无参的即可

LinkedHashMap实现有序的Map

Map 接口的哈希表和链表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,LinkedHashMap维护着一个双向循环链表。此链表定义了迭代顺序,该迭代顺序通常就是将存放元素的顺序。
需要注意的是,如果在Map中重新存入以有的key,那么key的位置会不会发生改变,只是将value值替换。

总结:
HashMap和LinkedHashMap区别在于HashMap是无序的,LinkedHashMap是有序的。在使用HashMap时,要根据实际情况选择最优的解决方案,降低性能和空间上的浪费。

版权声明:本文为博主原创文章,未经博主允许不得转载。

Java HashMap LinkedHashMap 区别及原理

标签:java   hashmap   linked   原理   散列数组   

原文地址:http://blog.csdn.net/shf4715/article/details/47061111

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!