`

LinkedHashMap与LinkedHashSet可预知迭代顺序

    博客分类:
  • java
阅读更多

一,简介

       jdk中HashMap和HashSet的遍历是无序的,而LinkedHashMap和LinkedHashSet是两种可以预知迭代顺序的集合类。LinkedHashMap支持两种迭代顺序(插入顺序和访问顺序,其中默认是插入顺序)。而LinkedHashSet仅支持按插入顺序遍历。

二,实现原理

      LinkedHashMap<K,V>继承自HashMap<K,V>,与HashMap的区别是LinkedHashMap底层还维护着一个双向循环链表(用于记录元素的插入顺序或访问顺序)。

      结构图:

      JDK源码:

     

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{

    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * The head of the doubly linked list.
     */
    private transient Entry<K,V> header;//双向循环链表的表头

    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    private final boolean accessOrder;
    
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;//默认为插入顺序,如果要以访问顺序遍历,需要设置为true
    }

    public LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
    /**
     *LinkedHashMap重写了HashMap中的get方法
     */
    public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);//记录访问顺序
        return e.value;
    }
    
      /**
     * LinkedHashMap entry. 
     * 由于继承于HashMap.Entry<K,V>,所以Entry<K,V>实际上包含以下三个Entry<K,V>属性:before,after,next(HashMap是基于单链表+数组实现的,next单链表节点的后继元素)。
     */
    private static class Entry<K,V> extends HashMap.Entry<K,V> {
        // These fields comprise the doubly linked list used for iteration.
        Entry<K,V> before, after;//双向链表中节点的前驱和后继节点引用

	Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }

        /**
         * Removes this entry from the linked list.
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }

        /**
         * Inserts this entry before the specified existing entry in the list.
         */
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {//如果是访问顺序,则修改header引用为最新访问的节点元素
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

        void recordRemoval(HashMap<K,V> m) {
            remove();
        }
    }
}

   LinkedHashMap中的访问顺序遍历,已经为我们实现LRU算法(最近最少使用算法)提供了便利。记录访问顺序:将最新访问的元素添加到双向循环链表的表头,并从原来的位置删除。由于链表的增加、删除操作是常量级的,故并不会带来性能的损失。LinkedHashMap类是重写了HashMap中的get方法(添加记录访问顺序逻辑)。

   下面介绍一下LinkedHashSet的实现原理

    LinkedHashSet是继承HashSet实现的,而HashSet是不具备按插入顺序遍历的。那么LinkedHashSet又是如何实现按插入顺序遍历的呢?JDK源码如下:

  

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    private static final long serialVersionUID = -2851667679971038690L;

    /**
     * Constructs a new, empty linked hash set with the specified initial
     * capacity and load factor.
     *
     * @param      initialCapacity the initial capacity of the linked hash set
     * @param      loadFactor      the load factor of the linked hash set
     * @throws     IllegalArgumentException  if the initial capacity is less
     *               than zero, or if the load factor is nonpositive
     */
    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    /**
     * Constructs a new, empty linked hash set with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param   initialCapacity   the initial capacity of the LinkedHashSet
     * @throws  IllegalArgumentException if the initial capacity is less
     *              than zero
     */
    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    /**
     * Constructs a new, empty linked hash set with the default initial
     * capacity (16) and load factor (0.75).
     */
    public LinkedHashSet() {
        super(16, .75f, true);
    }

    /**
     * Constructs a new linked hash set with the same elements as the
     * specified collection.  The linked hash set is created with an initial
     * capacity sufficient to hold the elements in the specified collection
     * and the default load factor (0.75).
     *
     * @param c  the collection whose elements are to be placed into
     *           this set
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
}

   通过阅读JDK源码,发现LinkedHashSet中并没有像LinkedHashMap一样实现一个记录可预订顺序的双向循环链表。那肯定是HashSet帮LinkedHashSet做了这件事, 于是乎继续阅读HashSet实现源码。

 

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

     public HashSet() {
	map = new HashMap<E,Object>();
    }
    
     /**访问权限为public,组合的是HashMap*/
    public HashSet(int initialCapacity, float loadFactor) {
	map = new HashMap<E,Object>(initialCapacity, loadFactor);
    }
    /**访问权限为defalut为包内访问,组合的是LinkedHashMap,由于HashSet没有提供可以设置
     * accessOrder属性的构造函数,则LinkedHashSet仅具备按插入顺序遍历的功能
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
	map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
    }
}

   通过阅读HashSet的源码,LinkedHashSet最终是组合LinkedHashMap来实现的,但由于父类HashSet没有提供赋值accessOrder属性的入口,所以只具备按插入顺序遍历的功能。平常我们在应用中使用的HashSet都是基于组合HashMap实现的,所以HashSet的遍历是无序的。

三,总结

   LinkedHashMap(插入顺序和访问顺序)和LinkedHashSet(插入顺序),这两种集合类操作是非线程安全的。


 
 

  • 大小: 26.8 KB
分享到:
评论

相关推荐

    ritelinked:LinkedHashMap和LinkedHashSet

    RiteLinked-类似HashMap的容器,以用户可控制的顺序保存其键值对 提供了LinkedHashMap和LinkedHashSet更多最新版本。 您可以在std或no_std环境中轻松使用它。 一些实用的功能组合,支持,帮助您更好地将其嵌入到...

    RiteLinked - Rust 中的 LinkedHashMap 和 LinkedHashSet

    RiteLinked——类似HashMap的容器,以用户可控的顺序保存它们的键值对RiteLinked提供了LinkedHashMap和LinkedHashSet更多最新版本。您可以在std或no_std环境中轻松使用它。一些实用的功能组合,支持,帮助您更好地将...

    LinkedHashmap的使用

    因此,迭代LinkedHashMap时,元素会按照插入或访问的顺序返回,提供了可预测的遍历顺序。 **LinkedHashMap的工作原理** LinkedHashMap内部维护了一个双向链表,每个元素既是链表中的节点,也是哈希表中的键值对。...

    HashMap,HashTable,LinkedHashMap,TreeMap的区别

    LinkedHashMap 的输出顺序与输入顺序保持一致。LinkedHashMap 的实现类似于 HashMap,但是它维持了一个双向链表,以便于维持顺序。 HashTable HashTable 是一种同步的 Map 实现类,它继承自 Dictionary 类,实现了...

    LinkedHashMap

    - `entrySet()`: 返回一个Set视图,包含了`LinkedHashMap`的所有键值对,迭代时按插入或访问顺序返回。 4. **应用场景** - 当需要保持元素的插入顺序或者访问顺序时,`LinkedHashMap`是理想的选择,例如日志记录...

    java中HashMap,LinkedHashMap,TreeMap,HashTable的区别

    这使得`LinkedHashMap`在迭代时能够保持元素的插入顺序。 - **特点**: - **有序性**:`LinkedHashMap`可以按照插入顺序返回元素,这对于需要保持元素插入顺序的应用非常有用。 - **访问顺序**:除了插入顺序外,...

    Java中LinkedHashMap源码解析

    LinkedHashMap是Java中的一种哈希表实现,它继承自HashMap,具有可预知的迭代顺序。LinkedHashMap维护着一个运行于所有条目的双重链表结构,该链表定义了迭代顺序,可以是插入或者访问顺序。 LinkedHashMap的节点...

    java HashMap,TreeMap与LinkedHashMap的详解

    `HashMap`不保证元素的顺序,特别是当迭代遍历Map时,元素的顺序可能会发生变化。此外,`HashMap`允许null键和null值。 2. **TreeMap** `TreeMap`是基于红黑树数据结构实现的,它保证了Map中的元素按照键的自然...

    hashlink-类似HashMap的容器,以用户可控制的顺序保存其键值对-Rust开发

    此板条箱是链接哈希表的一个分支,用于构建o hashlink-类HashMap的容器,将其键值对保存在用户中可控制的顺序该板条箱是基于hash-brown的links-hash-map的一个分叉,用于实现LinkedHashMap,LinkedHashSet和LruCache...

    详解Java中LinkedHashMap

    Java中的LinkedHashMap是HashMap的一个子类,主要解决了HashMap的两个问题:一是迭代的顺序问题,HashMap的迭代顺序不一定是元素插入的顺序;二是线程安全问题,HashMap不是线程安全的。LinkedHashMap通过维护一个...

    linked_hash_set-具有插入顺序的HashSet-Rust开发

    linked_hash_set此库基于元素的插入顺序提供了具有可预测迭代顺序的哈希集。 它实现为linked_hash_set。此库基于元素的插入顺序提供了具有可预测迭代顺序的哈希集。 它实现为linked_hash_map :: LinkedHashMap,其中...

    JavaLinkedHashMap源码解析Java开发Ja

    Java LinkedHashMap 是一个根据插入顺序或者访问顺序来维护元素顺序的哈希表与链表相结合的数据结构。在 Java 集合框架中,它继承自 HashMap 类,并实现了 Map 接口。LinkedHashMap 的特点在于,它不仅仅是一个哈希...

    java软件技术文档-深入java8的集合4:LinkedHashMap的实现原理.pdf

    - **iterator()**: 返回一个迭代器,该迭代器按照 LinkedHashMap 中定义的顺序遍历元素。这可能是插入顺序或访问顺序,取决于 `accessOrder` 的设置。 - **entrySet()**: 返回一个 Set 视图,包含映射的所有键值对...

    java中set、list和map的使用方法实例

    // HashSet不保证迭代顺序, LinkedHashSet按照元素插入的顺序迭代. // 学习List对象容器的使用 // List容器中的对象允许重复 // 常用的list接口的实现类有ArrayList和LinkedList // 学习map对象容器的使用 // map...

    indexmap:具有一致顺序和快速迭代的哈希表; 通过键或序列索引访问项目

    这与Python中的`collections.OrderedDict`或者Java 8及更高版本的`LinkedHashMap`类似。一致的顺序对于那些依赖于元素插入顺序的场景非常有用,例如日志记录、序列化或者需要保持输出稳定性的算法。 **通过键或序列...

    Java集合系列之LinkedHashMap源码分析

    Java集合系列之LinkedHashMap源码分析 Java集合系列之LinkedHashMap源码分析是Java集合...LinkedHashMap的源码分析展示了其链表结构和哈希表结构的实现细节,展示了LinkedHashMap如何实现按插入顺序和访问顺序排序的。

    一文搞懂Java的LinkedHashMap.docx

    与HashMap不同,LinkedHashMap在遍历时会按照元素的插入顺序依次输出,即使`null`值也是如此。 除了插入顺序,LinkedHashMap还支持按照访问顺序排序,即每次访问某个元素后,该元素会被移动到链表末尾。只需在创建...

Global site tag (gtag.js) - Google Analytics