Java集合(十)HashTable接口和Properties

发布于:2022-11-29 ⋅ 阅读:(244) ⋅ 点赞:(0)

HashTable的基本介绍:
(1)存放的元素是键值对:即K-V

(2)hashtable的键和值都不能为null,否则会抛出NullPointerException

(3)hashTable使用方法基本上和HashMap一样

(4)hashtable是线程安全的(synchronized),hashMap是线程不安全的

我们通过put方法来进行查看:

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

(5)简单看下底层结构

我们查看源码图:

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

我们进行设计代码进行查看:

package com.rgf.map;

import java.util.Hashtable;

@SuppressWarnings({"all"})
public class HashTableExercise {
    public static void main(String[] args) {
        Hashtable table = new Hashtable();
        table.put("john",100);
        table.put(null,100);
        table.put("john",null);
        table.put("Lucy",100);
        table.put("lic",100);
        table.put("lic",88);
        System.out.println(table);
    }
}

我们运行之后如下所示:

我们发现当键和值为空的时候,抛出异常:NullPointerExcepion。 

我们修改后代码如下所示:

package com.rgf.map;

import java.util.Hashtable;

@SuppressWarnings({"all"})
public class HashTableExercise {
    public static void main(String[] args) {
        Hashtable table = new Hashtable();
        table.put("john",100);//OK
    //    table.put(null,100);//异常 NullPointerException
      //  table.put("john",null);//异常
        table.put("Lucy",100);//OK
        table.put("lic",100);//
        table.put("lic",88);//替换
        System.out.println(table);
        
    }
}

运行如下所示:

我们下来进行debug,从这行代码开始打断点:  table.put("john",100);的演示:

 

我们发现底层有数组,类型为Hashtable$Entry[ ],是Hashtable的内部类,初始化大小为11 

private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;

为Hashtable里面的静态内部类。实现了Map.Entry接口。

threshold 8=11*0.75

我们进行查看如何进行扩容:

package com.rgf.map;

import java.util.Hashtable;

@SuppressWarnings({"all"})
public class HashTableExercise {
    public static void main(String[] args) {
        Hashtable table = new Hashtable();
        table.put("john",100);//OK
    //    table.put(null,100);//异常 NullPointerException
      //  table.put("john",null);//异常
        table.put("Lucy",100);//OK
        table.put("lic",100);//
        table.put("lic",88);//替换
        table.put("hello1",88);
        table.put("hello2",88);
        table.put("hello3",88);
        table.put("hello4",88);
        table.put("hello5",88);
        table.put("hello6",88);
        
        System.out.println(table);

    }
}

我们进行debug之后,如下所示:

我们发现此时扩容为23,临界值为17.

我们发现原先的大小为11,扩容为23,并不是以两倍进行扩容。而是按照自己的扩容机制进行扩容。 

我们设置代码如下所示:

package com.rgf.map;

import java.util.Hashtable;

@SuppressWarnings({"all"})
public class HashTableExercise {
    public static void main(String[] args) {
        Hashtable table = new Hashtable();
        table.put("john",100);//OK
    //    table.put(null,100);//异常 NullPointerException
      //  table.put("john",null);//异常
        table.put("Lucy",100);//OK
        table.put("lic2",100);//
        table.put("lic",88);//替换
        table.put("hello1",88);
        table.put("hello2",88);
        table.put("hello3",88);
        table.put("hello4",88);
        table.put("hello5",88);
        table.put("hello6",88);

        System.out.println(table);

    }
}

我们在第九个进行打断点:

 table.put("hello5",88);

我们进行debug如下所示:

我们先进行自由装箱:

 退出之后,我们进入put方法:

 执行如下方法:hashtable的底层是Entry数组。这条语句是执行添加。添加K-V,封装到entry里面。

  addEntry(hash, key, value, index);
        return null;
    }

我们将断点设置到该方法,进入断点如下所示:

private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

我们进入扩容机制rehash的源码里面:

protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

 我们发现其中扩容的关键代码如下:

 int newCapacity = (oldCapacity << 1) + 1;

原先表左移一位,即乘以2后再加1.即原先的11*2+1=23,所以当  if (count >= threshold) {满足的时候,就进行扩容。

HashMap和Hashtable进行比对:

版本 线程安全(同步) 效率 允许null键null值
HashMap 1.2 不安全 可以
HashTable 1.0 安全 较低 不可以

Properties基本介绍:

1.Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据

2.他的使用特点和Hashtable类似

3.Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改。

 

4.说明:工作后 xxx.properties文件通常作为配置文件。

 我们设计代码如下所示:

package com.rgf.map;

import java.util.Properties;

public class Properties_ {
    public static void main(String[] args) {
        //1.Propertise继承Hashtable
        //2.可以通过k-v存放数据,当然key 和 value不能为空
        Properties properties = new Properties();
        properties.put(null,"abc");  //抛出空指针异常
        properties.put("abc",null);  //抛出空指针异常
}
}

运行之后如下所示:

我们发现出现空值会抛出异常。

 我们的代码设计如下所示:

package com.rgf.map;

import java.util.Properties;

public class Properties_ {
    public static void main(String[] args) {
        //1.Propertise继承Hashtable
        //2.可以通过k-v存放数据,当然key 和 value不能为空
        Properties properties = new Properties();
        properties.put("john",100);  //k-v
        properties.put("lucy",100);
        properties.put("lic",100);
        properties.put("lic",88); //如果有相同的key,value被替换
        System.out.println("properties="+properties);
       //通过k获取对应值
        properties.get("lic");
        System.out.println(properties.get("lic"));  //88
        //删除
        properties.remove("lic");
        System.out.println("properties="+properties);

        //修改
        properties.put("john","约翰");
        System.out.println("properties="+properties);


    }
}

我们运行如下所示:

总结如下:在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择。分析如下:

(1)先判断存储的类型(一组对象(单列)或一组键值对(双列))

(2)一组对象(单列):Collection接口

       允许重复:List(有序可重复)

               增删多:LinkedList(底层维护了一个双向链表)

               改查多:ArrayList(底层维护了Object类型的可变数组)

不允许重复:Set(无序不可重复)

              无序:HashSet(底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树)

               排序:TreeSet

              插入和取出顺序一致:LinkedHashSet【底层是LinkedHashMap,LinkedHashMap的底层是HashMap】,维护数组+双向链表

(3)一组键值对(双列):Map

          键无序:HashMap(底层是:哈希表  jdk7:数组+链表,jdk8:数组+链表+红黑树)

          键排序:TreeMap

          键插入和取出顺序一致:LinkedHashMap

          读取文件:Prperties 

本文含有隐藏内容,请 开通VIP 后查看