`
128kj
  • 浏览: 604093 次
  • 来自: ...
社区版块
存档分类
最新评论

学习笔记:三数组实现的HashMap

阅读更多
  网上的一个HashMap代码,用三个数组实现,不同于jdk中的实现方式。处理哈希冲突是采用二次哈希(再哈希)的策略,学习了一把,个别地方可能没有理解到位。写了一些注释,如果有错误,敬请指出。

public final class LongHashMap {

    
    protected long table[];//存放键,类型为long,应该是用于特殊场所
    protected Object values[];//存放值
    protected byte state[];//state[i]=0,1,2表示table[i]与values[i]没有使用,已经使用,已删除
    protected int freeEntries;//空闲的空间数

    protected int distinct;//当前存了多少对键值
 
    protected int lowWaterMark;//当LongHashMap存放的键值对少于此数时,将重新调整(再哈希)
    protected int highWaterMark;//当LongHashMap存放的键值对大于此数时,将重新调整,由容量和装载因子决定

   
    protected double minLoadFactor;//最小装载因子
    protected double maxLoadFactor;//最大装载因子

   // 如果元素放得太满,就必须进行rehash(再哈希)。再哈希使空间增大,并将原有的对象重新导入新的LongHashMap中,
   //而原始的LongHashMap被删除。loadfactor(装载因子)决定何时要对LongHashMap进行再哈希。
    protected static final int DEFAULT_CAPACITY = 277;//缺省的容量,一个素数
    protected static final double DEFAULT_MIN_LOAD_FACTOR = 0.2;//缺省的最小的装载因子
    protected static final double DEFAULT_MAX_LOAD_FACTOR = 0.6;//缺省的最大的装载因子

    protected static final byte FREE = 0;
    protected static final byte FULL = 1;
    protected static final byte REMOVED = 2;

   //用缺省的容量构建HashMap
    public LongHashMap() {
        this(DEFAULT_CAPACITY);
    }

   //构造函数
    public LongHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_MIN_LOAD_FACTOR, DEFAULT_MAX_LOAD_FACTOR);
    }

   //构造函数
    public LongHashMap(int initialCapacity, double minLoadFactor, double maxLoadFactor) {
        setUp(initialCapacity,minLoadFactor,maxLoadFactor);
    }

    //使用指定的初始化容量,最小装载因子,最大装载因子构建LongHashMap
    protected void setUp(int initialCapacity, double minLoadFactor, double maxLoadFactor) {
        if (initialCapacity < 0) {//参数检查
            throw new IllegalArgumentException(
                "Initial Capacity must not be less than zero: "+ initialCapacity
            );
        }
        if (minLoadFactor < 0.0 || minLoadFactor >= 1.0) {
                throw new IllegalArgumentException(
                    "Illegal minLoadFactor: "+ minLoadFactor
                );
        }
        if (maxLoadFactor <= 0.0 || maxLoadFactor >= 1.0) {
                throw new IllegalArgumentException(
                    "Illegal maxLoadFactor: "+ maxLoadFactor
                );
        }
        if (minLoadFactor >= maxLoadFactor) {
                throw new IllegalArgumentException(
                    "Illegal minLoadFactor: " + minLoadFactor +
                    " and maxLoadFactor: " + maxLoadFactor
                );
        }

        int capacity = initialCapacity;
        capacity = nextPrime(capacity);//程序将调整初始化容量,使之为素数
      
        if (capacity==0) {
            capacity=1;
        }

        this.table = new long[capacity];//关键字数组
        this.values = new Object[capacity];//值数组
        this.state = new byte[capacity];//状态数组

       
        this.minLoadFactor = minLoadFactor;
        
        if (capacity == LARGEST_PRIME) this.maxLoadFactor = 1.0;
        else this.maxLoadFactor = maxLoadFactor;

        this.distinct = 0;//开始时,LongHashMap中没有存入键值对
        this.freeEntries = capacity; // 开始时空闲的空间数=容量
       
        this.lowWaterMark = 0;

        // Math.min(capacity-2, (int) (capacity * maxLoadFactor));
        this.highWaterMark = chooseHighWaterMark(capacity, this.maxLoadFactor);
    }

     
     //扩容量,一个素数
     private int chooseGrowCapacity(int size, double minLoad, double maxLoad) {
        return nextPrime(Math.max(size+1, (int) ((4*size / (3*minLoad+maxLoad)))));
    }

     public boolean put(long key, Object value) {//在LongHashMap中存放键值对
        int i = indexOfInsertion(key);
        if (i<0) { 
            i = -i -1;
            this.values[i]=value;
            return false;
        }
         if (this.distinct > this.highWaterMark) {//当存的健值对超过highWaterMark时,将重新构建LongHashMap
            int newCapacity = chooseGrowCapacity(
                    this.distinct+1,
                    this.minLoadFactor,
                    this.maxLoadFactor
                );
            rehash(newCapacity);//用新容量重新构造
            return put(key, value);
        }

        this.table[i]=key;
        this.values[i]=value;
        if (this.state[i]==FREE) this.freeEntries--;//剩余空间少了一个
        this.state[i]=FULL;
        this.distinct++;//当前存放的键值对数目加1

        if (this.freeEntries < 1) {
            int newCapacity = chooseGrowCapacity(
                    this.distinct+1,
                    this.minLoadFactor,
                    this.maxLoadFactor
                );
            rehash(newCapacity);//用新容量重新构造
        }

        return true;
    }
  

         //求关键字key的索引值,用于插入(添加)
    private final int indexOfInsertion(long key) {
        final long tab[] = table;
        final byte stat[] = state;
        final int length = tab.length;
        //这就是哈希函数了
        final int hash = ((int)(key ^ (key >> 32))) & 0x7FFFFFFF;
        int i = hash % length;

         //发生哈希冲突时,用于再哈希探测的步长
        int decrement = (hash) % (length-2);
      
        if (decrement == 0) decrement = 1;
        //stat[i]有三种情况
        while (stat[i] == FULL && tab[i] != key) {//第一种,发生哈希冲突,往前探测
            i -= decrement;
            if (i<0) i+=length;
        }

        if (stat[i] == REMOVED) {//第二种,此位置原来的键值对已删除
           
            int j = i;
            //有意思,已删除的位置并不用来存新的
            while (stat[i] != FREE && (stat[i] == REMOVED || tab[i] != key)) {
                i -= decrement;
                if (i<0) i+=length;
            }
            if (stat[i] == FREE) i = j;
        }

        if (stat[i] == FULL) {//第三种,这种情况会出现吗?
          
            return -i-1;
        }
        //第三种,stat[i]=FREE
       
        return i;
    }

     //删除key的value
    public boolean removeKey(long key) {
        int i = indexOfKey(key);//获取关键字的索引
        if (i<0) return false; 

        this.state[i]=REMOVED;//作删除标记
        this.values[i]=null; //注意:table[i]并没有置为null
        this.distinct--;

        if (this.distinct < this.lowWaterMark) {//存放的键值对少于lowWaterMark,重新调整
                int newCapacity = chooseShrinkCapacity(
                        this.distinct,
                        this.minLoadFactor,
                        this.maxLoadFactor
                    );
         rehash(newCapacity);//用新容量重新构造
        }

        return true;
    }


      public final Object get(long key) {//获取关键字对应的值
        int i = indexOfKey(key);
        if (i<0) {
            return null;
        }
        else {
            return values[i];
        }
    }

    private final int indexOfKey(long key) {//求关键字之索引值,用于查找
        final long tab[] = table;
        final byte stat[] = state;
        final int length = tab.length;
        //这个是哈希函数
        final int hash = ((int)(key ^ (key >> 32))) & 0x7FFFFFFF;
        int i = hash % length;//得到了关键字的索引值
       
        //用于再哈希探测的步长
        int decrement = (hash) % (length-2);//减量
  
        if (decrement == 0) decrement = 1;

        while (stat[i] != FREE && (stat[i] == REMOVED || tab[i] != key)) {
            i -= decrement;//往前找
            if (i<0) i+=length;
        }

        if (stat[i] == FREE) return -1; // 没有找到
        return i; //找到了,返回索引值
    }

  
      public void clear() {//清空
        for (int i=0; i<state.length; i++) {
            state[i] = FREE;
        }
        for (int i=0; i<values.length-1; i++) {
            values[i] = null;
        }

        this.distinct = 0;
        this.freeEntries = table.length; 
        trimToSize();//清空以后,容量不能太大,这里重新调整,以节约空间
    }

     public void trimToSize() {
       int newCapacity = nextPrime((int)(1 + 1.2*size()));
        if (table.length > newCapacity) {
            rehash(newCapacity);
        }
    }



    //是否包含key
    public boolean containsKey(long key) {
        return indexOfKey(key) >= 0;
    }

    //是否包含value
    public boolean containsValue(Object value) {
        return indexOfValue(value) >= 0;
    }

  
    public void ensureCapacity(int minCapacity) {//确保容量不小于minCapacity
        if (table.length < minCapacity) {
            int newCapacity = nextPrime(minCapacity);
            rehash(newCapacity);//再哈希
        }
    }


    protected int indexOfValue(Object value) {//获取值的索引
        final Object val[] = values;
        final byte stat[] = state;

        for (int i=stat.length; --i >= 0;) {
            if (stat[i]==FULL && val[i]==value) return i;
        }

        return -1; // not found
    }

   //获取value的第一个键值,可能的多个
    public long keyOf(Object value) {
        int i = indexOfValue(value);
        if (i<0) return Long.MIN_VALUE;
        return table[i];
    }

 
    public long[] keys() {//所有的键
        long[] elements = new long[distinct];
        long[] tab = table;
        byte[] stat = state;
        int j=0;
        for (int i = tab.length ; i-- > 0 ;) {
            if (stat[i]==FULL) {
                elements[j++]=tab[i];
            }
        }
        return elements;
    }

  
     public int size() {//当前存了多少对键值
        return distinct;
    }

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

     // 如果元素放得太满,就必须进行rehash(再哈希)。再哈希使空间增大,并将原有的对象重新导入新的LongHashMap中,
   //而原始的LongHashMap被删除。loadfactor(装载因子)决定何时要对LongHashMap进行再哈希。

    protected void rehash(int newCapacity) {//用新的容量重新构建LongHashMap
        int oldCapacity = table.length;//原来的容量
        long oldTable[] = table;//原来的键
        Object oldValues[] = values;//原来的值
        byte oldState[] = state;

        long newTable[] = new long[newCapacity];
        Object newValues[] = new Object[newCapacity];
        byte newState[] = new byte[newCapacity];
        
         //(int) (newCapacity * minLoadFactor);
        this.lowWaterMark  = chooseLowWaterMark(newCapacity,this.minLoadFactor);
        this.highWaterMark = chooseHighWaterMark(newCapacity,this.maxLoadFactor);

        this.table = newTable;
        this.values = newValues;
        this.state = newState;
        this.freeEntries = newCapacity-this.distinct; // 当前的剩余空间

        for (int i = oldCapacity ; i-- > 0 ;) {
            if (oldState[i]==FULL) {
                long element = oldTable[i];
                int index = indexOfInsertion(element);
                newTable[index]=element;
                newValues[index]=oldValues[i];
                newState[index]=FULL;
            }
        }
    }

   

  
    public Object[] values() {
        Object[] elements = new Object[distinct];

        Object[] val = values;
        byte[] stat = state;

        int j=0;
        for (int i = stat.length ; i-- > 0 ;) {
            if (stat[i]==FULL) {
                elements[j++]=val[i];
            }
        }
        return elements;
    }

   

    
    private int chooseHighWaterMark(int capacity, double maxLoad) {
          return Math.min(capacity-2, (int) (capacity * maxLoad));
    }

   
    protected int chooseLowWaterMark(int capacity, double minLoad) {
        return (int) (capacity * minLoad);
    }

    
    protected int chooseMeanCapacity(int size, double minLoad, double maxLoad) {
        return nextPrime(Math.max(size+1, (int) ((2*size / (minLoad+maxLoad)))));
    }

 
    protected int chooseShrinkCapacity(int size, double minLoad, double maxLoad) {
        return nextPrime(Math.max(size+1, (int) ((4*size / (minLoad+3*maxLoad)))));
    }

  
    protected int nextPrime(int desiredCapacity) {//对指定的容量,在素数表中进行对半查找,返回一个素数容量
        int i = java.util.Arrays.binarySearch(primeCapacities, desiredCapacity);
        if(desiredCapacity==100) System.out.println("i="+i);
        if (i<0) {
             i = -i -1; 
        }
        return primeCapacities[i];
    }

   private void printState(){
      for(int i=0;i<state.length;i++)
        System.out.print(state[i]+"  ");
      System.out.println();
   }
    
    public static final int LARGEST_PRIME = Integer.MAX_VALUE; //最大的素数.

   
    private static final int[] primeCapacities = {//容量素数表
        LARGEST_PRIME,

        //chunk #1
        5,11,23,47,97,197,397,797,1597,3203,6421,12853,25717,51437,102877,205759,
        411527,823117,1646237,3292489,6584983,13169977,26339969,52679969,105359939,
        210719881,421439783,842879579,1685759167,

        //chunk #2
        433,877,1759,3527,7057,14143,28289,56591,113189,226379,452759,905551,1811107,
        3622219,7244441,14488931,28977863,57955739,115911563,231823147,463646329,927292699,
        1854585413,

        //chunk #3
        953,1907,3821,7643,15287,30577,61169,122347,244703,489407,978821,1957651,3915341,
        7830701,15661423,31322867,62645741,125291483,250582987,501165979,1002331963,
        2004663929,

        //chunk #4
        1039,2081,4177,8363,16729,33461,66923,133853,267713,535481,1070981,2141977,4283963,
        8567929,17135863,34271747,68543509,137087021,274174111,548348231,1096696463,

        //chunk #5
        31,67,137,277,557,1117,2237,4481,8963,17929,35863,71741,143483,286973,573953,
        1147921,2295859,4591721,9183457,18366923,36733847,73467739,146935499,293871013,
        587742049,1175484103,

        //chunk #6
        599,1201,2411,4831,9677,19373,38747,77509,155027,310081,620171,1240361,2480729,
        4961459,9922933,19845871,39691759,79383533,158767069,317534141,635068283,1270136683,

        //chunk #7
        311,631,1277,2557,5119,10243,20507,41017,82037,164089,328213,656429,1312867,
        2625761,5251529,10503061,21006137,42012281,84024581,168049163,336098327,672196673,
        1344393353,

        //chunk #8
        3,7,17,37,79,163,331,673,1361,2729,5471,10949,21911,43853,87719,175447,350899,
        701819,1403641,2807303,5614657,11229331,22458671,44917381,89834777,179669557,
        359339171,718678369,1437356741,

        //chunk #9
        43,89,179,359,719,1439,2879,5779,11579,23159,46327,92657,185323,370661,741337,
        1482707,2965421,5930887,11861791,23723597,47447201,94894427,189788857,379577741,
        759155483,1518310967,

        //chunk #10
        379,761,1523,3049,6101,12203,24407,48817,97649,195311,390647,781301,1562611,
        3125257,6250537,12501169,25002389,50004791,100009607,200019221,400038451,800076929,
        1600153859
    };

    static { 
        java.util.Arrays.sort(primeCapacities);
    }

    //测试一下
     public static void main(String args[]){
      LongHashMap lh=new LongHashMap(5);//初始容量为5
      System.out.println("size="+lh.size());
      for(int i=0;i<3;i++)//先放三个
        lh.put(i, Integer.valueOf(i));
      System.out.println("size="+lh.size());
      lh.removeKey(1);//删除二个
       lh.removeKey(2);
      lh.put(123,"ok");//添加一个

      //看看状态
      lh.printState();
      lh.put(1234,"oo");//再放一个
      //看看状态 
      lh.printState();

      //取出来
       System.out.println(lh.get(0));
      System.out.println(lh.get(123));
      System.out.println(lh.get(1234));

    }
}


运行:

C:\ex>java   LongHashMap
size=0
size=3
1  2  2  1  0
1  0  0  0  1  0  0  0  0  0  1  0  0  0  0  0  0
0
ok
oo

源码:
0
1
分享到:
评论

相关推荐

    数组和集合的学习笔记

    ### 数组和集合的学习笔记 #### 一、概述 在Java编程语言中,数组和集合是数据存储与操作的重要组成部分。数组是一种基本的数据结构,用于存储相同类型的数据元素;而集合(Collections)则是用于存储、检索和操作...

    java 学习笔记

    在Java学习笔记中,我们重点关注Java的IO(输入/输出)操作、常用类库以及集合框架。 1. **Java IO**: - **File类**:提供了文件和目录的基本操作,如创建、删除、重命名等。 - **RandomAccessFile**:支持对...

    jsp二期学习笔记(北大青鸟二期学习)

    ### JSP 二期学习笔记(北大青鸟二期学习) #### JSP 二期学习概述 本学习笔记主要记录了在北大青鸟进行的JSP二期培训过程中所学到的关键知识点和技术细节。JSP(JavaServer Pages)是一种基于Java的技术,用于...

    良葛格 java 学习笔记

    《良葛格 Java 学习笔记》是一份专为初学者设计的 Java 编程教程,旨在帮助初入编程领域的学习者快速掌握 Java 语言的基础知识。这份 PDF 格式的文档详细介绍了 Java 的核心概念、语法和编程技巧,是学习 Java 的...

    Java学习笔记整理

    这些学习笔记将带你深入了解Java的核心概念,特别是面向对象编程和集合框架。以下是对每个文件内容的详细阐述: 1. **Day0804_HashMap的基本使用.docx**:HashMap是Java集合框架中的一个重要组件,它提供了键值对的...

    java学习笔记(下)

    ### Java学习笔记(下)——深入理解框架与核心技术 #### 物件容器(Container) 在Java中,物件容器是管理物件的重要工具,分为两大类:`Collection`与`Map`。 - **Collection类**:主要包含`List`与`Set`。`...

    java学习笔记,全程

    - ArrayList、LinkedList、HashSet、HashMap:研究这些常用集合类的实现原理,掌握它们的添加、删除、查找操作,以及适用场景。 - 集合接口与泛型:了解List、Set、Map接口,以及泛型在集合中的应用,提高代码的...

    Java学习笔记(必看经典).doc

    4. **数组和集合框架**:数组是存储固定数量相同类型元素的数据结构,而集合框架(如ArrayList、LinkedList、HashSet、HashMap等)提供了更丰富的数据组织方式,支持动态大小调整和各种操作。 5. **异常处理**:...

    《恋上数据结构》第1季度 + 第2季 完整学习笔记,从0实现的 Java 数据结构大全。.zip

    《恋上数据结构》的学习笔记涵盖了第一和第二季度的内容,旨在帮助读者从零基础开始深入理解并实现Java语言中的各种数据结构。下面将详细阐述这些关键知识点。 1. 数组:数组是最基本的数据结构,它是一组相同类型...

    常见数据结构与算法的Python实现及学习笔记.zip

    本资源包"常见数据结构与算法的Python实现及学习笔记.zip"聚焦于使用Python语言来阐述这些概念,这对于Python开发者或者正在学习Python的学生来说是一份宝贵的资源。下面,我们将深入探讨其中涵盖的一些关键知识点。...

    良葛格JAVA 学习笔记

    《良葛格JAVA 学习笔记》是由知名IT专家林信良,网名“良葛格”,在台湾大学电机工程学系的深厚学术背景基础上,结合其作为SUN教育训练中心讲师的丰富教学经验编写的。他的著作还包括《Spring 技术手册》,并且他...

    java学习笔记markdown

    【Java学习笔记Markdown版】是针对Java初学者和进阶者的一份详尽教程,以Markdown格式编写,便于阅读和整理。Markdown是一种轻量级的标记语言,它允许用户使用易读易写的纯文本格式编写文档,然后转换成结构化的HTML...

    Java学习笔记(源码)

    【Java学习笔记(源码)】是一份详细记录了Java编程语言学习过程的资源集合,包含实际的源代码示例。这份笔记旨在帮助初学者和有一定经验的开发者深入理解和掌握Java语言的核心概念、语法以及常见应用。以下是笔记中...

    Java基础尚硅谷宋红康学习笔记

    3. **数组与集合**:数组用于存储固定数量的同类型元素,而集合框架(如ArrayList、LinkedList、HashSet、HashMap等)则提供了动态存储和操作对象的能力。 4. **异常处理**:Java通过try-catch-finally结构进行异常...

    Java公司培训经典学习笔记

    Java公司培训经典学习笔记是针对Java编程语言进行深入学习的一份宝贵资料,涵盖了从基础到高级的诸多知识点,旨在帮助开发者提升技能,适应企业级项目开发的需求。以下将详细阐述这些笔记中的关键点: 1. **Java...

    javase学习笔记(全)

    这份"javase学习笔记(全)"涵盖了刘意版传智播客课程的主要内容,是学习Java编程语言的重要参考资料。以下将对Java SE的一些关键知识点进行详细解释: 1. **Java基础**:Java的基础语法包括数据类型(如整型、浮点型...

    Java开发学习笔记

    Java开发学习笔记主要针对的是初学者,旨在帮助他们掌握Java编程的基础知识。下面将详细讲解Java开发中的核心概念和步骤。 一、Java环境变量设置 在开始Java编程之前,我们需要安装Java Development Kit (JDK)并...

    良葛格java学习笔记

    《良葛格java学习笔记》是一份集合了Java学习精华的资源,主要针对初学者和对Java编程感兴趣的读者。这份笔记是由用户从良葛格的网站上精心整理并转化为CHM格式,便于阅读和查阅。CHM(Compiled Help Manual)是微软...

Global site tag (gtag.js) - Google Analytics