深入map和set

来源:互联网 发布:蜀美网络招聘信息 编辑:程序博客网 时间:2024/05/18 01:22

对HashSet而言 系统采用hash算法决定集合的快速存取,集合元素,对HashMap而言,系统采用hash算法,跟据hash值来获取的key的存取位置,实现快速存取;

  HashMap类的put源码:

    public V put(K key,V value){

       if(key==null)

             return putForNullKey(value);

             //根据key的keyCode计算hash

          int hash=hash(key.hashCode());

          //搜索指定的hash值在对应table中的索引

          int i=indexFor(hash,table.length);

         //获取table中的entry对象

          for(Entry<K,V> e=table[i];e!=null;e=e.next){

               Object k;

               //判断key的hash值是否相等同时key equal是否相同

                if(e.hash==hash && ((k=e.key)==key||key.equals(k)){

                    V oldValue=e.value;

                    e.value=value;

                    e.recordAccess(this);

                    return oldValue;   //把老的值替换成新的值

             }

       }

        //如果i处索引为空

       modCount++;

      //创建新的entry对象

      addEntry(hash,key,value,i);

       return null;

   }

     向hashMap添加key-value的时候,由其key的hashcode返回值,决定该key-value (entry对象)的存储位置;

   当两个entry对象的key的hashCode返回值相同时候,将有key的euqals的比较值来比较是否为true,如果为true则覆盖,否则会产生新的entry对象;

  //添加新entry对象

   void addEntry(int hash,K key, V value,int buckIndex){

        Entry<K,V> e=table[buckIndex];

        table[buckIndex] =new Entry<K,V>(hash,key,value,buckIndex);

        if(size++>=threshold)    //如果ket-value对的数量超出了极限

          resize(2*table.length);  //进行扩容操作

   }

这个代码设计很吊,如果table[buckIndex]处有entry对象,则新创建的对象则指向本来就有的对象,所以就形成了一个entry链,所以新创建总是entry在链的末尾;

//指定初始化容量,负载因子创建hashMap

public HashMap(int intialCapacity,float loadFactory){

      if(intialCapacity<0)

             throw new 异常;

     if(intialCapacity>MAXMUM_CAPACTITY)

         intialCapacity=MAXMUM_CAPACTITY;

     if(loadFactory<0||Float.isNaN(loadFactory))

              throw new 异常;

    int capacity=1;

    while(capacity<intialCapacity)  //capacity小于初始化的值,则capacity扩大为2 的n次方

            capacity<<1;

    threshold=(int)(intialCapacity*loadFactory);  //最大极限值则是初始化值*负载因子

   table=new Entry[capacity];      //创建一个新的Entry对象数组

  int();  //进行初始化操作;

}


初始化的时候,会产生一个table数组,这个数组里面放的元素,我们称作为“桶” bucket,每个bucket会有相对应的索引,我们可以根据索引来进行取值;

public V get(Object key){

   if(key==null)

       return getForNullKey();

      int hash=hash(key.hashCode);

      for(Entry[K,V] e=table[indexFor(hash,table.length)];e!=null;e=e.next)

      {

                  Object k;

                  if(e.hash==hash&&((k==e.key)==key.equals(k))

                       return e.value;

     }

    return null; 

}

如果hashmap只有一个entry对象,则hashmap通过索引直接取出该桶内的entry,如果是一条entry链,则就会遍历全部,如果该要找到的对象在该链的底部的最后一个entry,

所以要重写equals和hashCode来进行判断是否相同。如果一条entry链全部遍历则会降低hashMap的性能;

hashMap的默认是16

总的来说:

    HashMap底层是key-value对当成整体进行处理,这个整体就是entry对象;底层则是一个Entry[]来保存所有的key-vaue对,当需要存储一个entry对象,会根据hash算法,来存到相应的位置,如果要取一个entry对象,也会根据hash算法来找到相应的位置进行获取;

hashMap一开始的时候初始化,不要空间大小不要初始化的太高;

负载因子默认是0.75,增大负载因子,会提高hashMap内部查询例如put,get。;降低负载因子,会提高hashMap的查询,反而增大了hashMap的空间开销


总的来说set底层就是hashMap,为了要符合hashMap的key-value对所以hashSet底层定义了一个静态的object类可以充当value;

set集合只是用了map的key而没有用value;



0 0