最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java集合框架源碼分析之LinkedHashMap詳解

 更新時間:2017年09月25日 16:50:30   作者:BridgeGeorge  
這篇文章主要介紹了Java集合框架源碼分析之LinkedHashMap詳解,內容包括了linkedhashmap的簡介和源碼剖析以及關于LinkedHashMap的源碼總結,內容豐富,需要的朋友可以參考下。

LinkedHashMap簡介

LinkedHashMap是HashMap的子類,與HashMap有著同樣的存儲結構,但它加入了一個雙向鏈表的頭結點,將所有put到LinkedHashmap的節(jié)點一一串成了一個雙向循環(huán)鏈表,因此它保留了節(jié)點插入的順序,可以使節(jié)點的輸出順序與輸入順序相同。

LinkedHashMap可以用來實現(xiàn)LRU算法(這會在下面的源碼中進行分析)。

LinkedHashMap同樣是非線程安全的,只在單線程環(huán)境下使用。

LinkedHashMap源碼剖析

LinkedHashMap源碼如下(加入了詳細的注釋):

package java.util; 
import java.io.*; 
public class LinkedHashMap<K,V> 
  extends HashMap<K,V> 
  implements Map<K,V> 
{ 
  private static final long serialVersionUID = 3801124242820219131L; 
  //雙向循環(huán)鏈表的頭結點,整個LinkedHashMap中只有一個header, 
  //它將哈希表中所有的Entry貫穿起來,header中不保存key-value對,只保存前后節(jié)點的引用 
  private transient Entry<K,V> header; 
  //雙向鏈表中元素排序規(guī)則的標志位。 
  //accessOrder為false,表示按插入順序排序 
  //accessOrder為true,表示按訪問順序排序 
  private final boolean accessOrder; 
  //調用HashMap的構造方法來構造底層的數(shù)組 
  public LinkedHashMap(int initialCapacity, float loadFactor) { 
    super(initialCapacity, loadFactor); 
    accessOrder = false;  //鏈表中的元素默認按照插入順序排序 
  } 
  //加載因子取默認的0.75f 
  public LinkedHashMap(int initialCapacity) { 
    super(initialCapacity); 
    accessOrder = false; 
  } 
  //加載因子取默認的0.75f,容量取默認的16 
  public LinkedHashMap() { 
    super(); 
    accessOrder = false; 
  } 
  //含有子Map的構造方法,同樣調用HashMap的對應的構造方法 
  public LinkedHashMap(Map<? extends K, ? extends V> m) { 
    super(m); 
    accessOrder = false; 
  } 
  //該構造方法可以指定鏈表中的元素排序的規(guī)則 
  public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) { 
    super(initialCapacity, loadFactor); 
    this.accessOrder = accessOrder; 
  } 
  //覆寫父類的init()方法(HashMap中的init方法為空), 
  //該方法在父類的構造方法和Clone、readObject中在插入元素前被調用, 
  //初始化一個空的雙向循環(huán)鏈表,頭結點中不保存數(shù)據(jù),頭結點的下一個節(jié)點才開始保存數(shù)據(jù)。 
  void init() { 
    header = new Entry<K,V>(-1, null, null, null); 
    header.before = header.after = header; 
  } 
  //覆寫HashMap中的transfer方法,它在父類的resize方法中被調用, 
  //擴容后,將key-value對重新映射到新的newTable中 
  //覆寫該方法的目的是為了提高復制的效率, 
  //這里充分利用雙向循環(huán)鏈表的特點進行迭代,不用對底層的數(shù)組進行for循環(huán)。 
  void transfer(HashMap.Entry[] newTable) { 
    int newCapacity = newTable.length; 
    for (Entry<K,V> e = header.after; e != header; e = e.after) { 
      int index = indexFor(e.hash, newCapacity); 
      e.next = newTable[index]; 
      newTable[index] = e; 
    } 
  } 
  //覆寫HashMap中的containsValue方法, 
  //覆寫該方法的目的同樣是為了提高查詢的效率, 
  //利用雙向循環(huán)鏈表的特點進行查詢,少了對數(shù)組的外層for循環(huán) 
  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; 
  } 
  //覆寫HashMap中的get方法,通過getEntry方法獲取Entry對象。 
  //注意這里的recordAccess方法, 
  //如果鏈表中元素的排序規(guī)則是按照插入的先后順序排序的話,該方法什么也不做, 
  //如果鏈表中元素的排序規(guī)則是按照訪問的先后順序排序的話,則將e移到鏈表的末尾處。 
  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; 
  } 
  //清空HashMap,并將雙向鏈表還原為只有頭結點的空鏈表 
  public void clear() { 
    super.clear(); 
    header.before = header.after = header; 
  } 
  //Enty的數(shù)據(jù)結構,多了兩個指向前后節(jié)點的引用 
  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); 
    } 
    //雙向循環(huán)鏈表中,刪除當前的Entry 
    private void remove() { 
      before.after = after; 
      after.before = before; 
    } 
    //雙向循環(huán)立鏈表中,將當前的Entry插入到existingEntry的前面 
    private void addBefore(Entry<K,V> existingEntry) { 
      after = existingEntry; 
      before = existingEntry.before; 
      before.after = this; 
      after.before = this; 
    } 
    //覆寫HashMap中的recordAccess方法(HashMap中該方法為空), 
    //當調用父類的put方法,在發(fā)現(xiàn)插入的key已經(jīng)存在時,會調用該方法, 
    //調用LinkedHashmap覆寫的get方法時,也會調用到該方法, 
    //該方法提供了LRU算法的實現(xiàn),它將最近使用的Entry放到雙向循環(huán)鏈表的尾部, 
    //accessOrder為true時,get方法會調用recordAccess方法 
    //put方法在覆蓋key-value對時也會調用recordAccess方法 
    //它們導致Entry最近使用,因此將其移到雙向鏈表的末尾 
    void recordAccess(HashMap<K,V> m) { 
      LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; 
      //如果鏈表中元素按照訪問順序排序,則將當前訪問的Entry移到雙向循環(huán)鏈表的尾部, 
      //如果是按照插入的先后順序排序,則不做任何事情。 
      if (lm.accessOrder) { 
        lm.modCount++; 
        //移除當前訪問的Entry 
        remove(); 
        //將當前訪問的Entry插入到鏈表的尾部 
        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; 
  /** 
   * The modCount value that the iterator believes that the backing 
   * List should have. If this expectation is violated, the iterator 
   * has detected concurrent modification. 
   */ 
  int expectedModCount = modCount; 
  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; 
  } 
  //從head的下一個節(jié)點開始迭代 
  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; } 
  } 
  //Entry迭代器 
  private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> { 
  public Map.Entry<K,V> next() { return nextEntry(); } 
  } 
  // These Overrides alter the behavior of superclass view iterator() methods 
  Iterator<K> newKeyIterator()  { return new KeyIterator();  } 
  Iterator<V> newValueIterator() { return new ValueIterator(); } 
  Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } 
  //覆寫HashMap中的addEntry方法,LinkedHashmap并沒有覆寫HashMap中的put方法, 
  //而是覆寫了put方法所調用的addEntry方法和recordAccess方法, 
  //put方法在插入的key已存在的情況下,會調用recordAccess方法, 
  //在插入的key不存在的情況下,要調用addEntry插入新的Entry 
  void addEntry(int hash, K key, V value, int bucketIndex) { 
    //創(chuàng)建新的Entry,并插入到LinkedHashMap中 
    createEntry(hash, key, value, bucketIndex); 
    //雙向鏈表的第一個有效節(jié)點(header后的那個節(jié)點)為近期最少使用的節(jié)點 
    Entry<K,V> eldest = header.after; 
    //如果有必要,則刪除掉該近期最少使用的節(jié)點, 
    //這要看對removeEldestEntry的覆寫,由于默認為false,因此默認是不做任何處理的。 
    if (removeEldestEntry(eldest)) { 
      removeEntryForKey(eldest.key); 
    } else { 
      //擴容到原來的2倍 
      if (size >= threshold) 
        resize(2 * table.length); 
    } 
  } 
  void createEntry(int hash, K key, V value, int bucketIndex) { 
    //創(chuàng)建新的Entry,并將其插入到數(shù)組對應槽的單鏈表的頭結點處,這點與HashMap中相同 
    HashMap.Entry<K,V> old = table[bucketIndex]; 
    Entry<K,V> e = new Entry<K,V>(hash, key, value, old); 
    table[bucketIndex] = e; 
    //每次插入Entry時,都將其移到雙向鏈表的尾部, 
    //這便會按照Entry插入LinkedHashMap的先后順序來迭代元素, 
    //同時,新put進來的Entry是最近訪問的Entry,把其放在鏈表末尾 ,符合LRU算法的實現(xiàn) 
    e.addBefore(header); 
    size++; 
  } 
  //該方法是用來被覆寫的,一般如果用LinkedHashmap實現(xiàn)LRU算法,就要覆寫該方法, 
  //比如可以將該方法覆寫為如果設定的內存已滿,則返回true,這樣當再次向LinkedHashMap中put 
  //Entry時,在調用的addEntry方法中便會將近期最少使用的節(jié)點刪除掉(header后的那個節(jié)點)。 
  protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { 
    return false; 
  } 
} 

總結

關于LinkedHashMap的源碼,給出以下幾點比較重要的總結:

1、從源碼中可以看出,LinkedHashMap中加入了一個head頭結點,將所有插入到該LinkedHashMap中的Entry按照插入的先后順序依次加入到以head為頭結點的雙向循環(huán)鏈表的尾部。

1、實際上就是HashMap和LinkedList兩個集合類的存儲結構的結合。在LinkedHashMapMap中,所有put進來的Entry都保存在哈希表中,但它又額外定義了一個以head為頭結點的空的雙向循環(huán)鏈表,每次put進來Entry,除了將其保存到對哈希表中對應的位置上外,還要將其插入到雙向循環(huán)鏈表的尾部。

2、LinkedHashMap由于繼承自HashMap,因此它具有HashMap的所有特性,同樣允許key和value為null。

3、注意源碼中的accessOrder標志位,當它false時,表示雙向鏈表中的元素按照Entry插入LinkedHashMap到中的先后順序排序,即每次put到LinkedHashMap中的Entry都放在雙向鏈表的尾部,這樣遍歷雙向鏈表時,Entry的輸出順序便和插入的順序一致,這也是默認的雙向鏈表的存儲順序;當它為true時,表示雙向鏈表中的元素按照訪問的先后順序排列,可以看到,雖然Entry插入鏈表的順序依然是按照其put到LinkedHashMap中的順序,但put和get方法均有調用recordAccess方法(put方法在key相同,覆蓋原有的Entry的情況下調用recordAccess方法),該方法判斷accessOrder是否為true,如果是,則將當前訪問的Entry(put進來的Entry或get出來的Entry)移到雙向鏈表的尾部(key不相同時,put新Entry時,會調用addEntry,它會調用creatEntry,該方法同樣將新插入的元素放入到雙向鏈表的尾部,既符合插入的先后順序,又符合訪問的先后順序,因為這時該Entry也被訪問了),否則,什么也不做。

4、注意構造方法,前四個構造方法都將accessOrder設為false,說明默認是按照插入順序排序的,而第五個構造方法可以自定義傳入的accessOrder的值,因此可以指定雙向循環(huán)鏈表中元素的排序規(guī)則,一般要用LinkedHashMap實現(xiàn)LRU算法,就要用該構造方法,將accessOrder置為true。

5、LinkedHashMap并沒有覆寫HashMap中的put方法,而是覆寫了put方法中調用的addEntry方法和recordAccess方法,我們回過頭來再看下HashMap的put方法:

// 將“key-value”添加到HashMap中   
public V put(K key, V value) {   
  // 若“key為null”,則將該鍵值對添加到table[0]中。   
  if (key == null)   
    return putForNullKey(value);   
  // 若“key不為null”,則計算該key的哈希值,然后將其添加到該哈希值對應的鏈表中。   
  int hash = hash(key.hashCode());   
  int i = indexFor(hash, table.length);   
  for (Entry<K,V> e = table[i]; e != null; e = e.next) {   
    Object k;   
    // 若“該key”對應的鍵值對已經(jīng)存在,則用新的value取代舊的value。然后退出!   
    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {   
      V oldValue = e.value;   
      e.value = value;   
      e.recordAccess(this);   
      return oldValue;   
    }   
  }   
  // 若“該key”對應的鍵值對不存在,則將“key-value”添加到table中   
  modCount++;  
  //將key-value添加到table[i]處  
  addEntry(hash, key, value, i);   
  return null;   
} 

當要put進來的Entry的key在哈希表中已經(jīng)在存在時,會調用recordAccess方法,當該key不存在時,則會調用addEntry方法將新的Entry插入到對應槽的單鏈表的頭部。

我們先來看recordAccess方法:

//覆寫HashMap中的recordAccess方法(HashMap中該方法為空), 
//當調用父類的put方法,在發(fā)現(xiàn)插入的key已經(jīng)存在時,會調用該方法, 
//調用LinkedHashmap覆寫的get方法時,也會調用到該方法, 
//該方法提供了LRU算法的實現(xiàn),它將最近使用的Entry放到雙向循環(huán)鏈表的尾部, 
//accessOrder為true時,get方法會調用recordAccess方法 
//put方法在覆蓋key-value對時也會調用recordAccess方法 
//它們導致Entry最近使用,因此將其移到雙向鏈表的末尾 
   void recordAccess(HashMap<K,V> m) { 
     LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; 
  //如果鏈表中元素按照訪問順序排序,則將當前訪問的Entry移到雙向循環(huán)鏈表的尾部, 
  //如果是按照插入的先后順序排序,則不做任何事情。 
     if (lm.accessOrder) { 
       lm.modCount++; 
    //移除當前訪問的Entry 
       remove(); 
    //將當前訪問的Entry插入到鏈表的尾部 
       addBefore(lm.header); 
     } 
   } 

該方法會判斷accessOrder是否為true,如果為true,它會將當前訪問的Entry(在這里指put進來的Entry)移動到雙向循環(huán)鏈表的尾部,從而實現(xiàn)雙向鏈表中的元素按照訪問順序來排序(最近訪問的Entry放到鏈表的最后,這樣多次下來,前面就是最近沒有被訪問的元素,在實現(xiàn)、LRU算法時,當雙向鏈表中的節(jié)點數(shù)達到最大值時,將前面的元素刪去即可,因為前面的元素是最近最少使用的),否則什么也不做。

再來看addEntry方法:

//覆寫HashMap中的addEntry方法,LinkedHashmap并沒有覆寫HashMap中的put方法, 
//而是覆寫了put方法所調用的addEntry方法和recordAccess方法, 
//put方法在插入的key已存在的情況下,會調用recordAccess方法, 
//在插入的key不存在的情況下,要調用addEntry插入新的Entry 
  void addEntry(int hash, K key, V value, int bucketIndex) { 
  //創(chuàng)建新的Entry,并插入到LinkedHashMap中 
    createEntry(hash, key, value, bucketIndex); 
    //雙向鏈表的第一個有效節(jié)點(header后的那個節(jié)點)為近期最少使用的節(jié)點 
    Entry<K,V> eldest = header.after; 
  //如果有必要,則刪除掉該近期最少使用的節(jié)點, 
  //這要看對removeEldestEntry的覆寫,由于默認為false,因此默認是不做任何處理的。 
    if (removeEldestEntry(eldest)) { 
      removeEntryForKey(eldest.key); 
    } else { 
    //擴容到原來的2倍 
      if (size >= threshold) 
        resize(2 * table.length); 
    } 
  } 
  void createEntry(int hash, K key, V value, int bucketIndex) { 
  //創(chuàng)建新的Entry,并將其插入到數(shù)組對應槽的單鏈表的頭結點處,這點與HashMap中相同 
    HashMap.Entry<K,V> old = table[bucketIndex]; 
  Entry<K,V> e = new Entry<K,V>(hash, key, value, old); 
    table[bucketIndex] = e; 
  //每次插入Entry時,都將其移到雙向鏈表的尾部, 
  //這便會按照Entry插入LinkedHashMap的先后順序來迭代元素, 
  //同時,新put進來的Entry是最近訪問的Entry,把其放在鏈表末尾 ,符合LRU算法的實現(xiàn) 
    e.addBefore(header); 
    size++; 
  } 

同樣是將新的Entry插入到table中對應槽所對應單鏈表的頭結點中,但可以看出,在createEntry中,同樣把新put進來的Entry插入到了雙向鏈表的尾部,從插入順序的層面來說,新的Entry插入到雙向鏈表的尾部,可以實現(xiàn)按照插入的先后順序來迭代Entry,而從訪問順序的層面來說,新put進來的Entry又是最近訪問的Entry,也應該將其放在雙向鏈表的尾部。

上面還有個removeEldestEntry方法,該方法如下:

 //該方法是用來被覆寫的,一般如果用LinkedHashmap實現(xiàn)LRU算法,就要覆寫該方法, 
  //比如可以將該方法覆寫為如果設定的內存已滿,則返回true,這樣當再次向LinkedHashMap中put 
  //Entry時,在調用的addEntry方法中便會將近期最少使用的節(jié)點刪除掉(header后的那個節(jié)點)。 
  protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { 
    return false; 
  } 
} 

該方法默認返回false,我們一般在用LinkedHashMap實現(xiàn)LRU算法時,要覆寫該方法,一般的實現(xiàn)是,當設定的內存(這里指節(jié)點個數(shù))達到最大值時,返回true,這樣put新的Entry(該Entry的key在哈希表中沒有已經(jīng)存在)時,就會調用removeEntryForKey方法,將最近最少使用的節(jié)點刪除(head后面的那個節(jié)點,實際上是最近沒有使用)。

6、LinkedHashMap覆寫了HashMap的get方法:

//覆寫HashMap中的get方法,通過getEntry方法獲取Entry對象。 
//注意這里的recordAccess方法, 
//如果鏈表中元素的排序規(guī)則是按照插入的先后順序排序的話,該方法什么也不做, 
//如果鏈表中元素的排序規(guī)則是按照訪問的先后順序排序的話,則將e移到鏈表的末尾處。 
  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; 
  } 

先取得Entry,如果不為null,一樣調用recordAccess方法,上面已經(jīng)說得很清楚,這里不在多解釋了。

7、最后說說LinkedHashMap是如何實現(xiàn)LRU的。

首先,當accessOrder為true時,才會開啟按訪問順序排序的模式,才能用來實現(xiàn)LRU算法。我們可以看到,無論是put方法還是get方法,都會導致目標Entry成為最近訪問的Entry,因此便把該Entry加入到了雙向鏈表的末尾(get方法通過調用recordAccess方法來實現(xiàn),put方法在覆蓋已有key的情況下,也是通過調用recordAccess方法來實現(xiàn),在插入新的Entry時,則是通過createEntry中的addBefore方法來實現(xiàn)),這樣便把最近使用了的Entry放入到了雙向鏈表的后面,多次操作后,雙向鏈表前面的Entry便是最近沒有使用的,這樣當節(jié)點個數(shù)滿的時候,刪除的最前面的Entry(head后面的那個Entry)便是最近最少使用的Entry。

結束語

以上就是本文關于Java集合框架源碼分析之LinkedHashMap詳解的全部內容,希望對大家學習Java能夠有所幫助。歡迎大家參閱本站其他專題,感謝大家讀腳本之家的支持!

相關文章

  • springboot排除某些自動配置的操作方法

    springboot排除某些自動配置的操作方法

    Spring Boot 提供的自動配置非常強大,某些情況下,自動配置的功能可能不符合我們的需求,需要我們自定義配置,這個時候就需要排除/禁用Spring Boot 某些類的自動化配置了,本文給大家介紹springboot排除某些自動配置的方法,感興趣的朋友一起看看吧
    2023-08-08
  • java 實現(xiàn)微信服務器下載圖片到自己服務器

    java 實現(xiàn)微信服務器下載圖片到自己服務器

    這篇文章主要介紹了 java 實現(xiàn)微信服務器下載圖片到自己服務器的相關資料,需要的朋友可以參考下
    2017-05-05
  • idea導入項目不顯示maven側邊欄的問題及解決方法

    idea導入項目不顯示maven側邊欄的問題及解決方法

    這篇文章主要介紹了idea導入項目不顯示maven側邊欄的問題及解決方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • Java高效實現(xiàn)電商產(chǎn)品排序實戰(zhàn)

    Java高效實現(xiàn)電商產(chǎn)品排序實戰(zhàn)

    這篇文章主要為大家介紹了Java高效實現(xiàn)電商產(chǎn)品排序實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • SpringBoot可視化接口開發(fā)工具magic-api的簡單使用教程

    SpringBoot可視化接口開發(fā)工具magic-api的簡單使用教程

    作為Java后端開發(fā),平時開發(fā)API接口的時候經(jīng)常需要定義Controller、Service、Dao、Mapper、XML、VO等Java對象。有沒有什么辦法可以讓我們不寫這些代碼,直接操作數(shù)據(jù)庫生成API接口呢?今天給大家推薦一款工具magic-api,來幫我們實現(xiàn)這個小目標!
    2021-06-06
  • SpringBoot搭建go-cqhttp機器人的方法實現(xiàn)

    SpringBoot搭建go-cqhttp機器人的方法實現(xiàn)

    本文主要介紹了SpringBoot搭建go-cqhttp機器人的方法實現(xiàn)
    2021-12-12
  • Spring詳細講解@Autowired注解

    Spring詳細講解@Autowired注解

    @Autowired注解可以用在類屬性,構造函數(shù),setter方法和函數(shù)參數(shù)上,該注解可以準確地控制bean在何處如何自動裝配的過程。在默認情況下,該注解是類型驅動的注入
    2022-06-06
  • 使用Java實現(xiàn)一個不同難度(高、中、低)的猜數(shù)字游戲

    使用Java實現(xiàn)一個不同難度(高、中、低)的猜數(shù)字游戲

    本文介紹了一個增強版的猜數(shù)字游戲,包括菜單打印、游戲維持、邏輯功能選擇和源代碼展示,游戲通過隨機數(shù)生成和邏輯判斷來維持游戲進程,用戶可以選擇不同的難度,源代碼展示了如何實現(xiàn)這三種不同難度的猜數(shù)字游戲,為玩家?guī)砀嗵魬?zhàn)和樂趣,需要的朋友可以參考下
    2024-09-09
  • Java操作pdf文件的方法大全

    Java操作pdf文件的方法大全

    這篇文章主要為大家詳細介紹了Java操作pdf文件的相關知識,例如合并pdf文件,手繪pdf文件以及導出PDF文件等,有需要的小伙伴可以參考一下
    2024-04-04
  • 快速解決SpringMVC @RequestBody 用map接收請求參數(shù)的問題

    快速解決SpringMVC @RequestBody 用map接收請求參數(shù)的問題

    今天小編就為大家分享快速解決SpringMVC @RequestBody 用map接收請求參數(shù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08

最新評論

威海市| 元氏县| 三河市| 蓝田县| 德化县| 黔西| 宜川县| 云阳县| 隆昌县| 金寨县| 西藏| 社旗县| 曲沃县| 江川县| 化州市| 和平区| 新乐市| 图木舒克市| 高要市| 景宁| 正定县| 阳东县| 本溪| 长葛市| 满城县| 蕉岭县| 榆社县| 广西| 绥滨县| 红安县| 晋宁县| 资溪县| 朝阳区| 涟源市| 吉隆县| 柳林县| 永康市| 闽清县| 休宁县| 仁寿县| 河池市|