【JDK源码阅读9-util】LinkedHashMap接口

LinkedHashMap接口

一、类继承关系

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

二、LinkedHashMap接口特点

与HashMap的异同:同样是基于散列表实现,

区别是LinkedHashMap内部多了一个双向循环链表的维护,该链表是有序,可以按元素插入顺序或元素最近访问顺序(LRU)排列

简单地说:LinkedHashMap=散列表+循环双向链表,能够保持遍历的顺序和插入的顺序一致。

总结:

1.LinkedHashMap继承自HashMap,具有HashMap的大部分特性,
比如支持null键和值,默认容量为16,装载因子为0.75,

非线程安全等等;

改为同步:

* Map m = Collections.synchronizedMap(new
LinkedHashMap());

2.LinkedHashMap通过设置accessOrder控制遍历顺序是按照插入顺序还是按照访问顺序。

当accessOrder为true时,可以利用其完成LRU缓存的功能;

3.LinkedHashMap内部维护了一个双向循环链表,并且其迭代操作时通过链表完成的,而不是去遍历hash表。

4
增加了维护链接列表的开支,性能比HashMap稍逊;

三、内部结构

第一张图是LinkedHashMap的全部数据结构,包含散列表和循环双向链表;

左边的红色箭头引用为Entry节点对象的next引用(散列表中的单链表),

绿色线条为Entry节点对象的before,
after引用(循环双向链表的前后引用);

由图可知:LinkedHashMa在遍历的时候直接遍历双向循环链;

第二张图专门把循环双向链表抽取出来,直观一点,注意该循环双向链表的头部存放的是最久访问的节点或最先插入的节点,尾部为最近访问的或最近插入的节点,迭代器遍历方向是从链表的头部开始到链表尾部结束,在链表尾部有一个空的header节点,该节点不存放key-value内容,为LinkedHashMap类的成员属性,循环双向链表的入口;

四、源码分析

1 与HashMap不同之处的两个重要属性。

LinkedHashMap比HashMap多了两个成员变量,其中header代表内部双向链表的头结点,后面我们就会发现,LinkedHashMap除了有个桶数组

容纳所有Entry之外,还有一个双向链表保存所有Entry引用。遍历的时候,并不是去遍历桶数组,而是直接遍历双向链表,所以LinkedHashMap的遍历

时间不受桶容量的限制,这是它和HashMap的重要区别之一。

而这个accessOrder代表的是是否按照访问顺序,true代表是,默认是插入顺序。所以我们可以将accessOrder置为true来实现LRU算法,

这可以用来做缓存。

            //双向循环链表的头结点
	    private transient Entry<K,V> header;
	    //accessOrder代表链表的排序方式;true为按照访问顺序,false为插入顺序。
	    private final boolean accessOrder;

2  构造器

           //按指定的初始化容量和加载因子,生成一个空的LinkedHashMap,
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(int initialCapacity, float loadFactor) {
	        super(initialCapacity, loadFactor);
	        accessOrder = false;//默认设为插入顺序
	    }
	    //按指定初始化容量,生成一个LinkedHashMap构造器;默认加载因子为0.75
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(int initialCapacity) {
	        super(initialCapacity);
	        accessOrder = false;//默认设为插入顺序
	    }
	    //生成一个LinkedHashMap构造器;默认加载因子为0.75,初始化容量为16
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap() {
	        super();
	        accessOrder = false;
	    }

	    //根据指定的map生成一个新的HashMap,负载因子使用默认值,初始容量大小为Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY)
	     //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(Map<? extends K, ? extends V> m) {
	        super(m);
	        accessOrder = false;
	    }
	    //按指定初始化容量,加载因子,链表的排序方式生成一个LinkedHashMap构造器
	    public LinkedHashMap(int initialCapacity,
	                         float loadFactor,
	                         boolean accessOrder) {
	        super(initialCapacity, loadFactor);
	        this.accessOrder = accessOrder;
	    }

构造器首先都会调用父类也就是HashMap的构造器来初始化桶数组,而accessOrder之后会被初始化,   
除了最后面的一个构造器允许指定accessOrder外,其他构造器都 默认将accessOrder置为了false 。

读者可能很奇怪,不是还有个header么,这个双向链表为啥不在构造器中初始化呢?这得回到HashMap中查看hashMap的构造器了:

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
       ... ...
        init();
    }

HashMap构造器最后一步调用了一个init方法,而这个init方法在HashMap中是个空实现,没有任何代码。

这其实就是所谓的“钩子”,具体代码由子类实现,如果子类希望每次构造的时候都去做一些特定的初始化操作,可以选择复写init方法。

我们看到LinkedHashMap中确实复写了init:

 /**
	     * 覆盖HashMap的init方法,在构造方法、Clone、readObject方法里会调用该方法
	     * 作用是生成一个双向链表头节点,初始化其前后节点引用
	     */
	    @Override
	    void init() {
	        header = new Entry<>(-1, null, null, null);//初始化双向链表
	        header.before = header.after = header;//不光是双向链表,还是循环链表
	    }

在init方法中,果然初始化了双向链表,而且我们还发现,这不光是个双向链表,还是个循环链表。

3、Entry内部结构

HashMap内部的Entry类并没有before和after指针, 也就是说LinkedHashMap自己重写了一个Entry类 :

 //LinkedHashMap节点对象
	    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);
	        }

	        //移除此节点,并修改前后的引用
	        private void remove() {
	            before.after = after;
	            after.before = before;
	        }

	        //将当前结点(this)插入到existingEntry结点前
	        private void addBefore(Entry<K,V> existingEntry) {
	            after  = existingEntry;//先获取existingEntry的前后节点
	            before = existingEntry.before;
	            before.after = this;//双向指针,都需要修改
	            after.before = this;
	        }

	        /**
	         * 在父类HashMap中的get,set方法会调用此方法recordAccess;
	         * 在LinkedHashMap中,当按访问顺序排序时,该方法会将当前节点插入到链表尾部(头结点的前一个节点)
	         * 否则不做任何事
	         */
	        void recordAccess(HashMap<K,V> m) {
	            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
	            if (lm.accessOrder) {//当按访问顺序排序时
	                lm.modCount++;
	                remove();//移除当前节点
	                addBefore(lm.header);//将当前节点插入到头结点前面
	            }
	        }

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

这里的Entry选择继承父类的Entry类,也就是说
LinkedHashMap中的Entry拥有三个指针,          
除了前驱后继指针外用于双向链表的连接外,还有一个next指针用于解决hash冲突(引用链)。

除此之外,Entry新增了几个方法,remove和addbefore用来操作双向链表不用多说。而recordAccess方法比较特殊,

这个方法在HashMap中也是空实现,并在put方法中会调用此方法:

public V put(K key, V value) {//HashMap的put方法
		if (key == null)
			return putForNullKey(value);
		int hash = hash(key);
		int i = indexFor(hash, table.length);
		for (Entry<K,V> e = table[i]; e != null; e = e.next) {
			Object k;
			if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
				V oldValue = e.value;
				e.value = value;
				e.recordAccess(this);//发生覆盖操作时,会调用此方法
				return oldValue;
			}
		}
	  ... ...
	}

此外,在LinkedHashMap的get方法中,也会调用此方法:

//根据指定key返回value:先得到entry结点,再返回其值;调用的是HashMap的getEntry方法
	    //与HashMap的区别是:当LinkedHashMap按访问顺序排序的时候,会将访问的当前节点移到链表尾部(头结点的前一个节点)
	    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;
	    }

也就是说,只要涉及到访问结点,那么就会调用这个方法。 观察该方法的逻辑:

如果accessOrder为true,

那么会调用addBefore方法将当前Entry放到双向链表的尾部,最终在我们遍历链表的时候就会发现最近最少使用的结点的都集中在

链表头部(
从近期访问最少到近期访问最多的顺序 ),这就是LRU。

LinkedHashMap并没有复写put方法,但是却复写了addEntry和createEntry方法,之前分析HashMap的时候我们就知道了,

put方法会调用addEntry将键值对挂到桶的某个合适位置,而addEntry又会调用createEntry方法创建一个键值对对象。

因而,LinkedHashMap其实是间接更改了put方法,想想也很容易理解,LinkedHashMap除了要向桶中添加键值对外,

还需向链表中增加键值对,所以必须得修改put方法。

 /**
	     * 创建节点,插入到LinkedHashMap,该方法覆盖HashMap的addEntry方法
	     */
	    void addEntry(int hash, K key, V value, int bucketIndex) {
	        super.addEntry(hash, key, value, bucketIndex);

	        // eldest:头结点的下个节点header.after,存放于链表头部,是最不经常访问或第一个插入的节点,
	        Entry<K,V> eldest = header.after;
	        //有必要的情况下(如容量不够,具体看removeEldestEntry方法的实现,这里默认为false,不删除),可以先删除
	        if (removeEldestEntry(eldest)) {
	            removeEntryForKey(eldest.key);
	        }
	    }

	    /**
	     * 创建节点,并将该节点插入到链表尾部
	     */
	    void createEntry(int hash, K key, V value, int bucketIndex) {
	        HashMap.Entry<K,V> old = table[bucketIndex];
	        Entry<K,V> e = new Entry<>(hash, key, value, old);
	        table[bucketIndex] = e;
	        e.addBefore(header);//节点插入到链表尾部
	        size++;
	    }

createEntry方法会将键值对分别挂到桶数组和双向链表中。

比较有意思的是addEntry方法,它提供了一个可选的操作,我们可以通过继承LinkedHashMap并复写removeEldestEntry方法让

该子类可以自动地删除最近最少访问的键值对——这可以用来做缓存!!

4 迭代器

LinkedHashMap自定义了迭代器以及迭代规则,LinkedHashMap是通过内部的双向链表来完成迭代的,遍历时间与键值对总数 成正比,而HashMap遍历时间与容量成正比,所以通常情况下,LinkedHashMap遍历性能是优于HashMap的,但是因为需要额外

维护链表,所以折中来看,两者性能相差无几。

 //迭代器
	    private abstract class LinkedHashIterator<T> implements Iterator<T> {
	        Entry<K,V> nextEntry    = header.after;
	        Entry<K,V> lastReturned = null;

	        //用于迭代期间快速失败行为
	        int expectedModCount = modCount;

	        //判断是否还有下个节点;当为头结点的时候说明没有下个节点即返回false
	        public boolean hasNext() {
	            return nextEntry != header;
	        }
	        //移除当前访问的节点
	        public void remove() {
	            if (lastReturned == null)
	                throw new IllegalStateException();
	            if (modCount != expectedModCount)
	                throw new ConcurrentModificationException();

	            LinkedHashMap.this.remove(lastReturned.key);
	            lastReturned = null;
	            expectedModCount = modCount;
	        }

	        Entry<K,V> nextEntry() {
	            if (modCount != expectedModCount)
	                throw new ConcurrentModificationException();
	            if (nextEntry == header)
	                throw new NoSuchElementException();

	            Entry<K,V> e = lastReturned = nextEntry;
	            nextEntry = e.after;
	            return e;
	        }
	    }

贴源码:

/**
	 * LinkedHashMap接口是的Map接口哈希表和链接列表的实现,
	 * 具有可预知的迭代顺序。这个实现类与HashMap不同之处在于其所有结点都之间以双链表进行维护。
	 * 此链表定义了迭代顺序,此顺序通常就是按键插入到映射中的顺序(插入顺序)。
	 * 注意:如果在映射中重新插入键,则插入顺序不受影响。
	 * 允许null元素;
	 * 增加了维护链接列表的开支,性能比HashMap稍逊;
	 * 此实现不是同步的,改为同步:
	 * 		Map m = Collections.synchronizedMap(new LinkedHashMap());
	 *
	 */

	public class LinkedHashMap<K,V>
						extends HashMap<K,V>
	    				implements Map<K,V>
	{
	    private static final long serialVersionUID = 3801124242820219131L;

	    //双向循环链表的头结点
	    private transient Entry<K,V> header;
	    //accessOrder代表链表的排序方式;true为按照访问顺序,false为插入顺序。
	    private final boolean accessOrder;

	    //按指定的初始化容量和加载因子,生成一个空的LinkedHashMap,
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(int initialCapacity, float loadFactor) {
	        super(initialCapacity, loadFactor);
	        accessOrder = false;//默认设为插入顺序
	    }
	    //按指定初始化容量,生成一个LinkedHashMap构造器;默认加载因子为0.75
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(int initialCapacity) {
	        super(initialCapacity);
	        accessOrder = false;//默认设为插入顺序
	    }
	    //生成一个LinkedHashMap构造器;默认加载因子为0.75,初始化容量为16
	  //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap() {
	        super();
	        accessOrder = false;
	    }

	    //根据指定的map生成一个新的HashMap,负载因子使用默认值,初始容量大小为Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY)
	     //默认将accessOrder设为false,按插入顺序排序.
	    public LinkedHashMap(Map<? extends K, ? extends V> m) {
	        super(m);
	        accessOrder = false;
	    }
	    //按指定初始化容量,加载因子,链表的排序方式生成一个LinkedHashMap构造器
	    public LinkedHashMap(int initialCapacity,
	                         float loadFactor,
	                         boolean accessOrder) {
	        super(initialCapacity, loadFactor);
	        this.accessOrder = accessOrder;
	    }

	    /**
	     * 覆盖HashMap的init方法,在构造方法、Clone、readObject方法里会调用该方法
	     * 作用是生成一个双向链表头节点,初始化其前后节点引用
	     */
	    @Override
	    void init() {
	        header = new Entry<>(-1, null, null, null);//初始化双向链表
	        header.before = header.after = header;//不光是双向链表,还是循环链表
	    }

	    /**
	     * 覆盖HashMap的transfer方法,性能优化,这里遍历方式不采用HashMap的双重循环方式
	     * 而是直接通过双向链表遍历Map中的所有key-value映射
	     */
	    @Override
	    void transfer(HashMap.Entry[] newTable, boolean rehash) {
	        int newCapacity = newTable.length;//获取新表大小
	        //遍历旧Map中的所有key-value
	        for (Entry<K,V> e = header.after; e != header; e = e.after) {
	            //看是否需要进行再哈希操作,需要的话就重新计算key的哈希值
	        	if (rehash)
	                e.hash = (e.key == null) ? 0 : hash(e.key);
	            int index = indexFor(e.hash, newCapacity);//根据数组长度重新计算索引
	            e.next = newTable[index];//插入到链表表头
	            newTable[index] = e;//将e放到索引为i的数组处
	        }
	    }

	    //判断是否含有值value
	    //与HashMap不同地方在于是直接遍历链表进行查询获取,而不用计算key的hash值去寻找桶再遍历
	    public boolean containsValue(Object value) {
	        // Overridden to take advantage of faster iterator
	        if (value==null) {
	            for (Entry e = header.after; e != header; e = e.after)
	                if (e.value==null)
	                    return true;
	        } else {
	            for (Entry e = header.after; e != header; e = e.after)
	                if (value.equals(e.value))
	                    return true;
	        }
	        return false;
	    }

	    //根据指定key返回value:先得到entry结点,再返回其值;调用的是HashMap的getEntry方法
	    //与HashMap的区别是:当LinkedHashMap按访问顺序排序的时候,会将访问的当前节点移到链表尾部(头结点的前一个节点)
	    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;
	    }

	    //清空链表:调用的父类clear方法
	    public void clear() {
	        super.clear();
	        header.before = header.after = header;
	    }

	    //LinkedHashMap节点对象
	    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);
	        }

	        //移除此节点,并修改前后的引用
	        private void remove() {
	            before.after = after;
	            after.before = before;
	        }

	        //将当前结点(this)插入到existingEntry结点前
	        private void addBefore(Entry<K,V> existingEntry) {
	            after  = existingEntry;//先获取existingEntry的前后节点
	            before = existingEntry.before;
	            before.after = this;//双向指针,都需要修改
	            after.before = this;
	        }

	        /**
	         * 在父类HashMap中的get,set方法会调用此方法recordAccess;
	         * 在LinkedHashMap中,当按访问顺序排序时,该方法会将当前节点插入到链表尾部(头结点的前一个节点)
	         * 否则不做任何事
	         */
	        void recordAccess(HashMap<K,V> m) {
	            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
	            if (lm.accessOrder) {//当按访问顺序排序时
	                lm.modCount++;
	                remove();//移除当前节点
	                addBefore(lm.header);//将当前节点插入到头结点前面
	            }
	        }

	        void recordRemoval(HashMap<K,V> m) {
	            remove();
	        }
	    }
	    //迭代器
	    private abstract class LinkedHashIterator<T> implements Iterator<T> {
	        Entry<K,V> nextEntry    = header.after;
	        Entry<K,V> lastReturned = null;

	        //用于迭代期间快速失败行为
	        int expectedModCount = modCount;

	        //判断是否还有下个节点;当为头结点的时候说明没有下个节点即返回false
	        public boolean hasNext() {
	            return nextEntry != header;
	        }
	        //移除当前访问的节点
	        public void remove() {
	            if (lastReturned == null)
	                throw new IllegalStateException();
	            if (modCount != expectedModCount)
	                throw new ConcurrentModificationException();

	            LinkedHashMap.this.remove(lastReturned.key);
	            lastReturned = null;
	            expectedModCount = modCount;
	        }

	        Entry<K,V> nextEntry() {
	            if (modCount != expectedModCount)
	                throw new ConcurrentModificationException();
	            if (nextEntry == header)
	                throw new NoSuchElementException();

	            Entry<K,V> e = lastReturned = nextEntry;
	            nextEntry = e.after;
	            return e;
	        }
	    }
	    //key迭代器
	    private class KeyIterator extends LinkedHashIterator<K> {
	        public K next() { return nextEntry().getKey(); }
	    }
	    //value迭代器
	    private class ValueIterator extends LinkedHashIterator<V> {
	        public V next() { return nextEntry().value; }
	    }
	    //key-value迭代器
	    private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
	        public Map.Entry<K,V> next() { return nextEntry(); }
	    }

	    // 返回不同的迭代器对象
	    Iterator<K> newKeyIterator()   { return new KeyIterator();   }
	    Iterator<V> newValueIterator() { return new ValueIterator(); }
	    Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }

	    /**
	     * 创建节点,插入到LinkedHashMap,该方法覆盖HashMap的addEntry方法
	     */
	    void addEntry(int hash, K key, V value, int bucketIndex) {
	        super.addEntry(hash, key, value, bucketIndex);

	        // eldest:头结点的下个节点header.after,存放于链表头部,是最不经常访问或第一个插入的节点,
	        Entry<K,V> eldest = header.after;
	        //有必要的情况下(如容量不够,具体看removeEldestEntry方法的实现,这里默认为false,不删除),可以先删除
	        if (removeEldestEntry(eldest)) {
	            removeEntryForKey(eldest.key);
	        }
	    }

	    /**
	     * 创建节点,并将该节点插入到链表尾部
	     */
	    void createEntry(int hash, K key, V value, int bucketIndex) {
	        HashMap.Entry<K,V> old = table[bucketIndex];
	        Entry<K,V> e = new Entry<>(hash, key, value, old);
	        table[bucketIndex] = e;
	        e.addBefore(header);//节点插入到链表尾部
	        size++;
	    }

	    /**
	     * 该方法在创建新节点的时候调用,
	     * 判断是否有必要删除链表头部的第一个节点(最不经常访问或最先插入的节点,由accessOrder决定)
	     */
	    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
	        return false;
	    }
	}

时间: 2024-10-19 05:03:47

【JDK源码阅读9-util】LinkedHashMap接口的相关文章

JDK 源码 阅读 - 2 - 设计模式 - 创建型模式

A.创建型模式 抽象工厂(Abstract Factory) javax.xml.parsers.DocumentBuilderFactory DocumentBuilderFactory通过FactoryFinder实例化具体的Factory. 使用例子: DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilder

JDK源码阅读(三):ArraryList源码解析

今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 一般来讲文章开始应该先介绍一下说下简介.这里就不介绍了 如果你不知道ArrayList是什么的话就没必要在看了.大致讲一下一些常用的方法 2.继承结构 ArrayList源码定义: ArrayList继承结构如下: Serializable 序列化接口 Cloneable 前面我们在看Object源

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *

jdk源码阅读-HashMap

前置阅读: jdk源码阅读-Map : http://www.cnblogs.com/ccode/p/4645683.html 在前置阅读的文章里,已经提到HashMap是基于Hash表实现的,所以在讲解HashMap之前 ,有必要提前了解下Hash的原理. 参考<算法导论><算法>

JDK源码阅读(一):Object源码分析

最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 hashCode 方法 toString 方法 finalize 方法 registerNatives 方法 1. Object结构图 2. 类构造器 ??类构造器是创建Java对象的方法之一.一般我们都使用new关键字来进行实例,还可以在构造器中进行相应的初始化操作. ??在一个Java类中必须存在一个

JDK源码笔记-java.util.HashMap

HashMap 的存储实现 当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例: Java代码 HashMap<String , Double> map = new HashMap<String , Double>(); map.put("语文" , 80.0); map.put("数学" , 89.0); map.put("英语" , 78.2); HashMap 采用一种所谓的&quo

jdk 源码阅读有感(一)String

闲暇之余阅读 jdk 源码. (一)核心属性 /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 String的核心结构,char型数组与 int 型 hash值. (二)构造器 构造器方面,由于上述两个值是不可更改的,所以直接 对 String

jdk源码阅读笔记之java集合框架(一)(基础篇)

结合<jdk源码>与<thinking in java>,对java集合框架做一些简要分析(本着实用主义,精简主义,遂只会挑出个人认为是高潮的部分). 先上一张java集合框架的简图: 会从以下几个方面来进行分析: java 数组; ArrayList,LinkedList与Vector; HashMap; HashSet 关于数组array: 数组的解释是:存储固定大小的同类型元素.由于是"固定大小",所以对于未知数目的元素存储就显得力不从心,遂有了集合.

JDK源码阅读环境搭建

内容来源 B站Up主: CodeSheep 视频: https://www.bilibili.com/video/BV1V7411U78L 感谢大佬分享学习心得 Thanks?(?ω?)?~~~ 1. 新建项目 新建JavaSourceLearn项目 新建source包存放源码 新建test包存放测试案例 2. 获取JDK源码 打开Project Structure 选择JDK版本查看安装目录 将src.zip解压到项目source包中 提示: 添加源码到项目之后首次运行需要较长时间进行编译,建