java based

搬運來源: https://github.com/jujunchen/Java-interview-question

1.Java 基礎(chǔ)

[TOC]

《Java編程思想》篡诽、《瘋狂Java:突破程序員基本功的16課.修訂版》,這里省略了一些非逞缕基礎(chǔ)的知識點

請你解釋為什么會出現(xiàn) 4.0 - 3.6=0.40000001 這種現(xiàn)象

二進制小數(shù)無法精確表達十進制小數(shù),計算機在計算十進制小數(shù)的過程中要先轉(zhuǎn)換為二進制進行計算杈女,這個過程中出現(xiàn)了誤差

請你講講一個十進制的數(shù)在內(nèi)存中是怎么存的朱浴?

以二進制補碼形式存儲吊圾,最高位是符號位,正數(shù)的補碼是它的原碼翰蠢,負數(shù)的補碼是它的反碼加1项乒,在求反碼時符號位不變,其他取反梁沧,1表示負數(shù)檀何,0正數(shù)

接口和抽象類的區(qū)別是什么 ?

接口中的所有方法隱含的都是抽象的廷支,而抽象類則可以同時包含抽象和非抽象方法

類可以實現(xiàn)很多個接口频鉴,但只能繼承一個抽象類

類可以不實現(xiàn)抽象類和接口聲明的所有方法,但這種情況下恋拍,該類必須聲明成抽象的

接口中的成員函數(shù)默認是public的垛孔。抽象類的成員函數(shù)可以是private,protected或者是public施敢。

JDK8后周荐,接口中可以包含default方法,抽象類中不可以

面向?qū)ο箝_發(fā)的六個基本原則悯姊,在項目中用過哪些原則

  • 六個基本原則

    • 單一原則

      一個類只做它該做的事情(高內(nèi)聚)羡藐。在面向?qū)ο笾校绻蛔屢粋€類完成它該做的事悯许,而不涉及與它無關(guān)的領(lǐng)域就是踐行了高內(nèi)聚的原則仆嗦,這個類就只有單一原則

    • 開閉原則

      軟件實體應(yīng)當對擴展開發(fā),對修改關(guān)閉先壕,要做到開閉有兩個要點:

      1. 抽象是關(guān)鍵瘩扼,一個系統(tǒng)中如果沒有抽象類或接口系統(tǒng)就沒有擴展點
      2. 封裝可變性,將系統(tǒng)中的各種可變因素封裝到一個繼承結(jié)構(gòu)中垃僚,如果多個可變因素混雜在一起集绰,系統(tǒng)將變的復(fù)雜而繁亂
    • 里氏替換原則

      任何時候都可以用子類型替換掉父類型,子類一定是增加父類的能力而不是減少父類的能力

    • 依賴倒置原則

      面向接口編程谆棺。高層模塊不應(yīng)該依賴底層模塊栽燕,兩者都應(yīng)該依賴其抽象,盡可能使用抽象類型而不用具體類型改淑,因為抽象類型可以被它的任何一個子類型所替代碍岔。

    • 接口隔離原則

      類間的依賴關(guān)系應(yīng)該建立在最小的接口上,不能大而全朵夏,接口表示能力蔼啦,一個接口只應(yīng)該描述一種能力,接口也應(yīng)該高度內(nèi)聚

    • 合成聚成復(fù)用原則
      has a 關(guān)聯(lián)仰猖;use a;

    • 迪米特法則

      由叫最少知識原則捏肢,一個對象應(yīng)該對其他對象有盡可能少的了解

  • 根據(jù)自己的項目來說

    詳細的可以看這里:https://www.cnblogs.com/qifengshi/p/5709594.html

關(guān)于網(wǎng)絡(luò)方面的問題奈籽,面試官應(yīng)該只會問一題,不會多問

HTTP請求的GET與POST方式的區(qū)別

  1. GET在瀏覽器回退是無害的鸵赫,而POST會再次提交請求
  2. GET請求會被瀏覽器主動cache,而POST不會衣屏,除非手動設(shè)置
  3. GET請求只能進行URL編碼,而POST支持多種編碼
  4. GET請求參數(shù)會被完整保留在瀏覽器歷史記錄中奉瘤,而POST中的參數(shù)不會被保留
  5. GET請求在URL中傳送參數(shù)是有大小限制的勾拉,不能大于2KB,而POST可以說沒有
  6. GET只接受ASCII字符煮甥,而POST沒有限制
  7. GET參數(shù)直接暴露在URL上盗温,而POST將數(shù)據(jù)放在request body中

TCP 三次握手,四次揮手

可見該文:https://blog.csdn.net/qq_38950316/article/details/81087809

TCP 和 UDP區(qū)別

  • 區(qū)別:

    UDP是無連接的成肘,即發(fā)送數(shù)據(jù)之前不需要建立連接

    UDP使用盡最大努力交付卖局,即不保證可靠交付,同時也不使用擁塞控制

    UDP是面向報文的双霍,沒有擁塞控制砚偶,適合多媒體通信要求

    UDP支持一對一,一對多洒闸,多對一和多對多的交互通信

    UDP首部開銷小染坯,只有8個字節(jié)

    TCP是面向連接的運輸層協(xié)議

    TCP只能一對一連接

    TCP提供可靠的交付服務(wù),提供全雙工通信

    TCP 面向字節(jié)流丘逸,頭部最低20個字節(jié)

從輸入網(wǎng)址到獲取頁面的過程

  • 查詢DNS单鹿, 獲取域名對應(yīng)的IP地址
    • 瀏覽器搜索自身的DNS緩存
    • 搜索操作系統(tǒng)的DNS緩存
    • 讀取本地的HOST文件
    • 發(fā)起一個DNS系統(tǒng)調(diào)用(寬帶運營服務(wù)器查看本身緩存,運營服務(wù)器發(fā)起一個迭代DNS解析請求)
  • 瀏覽器獲得域名對應(yīng)的IP地址后深纲,發(fā)起HTTP三次握手
  • TCP/IP建立連接后仲锄,瀏覽器可以向服務(wù)器發(fā)送HTTP請求了
  • 服務(wù)器接收到請求后,根據(jù)路徑參數(shù)湃鹊,經(jīng)過后端處理將頁面返回給瀏覽器
  • 瀏覽器渲染頁面儒喊,和外部資源,最終將完整的頁面呈現(xiàn)給用戶

Session與Cookie區(qū)別

Session:

服務(wù)器端會為每個訪問服務(wù)端的請求分配一個會話Session币呵,其數(shù)據(jù)存儲在服務(wù)器端怀愧,不依賴瀏覽器端環(huán)境,因此高效安全

Cookie:

數(shù)據(jù)已文件形式存在用戶瀏覽器端余赢,用戶可以通過瀏覽器禁用Cookie芯义,用戶可以對Cookie進行查看,修改没佑,和刪除

列出自己常用的JDK包

常用的包:

java.lang 包裝類毕贼,線程等都在該包

java.match 有BigDecimal 精確數(shù)字類型

java.util 并發(fā),集合等都在該包內(nèi)

equals與==的區(qū)別

  1. equals 比較兩個實體值是否相同蛤奢,可以被覆蓋鬼癣,但需要遵循幾個約定:

    自反性:對于任何非null的引用值x, x.equals(x)必須返回true

    對稱性:對于任何非null的引用值x和y陶贼,當y.equals(x)返回true時,x.equlas(y)必須返回true

    傳遞性:對于任何非null的引用值x待秃、y拜秧、z,如果x.equals(y)返回true章郁,并且y.equals(x)也返回true枉氮,那么x.equals(z)也必須返回true

    一致性:對于任何非null的引用值x和y,只要比較對象中的所有信息沒有被修改暖庄,多次調(diào)用equals一致返回true聊替,或者false

  2. == 比較兩個實體的引用地址是否相等,不能覆蓋培廓,如果引用地址相等惹悄,那認為兩個實體為同一個實體

int a = 1;
Integer b = new Integer(1);
Integer c = new Integer(1);
Integer d = Integer.valueOf(1);
Long e = new Long(1);
Long f = Long.valueOf(1);

assert a == b;
assert b != c;
assert b != d;
assert a == d;
assert a == e;
assert a == f;
assert b.equals(c);
assert !b.equals(e);

hashCode和equals方法的區(qū)別與聯(lián)系

對于覆蓋了equals方法的類中,同樣也要覆蓋hashCode方法肩钠。這是JDK規(guī)定的結(jié)果泣港。

比較兩個對象是否相同,hashCode比equals效率更高价匠,所以優(yōu)先會根據(jù)hashCode來比較当纱,但如果不重寫hashCode,原本兩個對象可以認為是相等踩窖,但由于hashCode默認返回表示對象地址的整數(shù)坡氯,必然不相等,所以需要重寫hashCode毙石。

由于hashCode有個問題廉沮,可能兩個不同的對象會有相同的hashCode,這樣還需要通過equals來比較

比如HashMap中徐矩,計算key的索引位置滞时,會用到key.hashCode,在確定是否為同一個元素時通過equals比較

什么是Java序列化和反序列化滤灯,如何實現(xiàn)Java序列化坪稽?或者請解釋Serializable 接口的作用

序列化是一種用來處理對象流的機制,也就是將對象的內(nèi)容轉(zhuǎn)化成二進制流鳞骤,可以將對象持久化或者網(wǎng)絡(luò)傳輸

反序列化是將二進制流還原為對象的過程

實現(xiàn)Java序列化窒百,通過實現(xiàn)Serializable即可

Object類中常見的方法,為什么wait notify會放在Object里邊豫尽?

因為Java提供的鎖是對象級的篙梢,每個對象都有對象頭,用來存儲鎖

解釋一下extends 和super泛型限定符

<? extends Fruit> 稱為 上界限定符美旧,list只能get渤滞,不能add(除了add null值),通常用于讀

<? super Apple>稱為 下界限定符贬墩,list只能add,不能get(只能用Object接收),通過用于寫

請列舉你所知道的Object類的方法并簡要說明

Object()默認構(gòu)造方法

clone()創(chuàng)建并返回此對象的一個副本

equals(Object obj) 當前對象是否與obj對象相同

finalize()當垃圾收集器確定該對象可以回收時,由垃圾收集器調(diào)用此方法

getClass返回一個對象的運行時類

hashCode()返回該對象的哈希碼值

notify()喚醒此對象監(jiān)視器上等待的單個線程

notifyAll()喚醒在此對象監(jiān)視器上等待的所有線程

toString()返回該對象的字符串表示

wait()使當前線程等待妄呕,直到其他線程調(diào)用此對象的notify()或者notifyAll()方法

wait(long timeout)導(dǎo)致當前的線程等待陶舞,直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量绪励。wait(long timeout, int nanos) 導(dǎo)致當前的線程等待肿孵,直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程疏魏,或者已超過某個實際時間量停做。

創(chuàng)建線程的方式

  1. 繼承Thread類創(chuàng)建線程,并重寫run方法蠢护,調(diào)用實例對象的start()方法啟動線程
  2. 實現(xiàn)Runnable接口雅宾,并實現(xiàn)run方法,將實現(xiàn)Runnable的類傳入Thread構(gòu)造函數(shù)中葵硕,并調(diào)用Thread實例對象的start方法啟動線程
  3. 實現(xiàn)Callable接口,并實現(xiàn)call方法贯吓,創(chuàng)建Callable實現(xiàn)類的實例懈凹,使用FutureTask包裝Callable對象,使用FutureTask對象傳入Thread中悄谐,調(diào)用start方法啟動線程介评,使用FutureTask對象的get方法獲取線程的返回值

ArrayList 與 LinkedList 區(qū)別

ArrayList 是一種順序存儲的線性表,底層使用數(shù)組實現(xiàn)

LinkedList是一種鏈式存儲的線性表爬舰,本質(zhì)是一個雙向鏈表们陆,實現(xiàn)了List、Deque接口情屹,可以當成雙向鏈表坪仇、隊列、棧使用

自定義注解

  1. 聲明注解的保留期限類型

    @Retention(RetentionPolicy.RUNTIME)表示該注解可以在運行期保留

    保留期限類型:java.lang.annotation.Retention

    SOURCE: 注解信息僅保留在目標類源代碼文件中垃你,對應(yīng)的字節(jié)碼文件不會保留

    CLASS: 注解信息存在于源代碼椅文、字節(jié)碼文件中,但運行期JVM不能獲得該注解信息

    RUNTIME: 注解信息存在于源代碼惜颇、字節(jié)碼文件皆刺、運行期JVM中,能夠通過反射機制獲取注解類信息

  2. 聲明注解可以使用的目標類型

    @Target(ElementType.METHOD) 表示這個注解只能在方法上使用

    目標類型:java.lang.annotation.ElementType

    TYPE: 類凌摄、接口羡蛾、注解類、Enum

    FIELD: 類成員變量或常量

    METHOD: 方法

    PARAMETER: 參數(shù)

    CONSTRUCTOR: 構(gòu)造器

    LOCAL_VARIABLE: 局部變量

    ANNOTATION_TYPE: 注解

    PACKAGE: 包

  3. 使用@interface 修飾類

  4. 聲明注解成員

    成員無入?yún)⑾强鳌⒉荒軖伋霎惓#?/p>

    可以通過default成員指定默認值

    成員類型只能使用基本數(shù)據(jù)類型痴怨、String煎殷、Class、enums腿箩、注解類型豪直,及上述類型的數(shù)組類型。如ForumService value()是非法的

    如果注解只有一個成員珠移,則成員名必須取名為value()弓乙,再使用時可以忽略成員名和賦值號,如果注解類擁有多個成員時钧惧,

    對value成員賦值暇韧,可以省略value和賦值號,如果是多個成員賦值浓瞪,必須使用賦值號

ArrayList擴容機制是怎么樣的懈玻? 詳細說一下。

在往ArrayList add元素的時候乾颁,如果ArrayList 已有元素數(shù)量+1 大于 ArrayList 存儲元素的總長度涂乌,就會觸發(fā)擴容。

首先ArrayList會計算新數(shù)組的長度英岭,長度為老數(shù)組的0.5倍盐肃,如果新數(shù)組長度還是小于插入元素需要的最小長度谁不,那么新數(shù)組長度賦值為最小長度骡澈,如果超過ArrayList允許的最大長度Integer.MAX_VALUE(2^{31} - 1)珍策,那么新數(shù)組長度為Integer.MAX_VALUE,否則為Integer.MAX_VALUE - 8(為什么要-8?Why the maximum array size of ArrayList is Integer.MAX_VALUE - 8?

最后將原數(shù)組元素拷貝到新數(shù)組進行擴容

HashMap 1.7 和 1.8 的區(qū)別

  • 1.7吭狡,在發(fā)生hash沖突的時候尖殃,數(shù)據(jù)結(jié)構(gòu)只有鏈表;

  • 1.8划煮,數(shù)據(jù)結(jié)構(gòu)有鏈表和紅黑樹送丰,使用紅黑樹是為了能夠提高查詢效率。在鏈表長度達到7時(bingCount >= TREEIFY_THRESHOLD - 1)般此,并且hash tab[]數(shù)組長度大于等于64時蚪战,將鏈表轉(zhuǎn)換成紅黑樹,如果數(shù)組長度小于64铐懊,只是對數(shù)組進行擴容

    https://blog.csdn.net/qq_21251983/article/details/90056067

HashMap中的key可以是任何對象或數(shù)據(jù)類型嗎

  • 可以是null邀桑,但不能是可變對象,如果是可變對象科乎,對象中的屬性改變壁畸,則對象的HashCode也相應(yīng)改變,導(dǎo)致下次無法查找到已存在Map中的數(shù)據(jù)
  • 如果要可變對象當著鍵,必須保證其HashCode在成員屬性改變的時候保持不變

HashMap 初始容量 計算方法

如果在new HashMap的時候捏萍,沒有指定初始initialCapacity太抓,則初始initialCapacity為16,負載因子為0.75令杈,下次擴容閾值為 16*0.75=12

這個初始容量 不一定等于初始化完成后底層數(shù)組實際的容量走敌,因為存在閾值的計算,方法如下逗噩;也不是初始容量是多少開始就能存多少個元素掉丽,因為存在負載因子,在底層數(shù)組還沒滿的時候就會進行擴容

閾值計算方法為:

 static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

該方法計算大于等于輸入?yún)?shù)并最接近參數(shù)的2的整數(shù)次冪的數(shù)异雁,如10捶障,返回16

cap -1 ,-1是為了在計算的時候能得到大于等于輸入?yún)?shù)的值

在HashMap 進行put方法的時候纲刀,如果判斷已有的元素大于 閾值就會觸發(fā)擴容計算项炼,擴容步驟

final Node<K,V>[] resize() {
        //原table數(shù)組賦值
        Node<K,V>[] oldTab = table;
        //如果原數(shù)組為null,那么原數(shù)組長度為0
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //賦值閾值
        int oldThr = threshold;
        //newCap 新數(shù)組長度
        //newThr 下次擴容的閾值
        int newCap, newThr = 0;
        // 1. 如果原數(shù)組長度大于0
        if (oldCap > 0) {
            //如果大于最大長度1 << 30 = 1073741824示绊,那么閾值賦值為Integer.MAX_VALUE后直接返回
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            // 2. 如果原數(shù)組長度的2倍小于最大長度锭部,并且原數(shù)組長度大于默認長度16,那么新閾值為原閾值的2倍
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        // 3. 如果原數(shù)組長度等于0耻台,但原閾值大于0空免,那么新的數(shù)組長度賦值為原閾值大小
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            // 4. 如果原數(shù)組長度為0,閾值為0盆耽,那么新數(shù)組長度,新閾值都初始化為默認值
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        // 5.如果新的閾值等于0
        if (newThr == 0) {
            //計算臨時閾值
            float ft = (float)newCap * loadFactor;
            //新數(shù)組長度小于最大長度扼菠,臨時閾值也小于最大長度摄杂,新閾值為臨時閾值,否則是Integer.MAX_VALUE
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        //計算出來的新閾值賦值給對象的閾值
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //用新計算的數(shù)組長度新建一個Node數(shù)組循榆,并賦值給對象的table
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //后面是copy數(shù)組和鏈表數(shù)據(jù)邏輯
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

總結(jié):

如下3種情況析恢,例子需要自己調(diào)式,主要關(guān)注數(shù)組長度(OldCap,newCap)新老變化秧饮,擴容閾值(oldThr,newThr)新老變化

//①
Map<String, String> map = new HashMap<>();
map.put("1", "1");
//②
Map<String, String> map1 = new HashMap<>(2);
map1.put("2", "2");
//③
Map<String, String> map2 = new HashMap<>(2, 0.5f);
map2.put("3", "3");

① 沒有設(shè)置initialCapacity映挂,也沒有設(shè)置負載因子,第一次put的時候會觸發(fā)擴容

if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

第一次的時候盗尸,數(shù)組長度為默認值16柑船,閾值為16*0.75=12,走的代碼4邏輯泼各,等到數(shù)組長度超過閾值12后鞍时,觸發(fā)第二次擴容,此時table數(shù)組,和threshold都不為0逆巍,即oldTab及塘、oldCap、oldThr都不為0锐极,先走代碼1笙僚,如果oldCap長度的2倍沒有超過最大容量,并且oldCap 長度大于等于 默認容量16灵再,那么下次擴容的閾值 變?yōu)閛ldThr大小的兩倍即 12 * 2 = 24肋层,newThr = 24,newCap=32

② 設(shè)置了initialCapacity檬嘀,沒有設(shè)置負載因子槽驶,此時hashMap使用默認負載因子0.75,本實例設(shè)置的初始容量為2鸳兽,通過計算閾值為2掂铐,第一次put的時候由于還沒初始化table數(shù)組,因此觸發(fā)第一次擴容揍异。此時oldCap為0全陨,oldThr為2,走代碼3衷掷,確定這次擴容的新數(shù)組大小為2辱姨,此時還沒有確定newThr 下次擴容的大小,于是進入代碼5 確定newThr為 2 * 0.75 = 1.5 取整 1 戚嗅,及下次擴容閾值為1雨涛。當數(shù)組已有元素大于閾值及1時,觸發(fā)第二次擴容懦胞,此時oldCap為1替久,oldThr為1,走代碼1newCap = oldCap << 1 結(jié)果為 4 小于最大容量躏尉, 但oldCap 小于hashMap默認大小16蚯根,結(jié)果為false,跳出判斷胀糜,此時由于newThr等于0颅拦,進入代碼5,確定newThr為 4 * 0.75 = 3教藻,下次擴容閾值為3

③ 設(shè)置了initialCapacity=2距帅,負載因子為0.5,通過tableSizeFor計算閾值為2怖竭,第一次put的時候锥债,進行擴容,此時oldCap為2,oldThr為2哮肚,進入代碼1登夫,同實例②,newCap = oldCap << 1 結(jié)果為 4 小于最大容量允趟, 但oldCap 小于hashMap默認大小16恼策,結(jié)果為false,跳出判斷潮剪,進入代碼5涣楷,確定newThr為 4 * 0.5 = 2,下次擴容閾值為2

面試回答要素

  1. 回答什么情況下會第一擴容,舉例說明抗碰,新數(shù)組大小狮斗,閾值大小
  2. 以后什么情況下會再次擴容,這次是怎么計算新數(shù)組大小弧蝇,及閾值大小的

HashMap碳褒、ConcurrentHashMap初始化閾值為什么要是8,才轉(zhuǎn)為紅黑樹看疗?

  1. 當初始閾值為8時沙峻,鏈表的長度達到8的概率變的很小,如果再大概率減小的并不明顯
  2. 樹結(jié)構(gòu)查找的時間復(fù)雜度是O(log(n))两芳,而鏈表的時間復(fù)雜度是O(n)摔寨,當閾值為8時,long8 = 3怖辆,相比鏈表更快是复,但樹結(jié)構(gòu)比鏈表占用的空間更多,所以這是一種時間和空間的平衡

手寫HashMap

要點:

  1. 底層是數(shù)組結(jié)構(gòu)
  2. hash沖突的時候竖螃,轉(zhuǎn)換為鏈表
  3. 考慮擴容處理

HashMap在并發(fā)下會產(chǎn)生什么問題佑笋?有什么替代方案?(HashTable, ConcurrentHashMap)。它們兩者的實現(xiàn)原理斑鼻。

  • HashMap并發(fā)下產(chǎn)生問題:由于在發(fā)生hash沖突,插入鏈表的時候猎荠,多線程會造成環(huán)鏈坚弱,再get的時候變成死循環(huán),Map.size()不準確关摇,數(shù)據(jù)丟失
    https://www.iteye.com/blog/hwl-sz-1897468

  • HashTable: 通過synchronized來修飾荒叶,效率低,多線程put的時候输虱,只能有一個線程成功些楣,其他線程都處于阻塞狀態(tài)

  • ConcurrentHashMap:
    1.7 采用鎖分段技術(shù)提高并發(fā)訪問率
    1.8 數(shù)據(jù)依舊是分段存儲,但鎖采用了synchronized,內(nèi)部采用Node數(shù)組+鏈表+紅黑樹的結(jié)構(gòu)存儲愁茁,當單個鏈表存儲數(shù)量達到紅黑樹閾值8時(此時鏈表已有元素7)蚕钦,并且數(shù)組長度大于64時,存儲結(jié)構(gòu)轉(zhuǎn)換為紅黑樹來存儲鹅很,否則只進行數(shù)組的擴容

    https://www.cnblogs.com/banjinbaijiu/p/9147434.html

HashMap 為什么不用平衡樹嘶居,而用紅黑樹

紅黑樹也是一種平衡樹,但不是嚴格平衡促煮,平衡樹是左右子樹高度差不超過1邮屁,紅黑樹可以是2倍

紅黑樹在插入、刪除的時候旋轉(zhuǎn)的概率比平衡樹低很多菠齿,效率比平衡樹高

查找時間復(fù)雜度都維持在O(logN)

關(guān)于HashMap的其他文章:https://blog.csdn.net/login_sonata/article/details/76598675

源碼解析:http://www.reibang.com/p/0a70ce2d3b67

常見異常分為哪兩種(Exception,Error)佑吝,區(qū)別是什么,了解受檢異常和非受檢異常嗎

Exception,Error有共同的父類Throwable

Error: 表示程序發(fā)生錯誤绳匀,是程序無法處理的芋忿,不可恢復(fù)的,如OutOfMemoryError

Exception: 表示程序可處理的異常襟士,又分為CheckedException(受檢異常)盗飒、UncheckedException(非受檢異常),受檢異常發(fā)生在編譯期陋桂,必須要使用try...catch 或者 throws捕獲或者拋出異常逆趣,否則編譯不通過;非受檢異常發(fā)生在運行期嗜历,具有不確定性宣渗,主要由程序的邏輯問題引起的,在程序設(shè)計的時候要認真考慮梨州,盡量處理異常

實現(xiàn)一個LRU

可以直接使用LinkedHashMap實現(xiàn)

《LinkedHashMap源碼分析》

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private int initialCapacity;
    public LRUCache(int initialCapacity) {
        //true表示按照訪問順序排序
        super(initialCapacity, 0.75f, true);
        this.initialCapacity = initialCapacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        if (size() > initialCapacity) {
            return true;
        }
        return false;
    }
}

用過流沒有痕囱,流怎么實現(xiàn)

Stream流是Java8中引入的新特性,Stream有幾個特點:

不存數(shù)據(jù)暴匠,都是通過管道將源數(shù)據(jù)元素傳遞給操作鞍恢;

對Stream的任何修改都不會修改數(shù)據(jù)源,都是新產(chǎn)生一個流

流的很多操作如filter每窖、map都是延遲執(zhí)行的帮掉,只有到終點才會將操作順序執(zhí)行

對于無限流可以通過“短路”操作訪問到有限元素后就返回

流的元素只訪問一次,如果需要重新訪問窒典,需要重新生成一個新的流

Stream中BaseStream規(guī)定了流的基本接口蟆炊,在Stream中使用Stage來描述一個完整的操作,將具有先后順序的各個Stage連一起瀑志,就構(gòu)成了整個流水線涩搓。

AbstractPipeline是流水線的核心污秆,定義了三個AbstractPipeline類型的變量:sourceStage(源階段)、previousStage(上游pipeline昧甘,上一階段)良拼,nexStage(下一階段)

ReferencePipeline 繼承了AbstractPipeline

Head、StatefulOp疾层、StatelessOp繼承了ReferencePipeline将饺,分別表示源,無狀態(tài)操作痛黎,有狀態(tài)操作

image-20200629003951004

比如Collection.stream()方法得到Head也就是stage0予弧,緊接著調(diào)用一系列的中間操作,不斷產(chǎn)生新的stage湖饱。這些stage對象以雙向鏈表的形式組織在一起掖蛤,構(gòu)成整個流水線。
由于每個Stage都記錄了前一個Stage和本次的操作以及回調(diào)函數(shù)井厌,依靠這種結(jié)構(gòu)就建立起對數(shù)據(jù)源的所有操作蚓庭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仅仆,隨后出現(xiàn)的幾起案子器赞,更是在濱河造成了極大的恐慌,老刑警劉巖墓拜,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件港柜,死亡現(xiàn)場離奇詭異,居然都是意外死亡咳榜,警方通過查閱死者的電腦和手機夏醉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涌韩,“玉大人畔柔,你說我怎么就攤上這事〕加#” “怎么了靶擦?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雇毫。 經(jīng)常有香客問我奢啥,道長,這世上最難降的妖魔是什么嘴拢? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮寂纪,結(jié)果婚禮上席吴,老公的妹妹穿的比我還像新娘赌结。我一直安慰自己,他們只是感情好孝冒,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布柬姚。 她就那樣靜靜地躺著,像睡著了一般庄涡。 火紅的嫁衣襯著肌膚如雪量承。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天穴店,我揣著相機與錄音撕捍,去河邊找鬼。 笑死泣洞,一個胖子當著我的面吹牛忧风,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播球凰,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狮腿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呕诉?” 一聲冷哼從身側(cè)響起缘厢,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甩挫,沒想到半個月后贴硫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡捶闸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年夜畴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片删壮。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡贪绘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出央碟,到底是詐尸還是另有隱情税灌,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布亿虽,位于F島的核電站菱涤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洛勉。R本人自食惡果不足惜粘秆,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望收毫。 院中可真熱鬧攻走,春花似錦殷勘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至摘符,卻和暖如春贤斜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逛裤。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工瘩绒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人别凹。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓草讶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炉菲。 傳聞我的和親對象是個殘疾皇子堕战,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容