亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java面試題沖刺第十一天--集合框架篇(2)

 更新時間:2021年07月14日 15:31:17   作者:_陳哈哈  
這篇文章主要為大家分享了最有價值的兩道集合框架的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下

面試題1:說一下 HashMap 的實現(xiàn)原理?

正經(jīng)回答:

眾所周知,HashMap是一個用于存儲Key-Value鍵值對的集合,每一個鍵值對也叫做Entry(包括Key-Value),其中Key 和 Value 允許為null。這些個鍵值對(Entry)分散存儲在一個數(shù)組當(dāng)中,這個數(shù)組就是HashMap的主干。另外,HashMap數(shù)組每一個元素的初始值都是Null。

在這里插入圖片描述

值得注意的是:HashMap不能保證映射的順序,插入后的數(shù)據(jù)順序也不能保證一直不變(如擴容后rehash)。

要說HashMap的原理,首先要先了解他的數(shù)據(jù)結(jié)構(gòu),

在這里插入圖片描述

如上圖為JDK1.8版本的數(shù)據(jù)結(jié)構(gòu),其實HashMap在JDK1.7及以前是一個“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組 + 鏈表的結(jié)合體。JDK8優(yōu)化為:數(shù)組+鏈表+紅黑樹。

我們常把數(shù)組中的每一個節(jié)點稱為一個桶。當(dāng)向桶中添加一個鍵值對時,首先計算鍵值對中key的hash值(hash(key)),以此確定插入數(shù)組中的位置(即哪個桶),但是可能存在同一hash值的元素已經(jīng)被放在數(shù)組同一位置了,這種現(xiàn)象稱為碰撞,這時按照尾插法(jdk1.7及以前為頭插法)的方式添加key-value到同一hash值的元素的最后面,鏈表就這樣形成了。

當(dāng)鏈表長度超過8(TREEIFY_THRESHOLD - 閾值)時,鏈表就自行轉(zhuǎn)為紅黑樹。

注意:同一hash值的元素指的是key內(nèi)容一樣么?不是。根據(jù)hash算法的計算方式,是將key值轉(zhuǎn)為一個32位的int值(近似取值),key值不同但key值相近的很可能hash值相同,如key=“a”和key=“aa”等。

通過上述回答的內(nèi)容,我們明顯給了面試官往深入問的多個誘餌,根據(jù)我們的回答,下一步他多可能會追問這些問題:

1、如何實現(xiàn)HashMap的有序? 4、put方法原理是怎么實現(xiàn)的? 6、擴容機制原理 → 初始容量、加載因子 → 擴容后的rehash(元素遷移) 2、插入后的數(shù)據(jù)順序會變的原因是什么? 3、HashMap在JDK1.7-JDK1.8都做了哪些優(yōu)化? 5、鏈表紅黑樹如何互相轉(zhuǎn)換?閾值多少? 7、頭插法改成尾插法為了解決什么問題?

而我們,當(dāng)然是提前準(zhǔn)備好如何回答好這些問題!當(dāng)你的回答超過面試同學(xué)的認(rèn)知范圍時,主動權(quán)就到我們手里了。

深入追問: 追問1:如何實現(xiàn)HashMap的有序?

使用LinkedHashMap 或 TreeMap。

LinkedHashMap內(nèi)部維護(hù)了一個單鏈表,有頭尾節(jié)點,同時LinkedHashMap節(jié)點Entry內(nèi)部除了繼承HashMap的Node屬性,還有before 和 after用于標(biāo)識前置節(jié)點和后置節(jié)點??梢詫崿F(xiàn)按插入的順序或訪問順序排序。

/**
 * The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;
/**
  * The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
//將加入的p節(jié)點添加到鏈表末尾
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
  LinkedHashMap.Entry<K,V> last = tail;
  tail = p;
  if (last == null)
    head = p;
  else {
    p.before = last;
    last.after = p;
  }
}
//LinkedHashMap的節(jié)點類
static class Entry<K,V> extends HashMap.Node<K,V> {
  Entry<K,V> before, after;
  Entry(int hash, K key, V value, Node<K,V> next) {
    super(hash, key, value, next);
  }
}

示例代碼:

public static void main(String[] args) {
  Map<String, String> linkedMap = new LinkedHashMap<String, String>();
  linkedMap.put("1", "占便宜");
  linkedMap.put("2", "沒夠兒");
  linkedMap.put("3", "吃虧");
  linkedMap.put("4", "難受");
  for(linkedMap.Entry<String,String> item: linkedMap.entrySet()){
    System.out.println(item.getKey() + ":" + item.getValue());
  }
}

輸出結(jié)果:

1:占便宜 2:沒夠兒 3:吃虧 4:難受

追問2:那TreeMap怎么實現(xiàn)有序的?

TreeMap是按照Key的自然順序或者Comprator的順序進(jìn)行排序,內(nèi)部是通過紅黑樹來實現(xiàn)。

TreeMap實現(xiàn)了SortedMap接口,它是一個key有序的Map類。要么key所屬的類實現(xiàn)Comparable接口,或者自定義一個實現(xiàn)了Comparator接口的比較器,傳給TreeMap用于key的比較。

TreeMap<String, String> map = new TreeMap<String, String>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
            return o2.compareTo(o1);
    }
});

追問3:put方法原理是怎么實現(xiàn)的?

在這里插入圖片描述

  1. 判斷數(shù)組是否為空,為空進(jìn)行初始化;
  2. 不為空,計算 k 的 hash 值,通過(n - 1) & hash計算應(yīng)當(dāng)存放在數(shù)組中的下標(biāo) index;
  3. 查看 table[index] 是否存在數(shù)據(jù),沒有數(shù)據(jù)就構(gòu)造一個Node節(jié)點存放在 table[index] 中;存在數(shù)據(jù),說明發(fā)生了hash沖突(存在二個節(jié)點key的hash值一樣), 繼續(xù)判斷key是否相等,相等,用新的value替換原數(shù)據(jù)(onlyIfAbsent為false);
  4. 如果不相等,判斷當(dāng)前節(jié)點類型是不是樹型節(jié)點,如果是樹型節(jié)點,創(chuàng)造樹型節(jié)點插入紅黑樹中;
  5. (如果當(dāng)前節(jié)點是樹型節(jié)點證明當(dāng)前已經(jīng)是紅黑樹了)如果不是樹型節(jié)點,創(chuàng)建普通Node加入鏈表中;
  6. 判斷鏈表長度是否大于 8并且數(shù)組長度大于64, 大于的話鏈表轉(zhuǎn)換為紅黑樹;
  7. 插入完成之后判斷當(dāng)前節(jié)點數(shù)是否大于閾值,如果大于開始擴容為原數(shù)組的二倍。

下面我們看看源碼中的內(nèi)容:

/**
 * 將指定參數(shù)key和指定參數(shù)value插入map中,如果key已經(jīng)存在,那就替換key對應(yīng)的value
 * 
 * @param key 指定key
 * @param value 指定value
 * @return 如果value被替換,則返回舊的value,否則返回null。當(dāng)然,可能key對應(yīng)的value就是null。
 */
public V put(K key, V value) {
    //putVal方法的實現(xiàn)就在下面
    return putVal(hash(key), key, value, false, true);
}

從源碼中可以看到,put(K key, V value)可以分為三個步驟:

  1. 通過hash(Object key)方法計算key的哈希值。
  2. 通過putVal(hash(key), key, value, false, true)方法實現(xiàn)功能。
  3. 返回putVal方法返回的結(jié)果。

那么看看putVal方法的源碼是如何實現(xiàn)的?

/**
 * Map.put和其他相關(guān)方法的實現(xiàn)需要的方法
 * 
 * @param hash 指定參數(shù)key的哈希值
 * @param key 指定參數(shù)key
 * @param value 指定參數(shù)value
 * @param onlyIfAbsent 如果為true,即使指定參數(shù)key在map中已經(jīng)存在,也不會替換value
 * @param evict 如果為false,數(shù)組table在創(chuàng)建模式中
 * @return 如果value被替換,則返回舊的value,否則返回null。當(dāng)然,可能key對應(yīng)的value就是null。
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //如果哈希表為空,調(diào)用resize()創(chuàng)建一個哈希表,并用變量n記錄哈希表長度
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //如果指定參數(shù)hash在表中沒有對應(yīng)的桶,即為沒有碰撞
    if ((p = tab[i = (n - 1) & hash]) == null)
        //直接將鍵值對插入到map中即可
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        //如果碰撞了,且桶中的第一個節(jié)點就匹配了
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            //將桶中的第一個節(jié)點記錄起來
            e = p;
        //如果桶中的第一個節(jié)點沒有匹配上,且桶內(nèi)為紅黑樹結(jié)構(gòu),則調(diào)用紅黑樹對應(yīng)的方法插入鍵值對
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        //不是紅黑樹結(jié)構(gòu),那么就肯定是鏈?zhǔn)浇Y(jié)構(gòu)
        else {
            //遍歷鏈?zhǔn)浇Y(jié)構(gòu)
            for (int binCount = 0; ; ++binCount) {
                //如果到了鏈表尾部
                if ((e = p.next) == null) {
                    //在鏈表尾部插入鍵值對
                    p.next = newNode(hash, key, value, null);
                    //如果鏈的長度大于TREEIFY_THRESHOLD這個臨界值,則把鏈變?yōu)榧t黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    //跳出循環(huán)
                    break;
                }
                //如果找到了重復(fù)的key,判斷鏈表中結(jié)點的key值與插入的元素的key值是否相等,如果相等,跳出循環(huán)
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                //用于遍歷桶中的鏈表,與前面的e = p.next組合,可以遍歷鏈表
                p = e;
            }
        }
        //如果key映射的節(jié)點不為null
        if (e != null) { // existing mapping for key
            //記錄節(jié)點的vlaue
            V oldValue = e.value;
            //如果onlyIfAbsent為false,或者oldValue為null
            if (!onlyIfAbsent || oldValue == null)
                //替換value
                e.value = value;
            //訪問后回調(diào)
            afterNodeAccess(e);
            //返回節(jié)點的舊值
            return oldValue;
        }
    }
    //結(jié)構(gòu)型修改次數(shù)+1
    ++modCount;
    //判斷是否需要擴容
    if (++size > threshold)
        resize();
    //插入后回調(diào)
    afterNodeInsertion(evict);
    return null;
}

追問4:HashMap擴容機制原理

capacity 即容量,默認(rèn)16。

loadFactor 加載因子,默認(rèn)是0.75

threshold 閾值。閾值=容量*加載因子。默認(rèn)12。當(dāng)元素數(shù)量超過閾值時便會觸發(fā)擴容。

  • 一般情況下,當(dāng)元素數(shù)量超過閾值時便會觸發(fā)擴容(調(diào)用resize()方法)。
  • 每次擴容的容量都是之前容量的2倍。
  • 擴展后Node對象的位置要么在原位置,要么移動到原偏移量兩倍的位置。

這里我們以JDK1.8的擴容為例:

HashMap的容量變化通常存在以下幾種情況:

1.空參數(shù)的構(gòu)造函數(shù):實例化的HashMap默認(rèn)內(nèi)部數(shù)組是null,即沒有實例化。第一次調(diào)用put方法時,則會開始第一次初始化擴容,長度為16。

2.有參構(gòu)造函數(shù):用于指定容量。會根據(jù)指定的正整數(shù)找到不小于指定容量的2的冪數(shù),將這個數(shù)設(shè)置賦值給閾值(threshold)。第一次調(diào)用put方法時,會將閾值賦值給容量,然后讓 閾值 = 容量 x 加載因子 。(因此并不是我們手動指定了容量就一定不會觸發(fā)擴容,超過閾值后一樣會擴容?。。?/p>

3.如果不是第一次擴容,則容量變?yōu)樵瓉淼?倍,閾值也變?yōu)樵瓉淼?倍。(容量和閾值都變?yōu)樵瓉淼?倍時,加載因子0.75不變)

此外還有幾個點需要注意:

  • 首次put時,先會觸發(fā)擴容(算是初始化),然后存入數(shù)據(jù),然后判斷是否需要擴容;可見首次擴容可能會調(diào)用兩次resize()方法。
  • 不是首次put,則不再初始化,直接存入數(shù)據(jù),然后判斷是否需要擴容;

擴容時,要擴大空間,為了使hash散列均勻分布,原有部分元素的位置會發(fā)生移位。

JDK7的元素遷移

JDK7中,HashMap的內(nèi)部數(shù)據(jù)保存的都是鏈表。因此邏輯相對簡單:在準(zhǔn)備好新的數(shù)組后,map會遍歷數(shù)組的每個“桶”,然后遍歷桶中的每個Entity,重新計算其hash值(也有可能不計算),找到新數(shù)組中的對應(yīng)位置,以頭插法插入新的鏈表。

  • 這里有幾個注意點:

是否要重新計算hash值的條件這里不深入討論,讀者可自行查閱源碼。因為是頭插法,因此新舊鏈表的元素位置會發(fā)生轉(zhuǎn)置現(xiàn)象。

元素遷移的過程中在多線程情境下有可能會觸發(fā)死循環(huán)(無限進(jìn)行鏈表反轉(zhuǎn))。

JDK1.8的元素遷移

JDK1.8則因為巧妙的設(shè)計,性能有了大大的提升:由于數(shù)組的容量是以2的冪次方擴容的,那么一個Entity在擴容時,新的位置要么在原位置,要么在原長度+原位置的位置。原因如下圖:

在這里插入圖片描述

數(shù)組長度變?yōu)樵瓉淼?倍,表現(xiàn)在二進(jìn)制上就是多了一個高位參與數(shù)組下標(biāo)確定。此時,一個元素通過hash轉(zhuǎn)換坐標(biāo)的方法計算后,恰好出現(xiàn)一個現(xiàn)象:最高位是0則坐標(biāo)不變,最高位是1則坐標(biāo)變?yōu)椤?0000+原坐標(biāo)”,即“原長度+原坐標(biāo)”。如下圖:

在這里插入圖片描述

因此,在擴容時,不需要重新計算元素的hash了,只需要判斷最高位是1還是0就好了。

JDK8的HashMap還有以下細(xì)節(jié)需要注意:

  • JDK8在遷移元素時是正序的,不會出現(xiàn)鏈表轉(zhuǎn)置的發(fā)生。
  • 如果某個桶內(nèi)的元素超過8個,則會將鏈表轉(zhuǎn)化成紅黑樹,加快數(shù)據(jù)查詢效率。

追問5:HashMap在JDK1.8都做了哪些優(yōu)化?

不同點 JDK 1.7 JDK 1.8
存儲結(jié)構(gòu) 數(shù)組 + 鏈表 數(shù)組 + 鏈表 + 紅黑樹
初始化方式 單獨函數(shù):inflateTable() 直接集成到了擴容函數(shù)resize()中
hash值計算方式 擾動處理 = 9次擾動 = 4次位運算 + 5次異或運算 擾動處理 = 2次擾動 = 1次位運算 + 1次異或運算
存放數(shù)據(jù)的規(guī)則 無沖突時,存放數(shù)組;沖突時,存放鏈表 無沖突時,存放數(shù)組;沖突 & 鏈表長度 < 8:存放單鏈表;沖突 & 鏈表長度 > 8:樹化并存放紅黑樹
插入數(shù)據(jù)方式 頭插法(先講原位置的數(shù)據(jù)移到后1位,再插入數(shù)據(jù)到該位置) 尾插法(直接插入到鏈表尾部/紅黑樹)
擴容后存儲位置的計算方式 全部按照原來方法進(jìn)行計算(即hashCode ->> 擾動函數(shù) ->> (h&length-1)) 按照擴容后的規(guī)律計算(即擴容后的位置=原位置 or 原位置 + 舊容量)

1.數(shù)組+鏈表改成了數(shù)組+鏈表或紅黑樹

防止發(fā)生hash沖突,鏈表長度過長,將時間復(fù)雜度由O(n)降為O(logn);

2.鏈表的插入方式從頭插法改成了尾插法,簡單說就是插入時,如果數(shù)組位置上已經(jīng)有元素,1.7將新元素放到數(shù)組中,新節(jié)點插入到鏈表頭部,原始節(jié)點后移;而JDK1.8會遍歷鏈表,將元素放置到鏈表的最后;

因為1.7頭插法擴容時,頭插法可能會導(dǎo)致鏈表發(fā)生反轉(zhuǎn),多線程環(huán)境下會產(chǎn)生環(huán)(死循環(huán));

在這里插入圖片描述

這個過程為,先將A復(fù)制到新的hash表中,然后接著復(fù)制B到鏈頭(A的前邊:B.next=A),本來B.next=null,到此也就結(jié)束了(跟線程二一樣的過程),但是,由于線程二擴容的原因,將B.next=A,所以,這里繼續(xù)復(fù)制A,讓A.next=B,由此,環(huán)形鏈表出現(xiàn):B.next=A; A.next=B

使用頭插會改變鏈表的上的順序,但是如果使用尾插,在擴容時會保持鏈表元素原本的順序,就不會出現(xiàn)鏈表成環(huán)的問題了。

就是說原本是A->B,在擴容后那個鏈表還是A->B。

3.擴容的時候1.7需要對原數(shù)組中的元素進(jìn)行重新hash定位在新數(shù)組的位置,1.8采用更簡單的判斷邏輯,位置不變或索引+舊容量大?。?/p>

4.在插入時,1.7先判斷是否需要擴容,再插入,1.8先進(jìn)行插入,插入完成再判斷是否需要擴容;

追問6:鏈表紅黑樹如何互相轉(zhuǎn)換?閾值多少?

  • **鏈表轉(zhuǎn)紅黑樹的閾值為:8
  • ****紅黑樹轉(zhuǎn)鏈表的閾值為:6 **

經(jīng)過計算,在hash函數(shù)設(shè)計合理的情況下,發(fā)生hash碰撞8次的幾率為百萬分之6,從概率上講,閾值為8足夠用;至于為什么紅黑樹轉(zhuǎn)回來鏈表的條件閾值是6而不是7或9?因為如果hash碰撞次數(shù)在8附近徘徊,可能會頻繁發(fā)生鏈表和紅黑樹的互相轉(zhuǎn)化操作,為了預(yù)防這種情況的發(fā)生。

面試題2:HashMap是線程安全的嗎?

正經(jīng)回答:

不是線程安全的,在多線程環(huán)境下,

  • JDK1.7:會產(chǎn)生死循環(huán)、數(shù)據(jù)丟失、數(shù)據(jù)覆蓋的問題;
  • JDK1.8:中會有數(shù)據(jù)覆蓋的問題。

以1.8為例,當(dāng)A線程判斷index位置為空后正好掛起,B線程開始往index位置寫入數(shù)據(jù)時,這時A線程恢復(fù),執(zhí)行寫入操作,這樣A或B數(shù)據(jù)就被覆蓋了。

追問1:你是如何解決這個線程不安全問題的?

在Java中有HashTable、SynchronizedMapConcurrentHashMap這三種是實現(xiàn)線程安全的Map。

  • HashTable:是直接在操作方法上加synchronized關(guān)鍵字,鎖住整個數(shù)組,粒度比較大;
  • SynchronizedMap:是使用Collections集合工具的內(nèi)部類,通過傳入Map封裝出一個SynchronizedMap對象,內(nèi)部定義了一個對象鎖,方法內(nèi)通過對象鎖實現(xiàn);
  • ConcurrentHashMap:使用分段鎖(CAS + synchronized相結(jié)合),降低了鎖粒度,大大提高并發(fā)度。

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望你能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • 解決java編譯錯誤:程序包不存在的問題

    解決java編譯錯誤:程序包不存在的問題

    出錯:Error:(3, 27) java: 程序包com.aliyun.odps.udf不存在,遇到這樣的錯誤問題如何解決呢,下面小編給大家?guī)砹薺ava編譯錯誤:程序包不存在的問題及解決方法,感興趣的朋友一起看看吧
    2023-05-05
  • 使用 Spring Boot 內(nèi)嵌容器 Undertow創(chuàng)建服務(wù)器的方法

    使用 Spring Boot 內(nèi)嵌容器 Undertow創(chuàng)建服務(wù)器的方法

    Undertow是一個非常輕量并高性能的web server,它來自 JBoss。支持blocking和non-blocking兩種NIO API。接下來通過本文給大家介紹使用Spring Boot 內(nèi)嵌容器 Undertow創(chuàng)建服務(wù)器的方法,感興趣的朋友一起看看吧
    2017-11-11
  • Java實現(xiàn)定時備份文件

    Java實現(xiàn)定時備份文件

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)定時備份文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Spring @Configuration注解及配置方法

    Spring @Configuration注解及配置方法

    這篇文章主要介紹了Spring @Configuration注解及配置方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Java線程之間的共享與協(xié)作詳解

    Java線程之間的共享與協(xié)作詳解

    這篇文章主要介紹了Java線程之間的共享與協(xié)作詳解,進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單位,線程是進(jìn)程的一個實體,是CPU調(diào)度和分派的基本單位,它是比經(jīng)常更小的、能夠獨立運行的基本單位
    2022-07-07
  • 使用監(jiān)聽器對Spring bean id進(jìn)行唯一校驗過程解析

    使用監(jiān)聽器對Spring bean id進(jìn)行唯一校驗過程解析

    這篇文章主要介紹了使用監(jiān)聽器對Spring bean id進(jìn)行唯一校驗過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • 在idea里如何調(diào)整maven內(nèi)存

    在idea里如何調(diào)整maven內(nèi)存

    這篇文章主要介紹了在idea里如何調(diào)整maven內(nèi)存問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • java中Class.forName方法的作用詳解

    java中Class.forName方法的作用詳解

    Class.forName(xxx.xx.xx) 返回的是一個類,但Class.forName方法的作用到底是什么終?下面這篇文章就來給大家詳細(xì)介紹了關(guān)于java中Class.forName方法的作用,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-06-06
  • 5個主流的Java開源IDE工具詳解

    5個主流的Java開源IDE工具詳解

    這篇文章主要介紹了5個主流的Java開源IDE工具,無論如何,Java在當(dāng)今使用的編程語言中始終排在前三名,在TIOBE索引中涉及700萬到1000萬的程序員和開發(fā)者
    2020-07-07
  • mybatis?foreach?屬性及其三種使用情況詳解

    mybatis?foreach?屬性及其三種使用情況詳解

    這篇文章主要介紹了mybatis?foreach?屬性及其三種使用情況詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01

最新評論