HashTable&Properties

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

本文学习HashTable,这是一个古老类,基本上被废除,但是学习它有助于理解HashMap。

HashTable简介

HashTable是一个key-value键值对的数据类型,较为古老,现已基本废除不再使用,使用synchronized保证同步,效率较低,一般单线程转为使用HashMap,多线程使用ConcurrentHashMap。

但学习其实现方式有助于理解hashMap的实现。

HashTable继承关系

继承Dictionary类,实现Map接口。

image-20220520003443203.png

HashTable原理

hashTable的原理和hashMap大致相同。在一些算法和设计方面,hashTable比hashMap低下。

HashTable属性

//节点数组
private transient Entry<?,?>[] table;
//节点个数
private transient int count;
//临界值
private int threshold;
//加载因子  0.75
private float loadFactor;
//模数,hashTable被修改的次数
private transient int modCount = 0;
复制代码

HashTable插入元素

  • 当key值重复,就覆盖并返回旧的value
  • 当key值不重复,直接插入。如果出现冲突,拉链发解决

一、(hash & 0x7FFFFFFF) % tab.length;

取余操作,获取散列下标,相比于hashMap的一次位运算,这里效率很低。

这里巧妙在于,与0x7FFFFFFF进行与运算,避免负数的出现

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    //不允许插入null值
    if (value == null) {
        throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    //这里并没有对key进行为空检测,是不合理的
    int hash = key.hashCode();
    //hash值与0x7FFFFFFF按位与,防止出现负数。
    //与tab.length做取余操作,得到散列下标
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    //不为空
    for(; entry != null ; entry = entry.next) {
        //出现重复则覆盖
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }
	//出现冲突、或对应下标节点为空,插入元素
    addEntry(hash, key, value, index);
    return null;
}
复制代码

addEntry方法

如果节点个数超过临界值,就扩容。

否则直接插入元素,或拉链法解决冲突

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 ;) {
        //i下标对单向链表
        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;
        }
    }
}
复制代码

拉链发解决冲突图示:

image-20220520023906652.png


Properties简介

Properties 继承于 Hashtable,可以作为一个map使用。其内部扩展了一些方法,只允许添加String的key-value,不过都是基于hashTable实现的。其使用最多的是作为配置工具类使用。

使用

properties作为map使用

/**
 * properties作为map使用
 */
@Test
public void propertiesUseAsMap() {
    Properties prop = new Properties();
    prop.put(1, "a");
    prop.put(2, 2);
    prop.put("3", new Object());
    System.out.println(prop.get(1));
    System.out.println(prop.get(2));
    System.out.println(prop.get("3"));
}
复制代码

其扩展方法,只能添加字符串的key-valu

@Test
public void useAsProp() {
    Properties prop = new Properties();
    prop.setProperty("name","name");
    prop.setProperty("age","23");
    prop.setProperty("email","[email protected]");
    System.out.println(prop);
    prop.forEach((key, value) -> {
        System.out.println("{key" + key + "," + "value" + value + "}");
    });
}
复制代码

有一些类自带properties属性

/**
 * 也可以自己写一个配置类
 */
@Test
public void testMyCP() {
    Properties prop = MyPropClass.getProperties();

    System.out.println(prop);
    prop.forEach((key, value) -> {
        System.out.println("{key" + key + "," + "value" + value + "}");
    });

}

class MyPropClass {
    private static Properties props;
    private static final String name;
    private static final String age;
    private static final String email;

    static {
        props = new Properties();
        name = "yyc";
        age = "23";
        email = "[email protected]";
        initProperties();
    }

    private static void initProperties() {
        props.setProperty("name", name);
        props.setProperty("age", age);
        props.setProperty("email", email);
    }


    public void setProperties(String key, String value) {
        props.setProperty(key, value);
    }

    public String getProperties(String key) {
        return props.getProperty(key);
    }

    public void setProperties(Properties properties) {

        MyPropClass.props = properties;
    }

    public static Properties getProperties() {
        return props;
    }
复制代码

为了防止一些硬编码,可以使用配置文件的形式配置信息,一般用于数据库链接或一些配置的类信息。

user.properties:

name=yyc
age=23
hobby=ball
复制代码
@Test
public void readProp() throws IOException {

    InputStream in = PropertiesTest.class.getClassLoader().getResourceAsStream("user.properties");
    Properties prop = new Properties();
    prop.load(in);
    String name = prop.getProperty("name");
    String age = prop.getProperty("age");
    String hobby = prop.getProperty("hobby");
    System.out.println("{name:" + name + "," + "age:" + age + "," + "hobby" + hobby + "}");
}
复制代码

同时也可以将Properties作为文件输出


/**
 * 也可以输出为properties  或  xml
 * @throws IOException
 */
@Test
public void writeProp() throws IOException {

    //创建配置
    Properties prop = new Properties();
    prop.setProperty("name","name");
    prop.setProperty("age","23");
    prop.setProperty("email","[email protected]");
    System.out.println(prop);
    prop.forEach((key, value) -> {
        System.out.println("{key" + key + "," + "value" + value + "}");
    });
    //创建  outputStream
    FileWriter fileWriter = new FileWriter(new File("D:\\user.properties"));
    //写入文件
    prop.store(fileWriter,"user信息");

}
复制代码

image-20220521005020873.png

猜你喜欢

转载自juejin.im/post/7103534218403135525