The Map interface, HashMap class, HashTable class, TreeMap class of the collection framework in Java (10,000 words super full detailed explanation)

1. Collective framework system

The Java collection framework provides a set of interfaces and classes with excellent performance and convenient use, which are located in the java.util package, so when using the collection framework, it is necessary to import the package.
The Java collection framework mainly includes two types of containers, one is a collection (Collection), which stores a collection of elements; the other is a map (Map), which stores key/value pair mappings.

  • The Collection system is as follows:
    Collection System Diagram
  • The Map system is as follows:

Explanation:
(1) The Collection interface has two important sub-interfaces List and Set, and their implementation subclasses are all single-column collections.
(2) The implementation subclass of the Map interface is a double-column collection, and the stored KV (key-value pair)

1. Overview of commonly used collection interfaces

As shown in the following table:

interface describe
Collection interface Collection is the most basic collection interface. A Collection represents a group of Objects (that is, the elements of Collection). Java does not provide classes directly inherited from Collection, but only subinterfaces (such as List and set) inherited from Collection interface. The Collection interface stores a set of non-unique and unordered objects (the objects in the Collection collection cannot be accessed through indexes).
List interface The List interface inherits from the Collection interface, but the List interface is an ordered collection. Using this interface can precisely control the insertion position of each element, and can pass the index (that is, the position of the element in the List, similar to the subscript of the array) To access the elements in the List collection, the index of the first element is 0. And the same elements are allowed in the List collection. It can be said that a collection of the List interface stores a set of non-unique, ordered (insertion order) objects.
Set interface The Set interface inherits from the Collection interface, and has exactly the same interface as the Collection, except that the methods are partially different. The same as the Collection interface, the Set interface stores a set of unique, unordered objects.
Map interface The Map interface is at the same level as the Collection interface (there is no inheritance relationship with each other). The Map graph stores a set of key-value objects and provides a mapping from key (key) to value (value).

Differences between Set and List interfaces:

(1) The Set interface collection stores unordered and non-repeated data. The List interface collection stores ordered and repeatable elements.

(2) The bottom layer of the Set collection uses a linked list data structure, which has low retrieval efficiency, high deletion and insertion efficiency, and insertion and deletion will not cause changes in element positions (the implementation subclasses include HashSet, TreeSet, etc.).

(3) List combined bottom layer is similar to array, but it can grow dynamically, automatically increasing the length of List according to the length of the actual stored data. It has high element retrieval efficiency, low insertion and deletion efficiency, and insertion and deletion will cause other element positions to change (implementation subclasses include ArrayList, LinkedList, Vector, etc.).

2. Implementation subclasses of commonly used Collection collections

Java provides a set of standard collection classes that implement the Collection interface. Some of them are concrete classes, which can be used directly, while others are abstract classes, which provide a partial implementation of the interface.

As shown in the following table:

class name describe
ArrayList class This class implements the List interface, allows storing null (empty value) elements, and can store repeated elements. This class implements variable-sized arrays, providing better performance when accessing and traversing elements randomly. This class is asynchronous and should not be used in multi-threaded situations. When the ArrayList class is expanded, it will be expanded by 1.5 times the current capacity.
Vector class This class is very similar to the ArrayList class, but this class is synchronous and can be used in multi-threaded situations. This class allows setting the default growth length, and the default expansion method is twice the original size.
LinkedList class This class implements the List interface, allows storing null (empty value) elements, and can store repeated elements. It is mainly used to create a linked list data structure. This class has no synchronization method. If multiple threads access a LinkedList at the same time, you must implement the access by yourself Synchronization, the solution is to construct a synchronized LinkedList when creating the LinkedList class.
HashSet class This class implements the Set interface, does not allow to store duplicate elements, and does not guarantee the order of elements in the set, it allows to store null (empty value) elements, but only one at most can be stored.
TreeSet class This class implements the Set interface, does not allow to store duplicate elements, and does not guarantee the order of elements in the set, it allows to store null (empty value) elements, but only one at most can be stored. This class can implement functions such as sorting.

3. Implementation subclasses of commonly used Map graphs

As shown in the following table:

class name describe
HashMap class The HashMap class is a hash table that stores key-value pairs (key-value) mappings. This class implements the Map interface, stores elements according to the HashCode value of the key, and has a fast access speed, but allows at most one element's key to be null (empty value), and it does not support thread synchronization.
TreeMap class The TreeMap class inherits AbstractMap, implements most of the Map interface, and uses a tree.
HashTable 类 Hashtable inherits from Dictionary (dictionary) class and is used to store key-value pairs.
Properties class Properties inherits from HashTable, which represents a persistent property set, and each key and its corresponding value in the property list are a string.

Hereby note: Due to the various contents of the collection framework, this article only introduces the content of the Map interface and its important implementation subclasses under the Map graph, and the rest of the knowledge of the collection framework will be shared in the next blog post.

2. Map interface

1. Features of the Map interface

(1) The Map interface is at the same level as the Collection interface (there is no inheritance relationship with each other). The Map graph stores a set of key-value objects and provides a mapping from key (key) to value (value).
(2) The key and value in the map can be any reference type of data, and they will be encapsulated into the HashMap$Node object.
(3) The key in the map cannot be repeated, but the value can be repeated; the key only allows one null (null value), and the value can have multiple nulls (null values).
(4) The String class is often used as the key in the Map.
(5) There is a one-way one-to-one relationship between key and value, that is, the corresponding value can be found through the specified key (because the key value is unique), and vice versa.

  • Code demo:

public class Map_ {
    
    
    public static void main(String[] args) {
    
    
        // Map 接口实现类的特点, 使用实现类 HashMap

        Map map = new HashMap();// 创建一个图

        // 在 map 中,put 方法可以用来添加和替换元素
        map.put("no1", "韩顺平");// 成功
        map.put("no2", "张无忌");// 成功
        map.put("no1", "张三丰");// 添加失败,当有相同的 key , 原来的 value 就会被替换

        // put 方法还会返回被替换的 value,如果成功添加一个新元素,就会返回 null
        System.out.println(map.put("no1", "张三丰"));

        map.put("no3", "张三丰");// value 相同,但 key 不同,不会发生替换
        map.put(null, null); // 成功
        map.put(null, "abc"); // 添加失败,当有相同的 key , 原来的 value 就会被替换
        map.put("no4", null); // 成功
        map.put("no5", null); // 成功
        map.put(1, "赵敏");// 成功
        map.put(new Object(), "金毛狮王");// 成功

        // 通过get 方法,传入 key ,会返回对应的 value
        System.out.println(map.get("no2"));// 返回张无忌

        System.out.println("map=" + map);
    }
}

2. The underlying principle of the map storing "key-value pairs" (critical and difficult points)

(1) In the HashMap class that implements the Map interface, a static internal class Node class is defined, and the attributes K key and V value are defined in the Node class. Therefore, the key-value key-value pair is essentially stored in the HashMap class in the Node class.
(2) The static internal class Node class also implements the local interface Entry interface in the Map interface. Therefore, the objects of the Node class (ie nodes) can use the methods defined in the Entry interface.

  • The source code of the HashMap class is as follows:

insert image description here

(3) While creating a HashMap class object (hash map), the system will automatically create an EntrySet collection storing Map.Entry type elements.
(4) When a key-value pair is stored in the HashMap, a HashMap$Node object (that is, a node) is actually created; at the same time, a Map.Entry class object is automatically created in the EntrySet collection, and the object A reference to the HashMap$Node object is stored in .

  • As shown below:

insert image description here

  • Code demo instructions:

public class MapSource_ {
    
    
    public static void main(String[] args) {
    
    

        Map map = new HashMap();// 向上转型

        map.put("no1", "韩顺平");
        map.put("no2", "张无忌");

/*  解读:

        1. k-v 实质存储位置: HashMap$Node node = newNode(hash, key, value, null)
        
        2. 为了方便程序员的遍历,系统会自动创建 EntrySet 集合 ,该集合存放的元素的类型为 Map.Entry, 而一个 Entry
           对象又指向一个 HashMap$Node。
        
        3. EntrySet 集合中, 存储对象的类型是 Map.Entry ,但实际上存放的还是 HashMap$Node 的引用。
           这是因为 static class Node<K,V> 类 实现了 Map.Entry<K,V> 接口。
        
        4. 当把 HashMap$Node 对象D的引用 存放到 EntrySet 集合后,就方便我们对 k-v 的遍历, 因为 Map.Entry接口中 提供了重要方法
           getKey()和 getValue() 用于对 k-v 的遍历。 

        5. Map 接口中提供了 keySet() 方法 和 values() 方法,分别用于返回一个只存储 key 的 Set 集合,和一个只存储 
            value 的 Collection 集合。
        */

        Set set = map.entrySet();
        System.out.println(set.getClass());// 运行类型为 HashMap$EntrySet

        for (Object obj : set) {
    
    

            System.out.println(obj.getClass()); // 运行类型为 HashMap$Node
            
            // 为了从 HashMap$Node 取出k-v,要 向下转型
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "-" + entry.getValue() );
        }

        // Map 接口提供的 keySet() 方法,返回一个只存储 key 的 Set 集合
        Set set1 = map.keySet();
        System.out.println(set1.getClass());


        // Map 接口提供的 values() 方法,返回一个只存储 value 的 Collection 集合。
        Collection values = map.values();
        System.out.println(values.getClass());

    }
}

3. Common methods of the Map interface

  • Code demo:

public class MapMethod {
    
    
    public static void main(String[] args) {
    
    

        Map map = new HashMap();

//      1.  put: 添加和替换,成功添加则返回 null,替换则返回被替换的 value
        map.put("邓超", "孙俪");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘令博", null);
        map.put(null, "刘亦菲");
        map.put("鹿晗", "关晓彤");

        System.out.println("map=" + map);

//      2.  remove:根据键删除映射关系
        map.remove(null);
        System.out.println("map=" + map);

//      3.  get:根据键获取值
        Object val = map.get("鹿晗");
        System.out.println("val=" + val);

//      4.  size:获取元素个数
        System.out.println("k-v=" + map.size());

//      5.  isEmpty:判断元素个数是否为0
        System.out.println(map.isEmpty());// F

//      6.  containsKey:查找键是否存在
        System.out.println("结果=" + map.containsKey("hsp"));// T

//      7.  clear:清除所有k-v
        map.clear();
        System.out.println("map=" + map);// []

    }
}

4. Map interface traversal method

Map interface traversal method:
Method 1: First use the keySet() method in the Map interface to obtain a Set collection that only stores the key, and then call the get() method through the key to obtain the corresponding value.
Method 2: directly use the value() method in the Map interface to get a Collection that only stores values ​​(this method is not recommended because the mapping relationship cannot be obtained).
Method 3: First use the entrySet() method in the Map interface to obtain an EntrySet collection that stores the Entry class object. The Entry class object points to the key-value HashMap$Node node, and then calls the Entry class object’s getKey() and getVal() method to get the corresponding key and value.

  • Code demo:

public class MapFor {
    
    
    public static void main(String[] args) {
    
    

        Map map = new HashMap();

        map.put("邓超", "孙俪");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘令博", null);
        map.put(null, "刘亦菲");
        map.put("鹿晗", "关晓彤");

        // 方式一:先使用 Map 接口中的 keySet() 方法得到一个 只存储 key 的 Set 集合,
        //        再通过 key 调用 get() 方法,得到对应的 value。
        Set keyset = map.keySet();

        // (1) 增强for
        System.out.println("-----第一种方式-------");
        for (Object key : keyset) {
    
    
            System.out.println(key + "-" + map.get(key));
        }
        
        // (2) 迭代器
        System.out.println("----第二种方式--------");
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
    
    
            Object key =  iterator.next();
            System.out.println(key + "-" + map.get(key));
        }

        // 方式二:直接使用 Map 接口中的 value() 方法得到一个只存储 value 的 Collection 集合
        //      (不推荐使用该方法,因为得不到映射关系)。
        
        Collection values = map.values();
        // 这里可以使用所有的Collections使用的遍历方法
         
        // (1) 增强for
        System.out.println("---取出所有的value 增强for----");
        for (Object value : values) {
    
    
            System.out.println(value);
        }
        // (2) 迭代器
        System.out.println("---取出所有的value 迭代器----");
        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
    
    
            Object value =  iterator2.next();
            System.out.println(value);

        }

        //方式三:先使用 Map 接口中的 entrySet() 方法,得到一个存储了 Entry 类对象的 EntrySet 集合,
        //      Entry 类对象指向了 key-value 的 HashMap$Node 结点,
        //      再调用 Entry 类对象 的getKey() 和 getVal() 方法,得到对应的 key 和 value。
        Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
        
        // (1) 增强for
        System.out.println("----使用EntrySet 的 for增强(第3种)----");
        for (Object entry : entrySet) {
    
    
            //将entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }

        // (2) 迭代器
        System.out.println("----使用EntrySet 的 迭代器(第4种)----");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()) {
    
    
            Object entry =  iterator3.next();
            // System.out.println(next.getClass());// HashMap$Node 类 -实现-> Map.Entry接口 (getKey, getValue)
            
            // 向下转型 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}

3. HashMap class (hash map)

1. Features of the HashMap class

(1) The HashMap class is also a hash table. This class implements the Map interface, and its stored content is a key-value pair (key-value) mapping. It has a fast access speed, but it does not support thread synchronization and is not safe.
(2) The key in the HashMap cannot be repeated, but the value can be repeated; the key only allows one null (null value), but the value can have multiple nulls (null values).
(3) The HashMap class is the same as the HashSet class. The stored elements are unordered (because it stores elements according to the HashCode value of the key), and their bottom layer maintains the data structure of array + linked list + red-black tree

2. HashMap underlying mechanism and source code analysis (critical and difficult points)

2.1 The underlying mechanism of HashMap adding key-value

  • Source code analysis:

(1) The bottom layer of HashMap maintains an array table of Node type, and the default capacity is nul.
(2) When creating an object of the HashMap class, initialize the load factor (loadfactor) to 0.75.
(3) When adding a key-value, get the index in the table through the hash value of the key, and then judge whether the node at the index position is null.
(4) If it is null, directly add key-value; if the node is not null, continue to judge whether the key of the node is equal to the key to be added; if they are equal, directly replace the value of the two; if not equal It is necessary to judge whether the nodes connected by the index are in a tree structure or a linked list structure.
(5) If it is a tree structure, use the method in the tree structure of the tree structure, which will not be discussed in depth here.
(6) If it is a linked list structure, traverse each node in the linked list. If the key value of no node is equal to the key of the new node, add the new node at the end of the linked list and judge whether to tree; If the key value of the existing node is equal to the key value of the new node, directly replace the corresponding value of the two.

  • Schematic diagram of the underlying data structure:

insert image description here

  • Source code analysis:

public class HashMapSource1 {
    
    
    public static void main(String[] args) {
    
    
        HashMap map = new HashMap();
        map.put("java", 10);// ok
        map.put("php", 10);// ok
        map.put("java", 20);// 替换value

        System.out.println("map=" + map);//

源码剖析:

1. 执行构造器 new HashMap()

   初始化加载因子 loadfactor = 0.75
   HashMap$Node[] table = null

2. 执行put()方法, 先调用 hash 方法,计算 key 的 hash值 (h = key.hashCode()) ^ (h >>> 16)

    public V put(K key, V value) {
    
    //K = "java" value = 10
        return putVal(hash(key), key, value, false, true);
    }

3. 执行 putVal()方法

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
           boolean evict) {
    
    
        Node<K,V>[] tab; Node<K,V> p; int n, i;// 辅助变量

        // 如果底层的 table 数组为 null, 或者 length =0 , 就调用 resize()方法 进行扩容到 16 (第一次扩容)
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

        // 取出 hash 值对应的 table 的索引位置的 Node, 如果为 null, 就直接把准备加入的 k-v
        //  封装成一个 Node ,加入该位置即可
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

        // 如果该索引位置的 Node 不为 null,则进入该分支
        else {
    
    

            Node<K,V> e; K k;// 辅助变量
         // 如果 table 的索引位置的 key 的 hash 和新元素的 key 的 hash 值相同,
         // 并且满足 (table 现有的结点的 key 和准备添加的 key 是同一个对象  || equals返回真)
         // 则说明: 新结点和数组中旧结点的 key 相同,
         // 就将辅助结点 e 指向 p,并直接替换该结点原有的 value值
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;

        //  如果 新结点和数组中旧结点的 key 不相同,且 数组连接的是 红黑树,则进入该分支,不细讲
            else if (p instanceof TreeNode)// 如果当前的table的已有的 Node 是红黑树,就按照红黑树的方式处理
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

        //  如果 新结点和数组中旧结点的 key 不相同, 则进入数组连接的链表,
        //  循环遍历比较链表中每个结点的 key 与 新结点的 key 是否相同。       
            else {
    
    
                
                for (int binCount = 0; ; ++binCount) {
    
    // 死循环

                    // 首先,辅助结点 e 直接指向 p 的下一个结点,
                    // 然后判断该结点是否存在 元素,如果不存在,说明 新结点 要添加进链表中
                    // 且此时的 e == null,不会执行最下面交换 value 的代码
                    if ((e = p.next) == null) {
    
    
                        p.next = newNode(hash, key, value, null);

                        // 加入后,立刻判断当前链表的结点个数,是否已经到8个,到8个后
                        // 就调用 treeifyBin() 方法对链表进行红黑树的转换
                        if (binCount >= TREEIFY_THRESHOLD - 1) 
                            treeifyBin(tab, hash);
                        break;// 然后直接退出循环
                    }

                    //  如果在循环比较过程中,发现旧结点有相同的 key, 就直接 break,
                    //  此时的 e 不为 null,所以会进入下面交换 value 的代码
                    if (e.hash == hash && 
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;

                    // 如果前面的两个判断条件都不满足,说明要继续向下一个结点进行比较
                    // 这里更新辅助结点 p 的指向,然后进入下次循环的起始位置,辅助结点 e 的指向也会更新 
                    p = e;
                }
            }

            // 这部分代码负责交换 value,只要进入该代码,都会交换两结点的value
            // 只要 e 指向的结点不为空,就代表存在旧结点与新结点的 key 相同
            // 无论它们的 value 相不相同,都交换它们的 value。
            if (e != null) {
    
     
                V oldValue = e.value;

                // !onlyIfAbsent 始终为真,是传入的参数
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value; //替换 两结点 key 对应的 value
                afterNodeAccess(e);

                // 退出 putVal()方法,返回被替换的 value,注意size和modcount没有增加。 
                return oldValue;
            }
        }

        ++modCount;// 每增加一个 Node 结点 ,就 size++,modCount++
        if (++size > threshold)// 如果size > 临界值,就进行数组扩容
            resize();
        afterNodeInsertion(evict);
        return null;
    }

4. 补充:关于树化的过程(链表转成红黑树)
    // 进入 putTreeVal()方法后,不会马上树化,而是先判断数组 table 的长度
    // 如果 table 为 null ,或者其大小(size)还没有到 64,暂时不树化,而是进行数组扩容.
    // 直到满足 size > 64 ,且 某条链表上结点的个数 >= 8 这两个条件
    // 才会真正的树化 -> 剪枝。

    final void treeifyBin(Node<K,V>[] tab, int hash) {
    
    
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
    }
         
    }
}

2.2 The underlying expansion mechanism of HashMap

(1) When HashMap uses the put() method to add kv, it will call the resize() method to expand the array.
(2) When adding kv for the first time, the resize() method will expand the size of the array (size variable) to 16; and set an array threshold threshold in the method, which is 0.75 times the size of the array. When the stored elements reach the critical value, the resize() method will be called again to expand the array. The default is to expand to twice the size of the previous array, and then update the critical value threshold.
(3) When the number of nodes in the linked list in an array position in the HashTable reaches a fixed value (8 by default), the hash table will convert the ordinary one-way linked list into a red-black tree, but the premise The size of the array has reached 64; otherwise, the array must be expanded (until the size of the array reaches 64), and then treed.

  • Source code analysis:

public class HashMapIncrement {
    
    
    public static void main(String[] args) {
    
    
        /*
        HashMap 第一次添加时 k-v 时,table 数组扩容到 16,
        临界值(threshold)是 16 * 加载因子(loadFactor == 0.75) = 12
        如果table 数组使用到了临界值 12, 就会将数组扩容到 16 * 2 = 32,
        新的临界值就是 32 * 0.75 = 24, 依次类推
         */
        HashMap map = new HashMap();

        // 往哈希图中添加 k-v ,下列的每个 k-v 都是添加到集合的数组中,不会添加到链表中
        // 所以哈希图将会一直进行数组的扩容。
        for(int i = 1; i <= 100; i++) {
    
    
            map。put("" + i, i); 
        }

        /*
        在Java8中, 如果一条链表的元素个数 >= TREEIFY_THRESHOLD(默认是 8 ),
        并且table的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树),
        否则仍然采用数组扩容机制,意思是说到第11个元素时就会扩容到64了
         */
        map = new HashMap(); // 创建一个新的哈希图

        // 往哈希图中添加 key ,但与上面不同,这次添加的 k-v 会添加到数组的同一个索引位置,
        // 因此数组的大小不会改变,依旧是默认的 16。
        // 由于每个结点的 key 都不同,所以这些结点会加入到数组的单向链表中,
        // 当链表中的结点增加到8个时,哈希图就要将普通链表进行树化;
        // 但此时数组的大小为 16,不满足树化要求的数组大小为 64,所以要先进行数组的扩容;
        // 则新加入的第9、10个结点依然是添加到单向链表的后面,此时数组大小扩容到 64;
        // 在添加第11 个结点时,数组大小和链表长度都满足了树化的条件,因此哈希图将链表进行树化。
        // 注意,本例中的每个结点都是不同的,但他们的 hash 值相同,因此加入的数组的索引位置相同。
        for(int i = 1; i <= 12; i++) {
    
    
            map.put(new A(i), i);
        }


        /*
            当我们向 map 增加一个元素,-> Node -> 加入table , 就算是增加了一个size
            在 table中 size > threshold ,就会扩容
         */

        map = new HashMap(); // 再次创建一个新的哈希图

        // 在 哈希图的某一条链表上添加了 7个 A对象-100 键值对
        for(int i = 1; i <= 7; i++) {
    
    
            map.put(new A(i), 100);
        }
        
        // 在另一条链表上添加到第4 个 B对象-200 键值对的时候,size = 12,到达临界值,数组会进行 resize()扩容
        // 但是由于为满足某条链表的结点个数 >= 8,所以不会进行树化。 
        for(int i = 1; i <= 7; i++) {
    
    
            map.put(new B(i), 200);
        }
    }
}

class B {
    
    
    private int n;

    public B(int n) {
    
    
        this.n = n;
    }
    @Override
    public int hashCode() {
    
    
        return 200;
    }
}

class A {
    
    
    private int n;

    public A(int n) {
    
    
        this.n = n;
    }
    @Override
    public int hashCode() {
    
    
        return 100;
    }
}
  • The above is the implementation process of the underlying expansion of HashMap when adding key-value. Friends have to debug it by themselves to understand it deeply.

Fourth, the HashTable class (hash table)

Features of the HashTable class

(1) HashTable is also a hash table, which stores the mapping of key-value pairs (key-value).
(2) HashTable inherits from Dictionary class and implements Map interface.
(3) HashTable is synchronizable, which means it is thread-safe.
(4) Neither the key nor the value of HashTable can be nul, otherwise a NullPointerException will be thrown.
(5) The usage method of HashTable is basically the same as that of HashMap, and all methods in the Map interface can be used.
(6) There is an array Hashtable$Entry[] at the bottom layer. The default initialization size of the array is 11, and the critical value threshold = 11 * 0.75 = 8; the default expansion mechanism is the original 2 times + 1.

  • Code demo:

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);// 添加失败,抛出异常 NullPointerException
        table.put("lucy", 100);// ok
        table.put("lic", 100);// ok
        table.put("lic", 88);// 100 被替换成 88

        System.out.println(table);
    }
}

5. Properties class

Basic introduction to the Properties class

insert image description here

  • Code demo:

public class Properties_ {
    
    
    public static void main(String[] args) {
    
    

        //1. Properties 继承  Hashtable
        //2. 可以通过 k-v 存放数据,当然key 和 value 不能为 null
        //增加
        Properties properties = new Properties();
        //properties.put(null, "abc");// 抛出 空指针异常
        //properties.put("abc", null); // 抛出 空指针异常
        properties.put("john", 100);
        properties.put("lucy", 100);
        properties.put("lic", 100);
        properties.put("lic", 88);// 如果有相同的key , value被替换

        System.out.println("properties=" + properties);

        // 通过k 获取对应值
        System.out.println(properties.get("lic"));// 88

        // 删除
        properties.remove("lic");
        System.out.println("properties=" + properties);

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

    }
}

Six, TreeMap class (tree map)

  • Source code analysis:

public class TreeMap_ {
    
    
    public static void main(String[] args) {
    
    

        // 使用默认的构造器,创建 TreeMap, 是无序的(也没有排序)

        // TreeMap treeMap = new TreeMap();
    
        // 使用构造器创建
        TreeMap treeMap = new TreeMap(new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                
             // return ((String) o2).compareTo((String) o1);  按照传入的 k(String) 的大小进行排序
                
                return ((String) o2).length() - ((String) o1).length();// 按照K(String) 的长度大小排序
            }
        });

        treeMap.put("jack", "杰克");
        treeMap.put("tom", "汤姆");
        treeMap.put("kristina", "克瑞斯提诺");
        treeMap.put("smith", "斯密斯");
        treeMap.put("hsp", "韩顺平");// 加入不了,因为hsp 和 tom长度相同

        System.out.println("treemap=" + treeMap);


源码分析:

1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给 TreeMap 的 comparator

    public TreeMap(Comparator<? super K> comparator) {
    
    
        this.comparator = comparator;
    }

2. 调用 put()方法

    2.1 第一次添加 k- v, 把 k-v 封装到 Entry对象,放入 root

    Entry<K,V> t = root;
    if (t == null) {
    
    
        compare(key, key); 

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }

    2.2 以后再添加 k- v

    Comparator<? super K> cpr = comparator;

    if (cpr != null) {
    
    

        // 遍历所有的key , 给当前 key 找到适当位置
        do {
    
     
            parent = t;
            cmp = cpr.compare(key, t.key);// 动态绑定到我们的匿名内部类的 compare方法
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;

            else  // 如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加
                return t.setValue(value);
        } while (t != null);
    }

    }
}

7. Collections tool class

(1) Collections is a tool class for manipulating collections/graphs such as Set, List and Map.
(2) Collections provides a series of static methods to sort, query, modify and other operations on the elements in the collection/graph.

  • Method introduction:
    insert image description here
    insert image description here

  • Code demo:


public class Collections_ {
    
    
    public static void main(String[] args) {
    
    

        //创建ArrayList 集合,用于测试
        
        List list = new ArrayList();
        list.add("tom");
        list.add("smith");
        list.add("king");
        list.add("milan");
        list.add("tom");


//      1.  reverse(List):反转 List 中元素的顺序
        Collections.reverse(list);
        System.out.println("list=" + list);

//      2.  shuffle(List):对 List 集合元素进行随机排序
       for (int i = 0; i < 5; i++) {
    
    
           Collections.shuffle(list);
           System.out.println("list=" + list);
       }

//      3.  sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
        Collections.sort(list);
        System.out.println("自然排序后");
        System.out.println("list=" + list);

//      4.  sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
      
        // 按照 字符串的长度大小排序
        Collections.sort(list, new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                //可以加入校验代码.
                return ((String) o2).length() - ((String) o1).length();
            }
        });
        System.out.println("字符串长度大小排序=" + list);


//      5.  swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

        Collections.swap(list, 0, 1);
        System.out.println("交换后的情况");
        System.out.println("list=" + list);

//      6.  Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println("自然顺序最大元素=" + Collections.max(list));

//      7.  Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
      
        //比如,我们要返回长度最大的元素
        Object maxObject = Collections.max(list, new Comparator() {
    
    
            @Override
            public int compare(Object o1, Object o2) {
    
    
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最大的元素=" + maxObject);


//      8.  Object min(Collection)
//      9.  Object min(Collection,Comparator)
        //上面的两个方法,参考max即可

//      10. int frequency(Collection,Object):返回指定集合中指定元素的出现次数
        System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));

//      11. void copy(List dest,List src):将src中的内容复制到dest中

        ArrayList dest = new ArrayList();
        // 为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样
        for(int i = 0; i < list.size(); i++) {
    
    
            dest.add("");
        }
        // 拷贝
        Collections.copy(dest, list);
        System.out.println("dest=" + dest);

//      12. boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值

        // 如果list中,有tom 就替换成 汤姆
        Collections.replaceAll(list, "tom", "汤姆");
        System.out.println("list替换后=" + list);

    }
}

Summarize

How to choose a collection implementation class during development (remember)

insert image description here

to feel

  • This article is the learning notes compiled and summarized by Xiaobai blogger when he was studying the Java online class of teacher Han Shunping at station B. Here, I would like to thank teacher Han Shunping for his online class. If you are interested, you can also go and have a look.
  • This article introduces the basic concepts of the collection framework in detail, and explains in depth the precautions and common methods of the Map interface, HashMap class, HashTable class, and TreeMap class commonly used in Map diagrams; it also analyzes the source codes of each subclass implementation, citing There are many, many examples, I hope you guys can gain something after reading it!
  • Finally, if there are any mistakes or omissions in this article, everyone is welcome to criticize and correct! work hard together! ! See you in the next blog post!

Guess you like

Origin blog.csdn.net/weixin_45395059/article/details/125850642