HashMap,LinkedHashMap,HashSet,LinkedHashSet

首先点开HashMap的源码,看下介绍
继承的是Map,就是个键值对的集合,key和value支持null。存储的数据是无序的,而且这个不是线程安全的,多个线程同时操作,可能会发生问题。

/**
 * HashMap is an implementation of {@link Map}. All optional operations are supported.
 *
 * 

All elements are permitted as keys or values, including null. * *

Note that the iteration order for HashMap is non-deterministic. If you want * deterministic iteration, use {@link LinkedHashMap}. * *

Note: the implementation of {@code HashMap} is not synchronized. * If one thread of several threads accessing an instance modifies the map * structurally, access to the map needs to be synchronized. A structural * modification is an operation that adds or removes an entry. Changes in * the value of an entry are not structural changes. * *

The {@code Iterator} created by calling the {@code iterator} method * may throw a {@code ConcurrentModificationException} if the map is structurally * changed while an iterator is used to iterate over the elements. Only the * {@code remove} method that is provided by the iterator allows for removal of * elements during iteration. It is not possible to guarantee that this * mechanism works in all cases of unsynchronized concurrent modification. It * should only be used for debugging purposes. * * @param the type of keys maintained by this map * @param the type of mapped values */ public class HashMap extends AbstractMap implements Cloneable, Serializable

下边通过代码测试下:

        HashMap hashMap=new HashMap<>();
        hashMap.put("key1", "value1");
        hashMap.put("key2", "value2");
        hashMap.put("key3", "value3");
        hashMap.put("key4", "value4");
        hashMap.put(null, "value4");
        hashMap.put("key5", null);
        Set keys=hashMap.keySet();
        for(String key:keys) {
            System.err.println(key);
        }
        System.err.println("=========");
        Collection values=hashMap.values();
        for(String value:values) {
            System.err.println(value);
        }

    //打印结果
key1
null
key2
key5
key3
key4
=========
value1
value4
value2
null
value3
value4

可以看到打印结果和我们存进去的顺序是不一样的。
下边看下put方法,返回值是上一个关联的value

     * @return the value of any previous mapping with the specified key or
     *         {@code null} if there was no such mapping.
     */
    @Override public V put(K key, V value) {
        if (key == null) {
            return putValueForNullKey(value);//看这里,key是可以为空的
        }
      //下面这些代码是,循环查看有没有一样的key,有的话把value值修改为当下的
        int hash = Collections.secondaryHash(key);
        HashMapEntry[] tab = table;
        int index = hash & (tab.length - 1);
        for (HashMapEntry e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }

        // No entry for (non-null) key is present; create one,没有找到一样的key,那就新建一个存进去
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        addNewEntry(key, value, hash, index);
        return null;
    }

这里需要看下HashMapEntry里都有啥,可以看到除了key,value,hashcode,还有一个next的东西,这个是啥?

    static class HashMapEntry implements Entry {
        final K key;
        V value;
        final int hash;
        HashMapEntry next;

        HashMapEntry(K key, V value, int hash, HashMapEntry next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }

看这里添加新元素的代码,说实话,这个index是key的hashcode操作来的,看里边各种左移右移的,都晕了,不管了。

    void addNewEntry(K key, V value, int hash, int index) {
        table[index] = new HashMapEntry(key, value, hash, table[index]);
    }

看一下数据的获取

    public Set> entrySet() {
        Set> es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }
    @Override public Set keySet() {
        Set ks = keySet;
        return (ks != null) ? ks : (keySet = new KeySet());
    }

    @Override public Collection values() {
        Collection vs = values;
        return (vs != null) ? vs : (values = new Values());
    }

可以看到entry和key返回的都是Set,而values返回的Collection,为啥,因为前者不能有重复元素,后者可以。

/**
 * A {@code Set} is a data structure which does not allow duplicate elements.
 *
 * @since 1.2 set没有重复元素,Collection有,所以也能看出hasmap的key是不能重复的
 */
public interface Set extends Collection

另外,我们学的时候知道这HashMap数据是无序的,而LinkedHashMap是有序的,为啥了,我们都知道他们是继承关系,
然后看下里边的方法,有序无语和put是没啥关系的。关键就是取的时候。

public class LinkedHashMap
    extends HashMap
    implements Map

对比下两者取的方法,返回的Set不一样

//HashMap的
    public Set> entrySet() {
        Set> es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }
//LinkedHashMap的
    public Set> entrySet() {
        Set> es;
        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
    }

其实是这里不同,他们两个存储的实体类有点区别,linked的多了2个before和after。

    static class Entry extends HashMap.Node {
        Entry before, after;
        Entry(int hash, K key, V value, Node next) {
            super(hash, key, value, next);
        }
    }

看下hashmap的put方法

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node[] tab; Node p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);//这里是新建一个Node
        else

//HashMap里newNode是这样的
    Node newNode(int hash, K key, V value, Node next) {
        return new Node<>(hash, key, value, next);
    }
//LinkedHashMap,可以看到它对before和after进行了赋值
    Node newNode(int hash, K key, V value, Node e) {
        LinkedHashMap.Entry p =
            new LinkedHashMap.Entry(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }
    private void linkNodeLast(LinkedHashMap.Entry p) {
        LinkedHashMap.Entry last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

这样就了解了,比如第一条数据p0存进来,这时候head,tail都是空的,所以会走if那里,结果就是
tail=p0,head=p0;
然后我们继续存第二条p1,这时候,head和tail都是p0,也就是不为空,会走else方法,结果就是
tail=p1, p1的before=p0,p0的after=p1
后边的就一样的道理,before存的是上一条数据,after存的是后一条数据。
所以这样就有序了,
可以看下它取数据的代码

    abstract class LinkedHashIterator {
        LinkedHashMap.Entry next;
        LinkedHashMap.Entry current;
        int expectedModCount;

        LinkedHashIterator() {
            next = head;//head就是我们存的第一条数据
            expectedModCount = modCount;
            current = null;
        }

        public final boolean hasNext() {
            return next != null;
        }

        final LinkedHashMap.Entry nextNode() {
            LinkedHashMap.Entry e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e;
            next = e.after;//after就是上边分析的,下一条数据拉。
            return e;
        }

最后看下linkedHashMap里有个boolean到底干啥的,下面是源码的英文说明,举例例子:
true的话,就是我们常说的文章的修改时间排序一样,false就按照文章的创建时间排序一样。

    /**
     * The iteration ordering method for this linked hash map: true
     * for access-order, false for insertion-order.
     *
     * @serial
     */
    final boolean accessOrder;

比如还是上边的代码,我们修改这个accessOrder为true,

        HashMap hashMap=new LinkedHashMap(2,0.75f,true);
        hashMap.put("key1", "value1");
        hashMap.put("key2", "value2");
        hashMap.put("key3", "value3");
        hashMap.put("key2", "value22");

打印出来的顺序就成了 key1,key3,key2拉,嗯,因为key2最后修改了一下,所以它跑到key3后边去了

顺道看下HashSet,LinkedHashSet,可以发现,他们里边的存储就是HashMap和LinkedHashMap

public HashSet() {
        map = new HashMap<>();
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
private static final Object PRESENT = new Object();

没看懂

里边的index,hashcode我是看晕了,左移,右移,又与操作,最后得出来的index,完全看不懂啊,嗯,说着说,完全不想去看懂,

自己简单看完源码,又百度搜了下,看这篇好像还不错,没仔细看。也许以后需要再看
http://blog.csdn.net/justloveyou_/article/details/71713781

你可能感兴趣的:(HashMap,LinkedHashMap,HashSet,LinkedHashSet)