java中HashTable、HashMap、LinkedHashMap

  前面写了list下arrlist和linkedlist的区别也就没有下文了,抽空总结一下map下的一些类。

一、概述

  首先说一下三个map的介绍(treemap比较特殊,暂时忽略)

  1、hashtable:数组+单链表结构、线程安全(操作加锁)、无序、

  2、hashmap:数组+单链表结构、线程不安全、无序、

  3、linkedhashmap:继承了hashmap、数组+单链表结构、线程不安全、有序(1、插入顺序 2、lru:最少最近访问顺序 [采用双向链表存储顺序])

  上面就是三个map的大体特性,那么这些特性是怎么实现的呢,我们可以从几个方面切入源码分析三个map不同之处:构造函数、put方法、get方法、

二、底层结构

  1、HashTable:存储一个值时,先根据key算出所存入table数组的下角标i,如果table[i]为空,直接存入生成的entry,如果不为空则从该位置的entry头遍历,如果key相同,则覆盖value,如果没有相同的key的entry,则将新生成的entry放入该数组下角标的头部;

  /**         * hashtable用来存储数据的数组,数组中的元素是Entry,下面说明.
     */
    private transient Entry<K,V>[] table;

    /**
     * hashtable数组中的元素为Entry,是一个key和value的结构(类中方法省略)
     */
    private static class Entry<K,V> implements Map.Entry<K,V> {     //当前entry的hash值
        int hash;    //当前entry的key值
        final K key;    //当前enry的value值
        V value;    //当前entry的下一个entry(这里可以看出是一个单链表)
        Entry<K,V> next;
    }

  2、hashmap:跟hashtable完全相同的底层结构

    /**
     * 一个空entry数组
     */
    static final Entry<?,?>[] EMPTY_TABLE = {};
    /**
     * 跟hashtable一样,table数组,存放entry对象,唯一区别就是直接先初始化为空数组
     */
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

   //entry对象跟entry完全相同 
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
    }

  3、LinkedHashMap:下面的代码可以看到,linkedhashmap继承了hashmap,在hashmap的基础上加了hear双向链表和entry中的前后标记

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
      /**
     * 类中看出继承了hashmap,所以底层的数据格直接使用了hashmap的table.     这个hear是一个链表,head为链表的头,后面涉及到
     */
    private transient Entry<K,V> header;

  /**
     * accessorder属性:用来标记hear双向链表是根绝lru排序,还是插入顺序排序
     */
    private final boolean accessOrder;

}
  /**
     * linkedhashmap中的entry继承了hashmap中的enry
     */
    private static class Entry<K,V> extends HashMap.Entry<K,V> {
        // 多了两个属性:entry前 和 entry后的记录,这是给head用的
        Entry<K,V> before, after;
    }

三、构造函数(只说明大流程)

  1、hashtable

     * 最常用的构造函数,这里调用了this构造函数,传入两个参数11和0.75,下面说明参数意义
     */
    public Hashtable() {
        this(11, 0.75f);
    }

    /**
     * 可以看到,第一个参数为初始化的容量小,这里默认设置为11,第二个参数是扩容因子,默认当容器中有百分之75使用的时候进行扩容*/
    public Hashtable(int initialCapacity, float loadFactor) {      //check容量设置大小
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);      //check加载因子大小
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;     //根据初始容量大小,初始化entry数组(还记得上面hashtable是定义属性的时候就初始化了么)
        table = new Entry[initialCapacity];      //计算扩容的临界值:总容量*加载因子
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);      //计算hashseed参数,用来计算元素key的hash值
        initHashSeedAsNeeded(initialCapacity);
    }

  可以看到:

    1、hashtable默认初始化大小为11,默认扩容加载因子为0.75

    2、hashtable的数组容器在初始化时完成

  2、hashmap

    /**
     * 这里进行了代码优化,使用常量,传入两个参数为:16和0.75
     */
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    /**
     * 初始容量跟hashtable不一样,这里是16,加载因子都为0.75
     * capacity and load factor.*/
    public HashMap(int initialCapacity, float loadFactor) {      //check初始容量
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
      //设置加载因子 和 初始容量值
        this.loadFactor = loadFactor;
        threshold = initialCapacity;      //这个方法为空,可以看到hashmap只是将初始值设置,并没有跟hashtable一样进行计算,到目前位置table数组还是一个空数组      //(其实hashmap是在第一次put进元素的时候,才对hashmap的容量等信息进行初始化的,下面会看到)
        init();
    }

  这里可以看到hashmap构造函数做的内容:

    1、hashmap容器,默认容量16,扩容加载因子0.75

    2、构造函数只进行了参数check和参数设置,并没有将table设置为真正的大小,也没有计算扩容的容量临界值

  3、linkedhashmap

    /**
     * 完全复用的hashmap的构造函数。accessorder属性默认设置为false:就是hear链表按照插入顺序排序
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

  linkedhashmap没什么好说的,完全跟hashmap一样,多了一个head链表的排序设置。

四、put方法

  1、hashtable

  

  

    

    

时间: 2024-10-12 06:44:30

java中HashTable、HashMap、LinkedHashMap的相关文章

java中的HashMap解析

这篇文章准备从源码的角度带大家分析一下java中的hashMap的原理,在了解源码之前,我们先根据自己的理解创建一个hashMap. 先说明一下创建的具体原理是这样的,所谓hashMap,必然是用hash方法来区分不同的key值.学过hash的都知道,我们解决hash冲突的一种方法就是使用散列和桶,首先确定所在的桶号,然后在桶里面逐个查找.其实我们也可以单纯使用数组实现map,使用散列是为了获得更高的查询效率. 要写自己的hashmap前,必须说明一下两个方法,就是hashcode()和equa

关于Java中的HashMap的深浅拷贝的测试与几点思考

0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到一些Hashmap的赋值.这里的赋值非常有趣,三个Hashmap翻来覆去赋值,就解决了敏感词表的初始化. 里面都是属于下文中的Hashmap"浅拷贝",那么究竟Java中的Hashmap有哪些拷贝方法呢? 1.测试代码 HashMap hm_source = new HashMap();

Java中关于HashMap的使用和遍历(转)

Java中关于HashMap的使用和遍历 分类: 算法与数据结构2011-10-19 10:53 5345人阅读 评论(0) 收藏 举报 hashmapjavastringobjectiteratorlist 1:使用HashMap的一个简单例子 [java] view plaincopy package com.pb.collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; im

javascript 实现类似Java中的hashmap

应用场景: 想把函数和函数对应的数据比较直观的存储起来 突然觉得Javascript中的object不够用了  所以自己仿了个java中的hashmap key支持任何Javascript类型的数据 但是NaN Undefined 这种类型的没有实验 以下是源码 function hashmapfn(){      var p={      rkey:7,      size:0,      obj:{}      };      function getKey(key){      if(ty

Java中的HashMap和HashTable到底哪不同?

学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:456544752  我们一起学Java! HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的HashMap和HashTable基于JDK 1.7.0_67.源码见这里 1. 时间 HashTable产生于JDK 1.1,而HashMap产生

Java中的HashMap和HashTable到底哪不同?(原文参考来自码农网)

HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的HashMap和HashTable基于JDK 1.7.0_67.源码见这里 1. 时间 HashTable产生于JDK 1.1,而HashMap产生于JDK 1.2.从时间的维度上来看,HashMap要比HashTable出现得晚一些. 2. 作者 以下是HashTable的作者: 以下代码及注释来自ja

Java中HashTable和HashMap的区别

在Java中,HashTable和HashMap都是哈希表,那么它们有什么区别呢? 1.它们所继承的类不一样. HashTable和HashMap都实现了Map接口,但是它们所继承的类时不同的.HashTable继承了Dictionary类(Dictionary类已经过时),而HashMap则是继承了AbstractMap类.我们来看看它们的函数声明. HashTable声明: 1 public class Hashtable<K,V>extends Dictionary<K,V>

java该HashTable,HashMap和HashSet

同一时候我们也对HashSet和HashMap的核心方法hashcode进行了具体解释,见<探索equals()和hashCode()方法>. 万事俱备,那么以下我们就对基于hash算法的三个集合HashTable,HashSet和HashMap具体解释. 本文文件夹: 1. HashTable和HashMap的差别 2. HashSet和HashMap的差别 3. HashMap,HashSet工作原理 4. HashSet工作原理 5. 常见问题 1. HashTable和HashMap的

深入理解Java中的HashMap

HashMap继承自抽象类AbstractMap,抽象类AbstractMap实现了Map接口.关系图如下所示: import java.util.*; public class SimpleMap<K,V> extends AbstractMap<K,V> { //keys存储所有的键 private List<K> keys = new ArrayList<K>(); //values存储所有的值 private List<V> values

深入理解Java中的HashMap的实现原理

HashMap继承自抽象类AbstractMap,抽象类AbstractMap实现了Map接口.关系图例如以下所看到的: Java中的Map<key, value>接口同意我们将一个对象作为key.也就是能够用一个对象作为key去查找还有一个对象. 在我们探讨HashMap的实现原理之前,我们先自己实现了一个SimpleMap类,该类继承自AbstractMap类. 详细实现例如以下: import java.util.*; public class SimpleMap<K,V>