1. 程式人生 > >【JAVA面試】java面試題整理(3)

【JAVA面試】java面試題整理(3)

                                     java面試題整理(3)

JAVA常考點3

目錄

1. 講下JAVA的執行時區域

回答:執行時資料區整體分為兩類 執行緒私有和執行緒共享。

執行緒私有的包括:

(1)程式計數器

若正在執行的是java方法,則計數器記錄的是正在執行的位元組碼指令的地址

若正在執行的是native方法,則計數器為空

該區域是唯一一個不會導致outofmemoryError的區域

(2)虛擬機器棧

描述的是Java方法執行的記憶體模型:每個方法都會建立一個棧幀用於儲存區域性變量表,運算元棧,動態連結,方法出口等資訊

區域性變量表存放了編譯期可知的基本資料型別,物件引用,和returnAddress型別(指向一條位元組碼指令地址),區域性變量表的記憶體空間在編譯器確定,在執行期不變

可導致兩種異常:執行緒請求的棧深度大於虛擬機器允許的深度-StackOverflowError;虛擬機器無法申請到足夠的記憶體-OutOfMemoryError

(3)本地方法棧

和虛擬機器棧類似,但它是為Native方法服務的

執行緒共享的包括:

(1)堆

java堆是被所有執行緒共享的記憶體區域,在虛擬機器啟動時建立,用來分配物件例項和陣列

堆是垃圾回收器主要管理的區域,堆可分為新生代和老年代

從記憶體分配角度看,堆可劃分出多個執行緒私有的分配緩衝區(TLAB)

大小可通過 -Xmx 和 -Xms 控制

(2)方法區

用來存放虛擬機器載入的類資訊,常量,靜態變數,即時編譯器編譯後的程式碼等資訊

GC會回收該區域的常量池和進行型別的解除安裝 *執行時常量池

♣ Class檔案的常量池用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後存放在執行時常量池中

♣ 還把翻譯出來的直接引用也放在執行時常量池中,執行時產生的常量也放在裡面

2、簡單說下垃圾回收機制

大致思路: 要進行垃圾回收,首先要判斷一個物件是否活著,這就引出了兩種方法…

引用計數法和可達性分析法

gc roots 型別

引用型別

兩次標記過程

垃圾回收演算法

記憶體分配策略

觸發垃圾回收

垃圾回收器

也會回收方法區

回答:要進行垃圾回收,首先要判斷物件是否存活,引出了兩個方法:

引用計數法

    • 思想:給物件設定引用計數器,沒引用該物件一次,計數器就+1,引用失效時,計數器就-1,當任意時候引用計數器的值都為0時,則該物件可被回收
    • Java不適用原因:無法解決物件互相迴圈引用的問題

可達性分析法

    • 以GC Roots為起點,從這些起點開始向下搜尋,經過的路徑稱為引用鏈。若一個物件到GC Roots之間沒有任何引用鏈,則該物件是不可達的。
    • 那麼可作為GC Roots的物件有
      • 虛擬機器棧(棧幀中的區域性變量表)中引用的物件
      • 方法區中類靜態屬性引用的物件
      • 方法區中常量引用的物件
      • 本地方法棧中JNI(Native方法)引用的物件

在可達性分析過程中,物件引用型別會對物件的生命週期產生影響,JAVA中有這幾種型別的引用:

    • 強引用:只要該引用還有效,GC就不會回收
    • 軟引用:記憶體空間足夠時不進行回收,在記憶體溢位發生前進行回收、用SoftReference類實現
    • 弱引用:弱引用關聯的物件只能存活到下一次Gc收集、用WeakReference類實現
    • 虛引用:無法通過虛引用獲得物件例項,也不會對物件的生存時間產生影響、唯一目的:當該物件被Gc收集時,收到一個系統通知。用PhantomReference類實現

一個物件真正不可用,要經歷兩次標記過程:

  • 首先進行可達性分析,篩選出與GC Roots沒用引用鏈的物件,進行第一次標記
  • 第一次標記後,再進行一次篩選,篩選條件是是否有必要執行finalize()方法。若物件有沒有重寫finalize()方法,或者finalize()是否已被jvm呼叫過,則沒必要執行,GC會回收該物件
  • 若有必要執行,則該物件會被放入F-Queue中,由jvm開啟一個低優先順序的執行緒去執行它(但不一定等待finalize執行完畢)。
  • Finalize()是物件最後一次自救的機會,若物件在finalize()中重新加入到引用鏈中,則它會被移出要回收的物件的集合。其他物件則會被第二次標記,進行回收

JAVA中的垃圾回收演算法有:

  • 標記-清除(Mark-Sweep)
    • 兩個階段:標記, 清除
    • 缺點:兩個階段的效率都不高;容易產生大量的記憶體碎片
  • 複製(Copying)
    • 把記憶體分成大小相同的兩塊,當一塊的記憶體用完了,就把可用物件複製到另一塊上,將使用過的一塊一次性清理掉
    • 缺點:浪費了一半記憶體
  • 標記-整理(Mark-Compact)
    • 標記後,讓所有存活的物件移到一端,然後直接清理掉端邊界以外的記憶體
  • 分代收集
    • 把堆分為新生代和老年代
    • 新生代使用複製演算法
    • 將新生代記憶體分為一塊大的Eden區和兩塊小的Survivor;每次使用Eden和一個Survivor,回收時將Eden和Survivor存活的物件複製到另一個Survivor(HotSpot的比例Eden:Survivor = 8:1)
    • 老年代使用標記-清理或者標記-整理

觸發GC又涉及到了記憶體分配規則: (物件主要分配在Eden,若啟動了本地執行緒分配緩衝,將優先在TLAB上分配)

  • 物件優先在Eden分配
    • 當Eden區沒有足夠的空間時就會發起一次Minor GC
  • 大物件直接進入老年代
    • 典型的大物件是很長的字串和陣列
  • 長期存活的物件進入老年代
    • 每個物件有年齡計數器,每經過一次GC,計數器值加一,當到達一定程度時(預設15),就會進入老年代
    • 年齡的閾值可通過引數 -XX:MaxTenuringThreshold設定
  • 物件年齡的判定
    • Survivor空間中相同年齡所有物件大小的總和大於Survivor空間的一半,年齡大於等於該年齡的物件就可直接進入老年代,無須等到MaxTenuringThreshold要求的年齡
  • 空間分配擔保
    • 發生Minor GC前,jvm會檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,若大於,則Minor GC是安全的
    • 若不大於,jvm會檢視HandlePromotionFailure是否允許擔保失敗,若不允許,則改為一次Full GC
    • 若允許擔保失敗,則檢查老年代最大可用的連續空間是否大於歷次晉升到老年代物件的平均大小,若大於,則嘗試進行Minor GC;若小於,則要改為Full GC

垃圾收集器:

Serial(序列收集器)

    • 特性:單執行緒,stop the world,採用複製演算法
    • 應用場景:jvm在Client模式下預設的新生代收集器
    • 優點:簡單高效

ParNew

    • 特點:是Serial的多執行緒版本,採用複製演算法
    • 應用場景:在Server模式下常用的新生代收集器,可與CMS配合工作

Parallel Scavenge

    • 特點:並行的多執行緒收集器,採用複製演算法,吞吐量優先,有自適應調節策略
    • 應用場景:需要吞吐量大的時候

SerialOld

    • 特點:Serial的老年代版本,單執行緒,使用標記-整理演算法

Parallel Old

    • Parallel Scavenge的老年代版本,多執行緒,標記-整理演算法

CMS

    • 特點:以最短回收停頓時間為目標,使用標記-清除演算法
    • 過程:
      • 初始標記:stop the world 標記GC Roots能直接關聯到的物件
      • 併發標記:進行GC Roots Tracing
      • 重新標記:stop the world;修正併發標記期間因使用者程式繼續運作而導致標記產生變動的 那一部分物件的標記記錄
      • 併發清除:清除物件
    • 優點:併發收集,低停頓
    • 缺點:
      • 對CPU資源敏感
      • 無法處理浮動垃圾(併發清除 時,使用者執行緒仍在執行,此時產生的垃圾為浮動垃圾)
      • 產生大量的空間碎片

G1

    • 特點:面向服務端應用,將整個堆劃分為大小相同的region。
      • 並行與併發
      • 分代收集
      • 空間整合:從整體看是基於“標記-整理”的,從區域性(兩個region之間)看是基於“複製”的。
      • 可預測的停頓:使用者可明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
    • 執行過程:
      • 初始標記:stop the world 標記GC Roots能直接關聯到的物件
      • 併發標記:可達性分析
      • 最終標記:修正在併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分標記記錄
      • 篩選回收:篩選回收階段首先對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間來制定回收計劃

GC自適應調節策略 Parallel Scavenge收集器有一個引數-XX:+UseAdaptiveSizePolicy。當這個引數開啟之後,就不需要手工指定新生代的大小、Eden與Survivor區的比例、晉升老年代物件年齡等細節引數了,虛擬機器會根據當前系統的執行情況收集效能監控資訊,動態調整這些引數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱為GC自適應的調節策略(GC Ergonomics)。

(垃圾回收器部分重點講CMS和G1)

最後提一下也會回收方法區:

  • 永久代中主要回收兩部分內容:廢棄常量和無用的類
  • 廢棄常量回收和物件的回收類似
  • 無用的類需滿足3個條件
    • 該類的所有例項物件已被回收
    • 載入該類的ClassLoader已被回收
    • 該類的Class物件沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法

上面的知識點在你多刷幾遍書,腦中形成相應的知識網後能很全面的說出來。

3、TCP和UDP的區別

  • TCP面向連線(如打電話要先撥號建立連線) UDP是無連線的,即傳送資料之前不需要建立連線
  • TCP提供可靠的服務。也就是說,通過TCP連線傳送的資料,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付
  • TCP面向位元組流,實際上是TCP把資料看成一連串無結構的位元組流;UDP是面向報文的
  • UDP沒有擁塞控制,因此網路出現擁塞不會使源主機的傳送速率降低(對實時應用很有用,如IP電話,實時視訊會議等)
  • 每一條TCP連線只能是點到點的;UDP支援一對一,一對多,多對一和多對多的互動通訊
  • TCP首部開銷20位元組;UDP的首部開銷小,只有8個位元組
  • TCP的邏輯通訊通道是全雙工的可靠通道,UDP則是不可靠通道

4、專案是怎樣預防sql注入的

回答:用的是mybatis,sql語句中用#{},#{}表示一個佔位符號,通過#{}可以實現preparedStatement向佔位符中設定值,jdbc有個預編譯的過程可以有效預防sql注入,儘量不用${},它是個拼接符,用來拼接sql字串。

  1. MySQL儲存引擎中的MyISAM和InnoDB區別

在使用MySQL的過程中對MyISAM和InnoDB這兩個概念存在了些疑問,到底兩者引擎有何分別一直是存在我心中的疑問。為了解開這個謎題,搜尋了網路,找到了如下資訊:

MyISAM是MySQL的預設資料庫引擎(5.5版之前),由早期的ISAM(Indexed Sequential Access Method:有索引的順序訪問方法)所改良。雖然效能極佳,但卻有一個缺點:不支援事務處理(transaction)。不過,在這幾年的發展下,MySQL也匯入了InnoDB(另一種資料庫引擎),以強化參考完整性與併發違規處理機制,後來就逐漸取代MyISAM。

InnoDB,是MySQL的資料庫引擎之一,為MySQL AB釋出binary的標準之一。InnoDB由Innobase Oy公司所開發,2006年五月時由甲骨文公司併購。與傳統的ISAM與MyISAM相比,InnoDB的最大特色就是支援了ACID相容的事務(Transaction)功能,類似於PostgreSQL。目前InnoDB採用雙軌制授權,一是GPL授權,另一是專有軟體授權。

MyISAM與InnoDB的區別是什麼?

儲存結構

MyISAM:每個MyISAM在磁碟上儲存成三個檔案。第一個檔案的名字以表的名字開始,副檔名指出檔案型別。.frm檔案儲存表定義。資料檔案的副檔名為.MYD (MYData)。索引檔案的副檔名是.MYI (MYIndex)。

InnoDB:所有的表都儲存在同一個資料檔案中(也可能是多個檔案,或者是獨立的表空間檔案),InnoDB表的大小隻受限於作業系統檔案的大小,一般為2GB。

儲存空間

MyISAM:可被壓縮,儲存空間較小。支援三種不同的儲存格式:靜態表(預設,但是注意資料末尾不能有空格,會被去掉)、動態表、壓縮表。

InnoDB:需要更多的記憶體和儲存,它會在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引。

可移植性、備份及恢復

MyISAM:資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作。

InnoDB:免費的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了。

事務支援

MyISAM:強調的是效能,每次查詢具有原子性,其執行數度比InnoDB型別更快,但是不提供事務支援。

InnoDB:提供事務支援事務,外部鍵等高階資料庫功能。 具有事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。

AUTO_INCREMENT

MyISAM:可以和其他欄位一起建立聯合索引。引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列,他可以根據前面幾列進行排序後遞增。

InnoDB:InnoDB中必須包含只有該欄位的索引。引擎的自動增長列必須是索引,如果是組合索引也必須是組合索引的第一列。

表鎖差異

MyISAM:只支援表級鎖,使用者在操作myisam表時,select,update,delete,insert語句都會給表自動加鎖,如果加鎖以後的表滿足insert併發的情況下,可以在表的尾部插入新的資料。

InnoDB:支援事務和行級鎖,是innodb的最大特色。行鎖大幅度提高了多使用者併發操作的新能。但是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的。

全文索引

MyISAM:支援 FULLTEXT型別的全文索引

InnoDB:不支援FULLTEXT型別的全文索引,但是innodb可以使用sphinx外掛支援全文索引,並且效果更好。

表主鍵

MyISAM:允許沒有任何索引和主鍵的表存在,索引都是儲存行的地址。

InnoDB:如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6位元組的主鍵(使用者不可見),資料是主索引的一部分,附加索引儲存的是主索引的值。

表的具體行數

MyISAM:儲存有表的總行數,如果select count(*) from table;會直接取出出該值。

InnoDB:沒有儲存表的總行數,如果使用select count(*) from table;就會遍歷整個表,消耗相當大,但是在加了wehre條件後,myisam和innodb處理的方式都一樣。

CURD操作

MyISAM:如果執行大量的SELECT,MyISAM是更好的選擇。

InnoDB:如果你的資料執行大量的INSERT或UPDATE,出於效能方面的考慮,應該使用InnoDB表。DELETE 從效能上InnoDB更優,但DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除,在innodb上如果要清空儲存有大量資料的表,最好使用truncate table這個命令。

外來鍵

MyISAM:不支援

InnoDB:支援

通過上述的分析,基本上可以考慮使用InnoDB來替代MyISAM引擎了,原因是InnoDB自身很多良好的特點,比如事務支援、儲存 過程、檢視、行級鎖定等等,在併發很多的情況下,相信InnoDB的表現肯定要比MyISAM強很多。另外,任何一種表都不是萬能的,只用恰當的針對業務型別來選擇合適的表型別,才能最大的發揮MySQL的效能優勢。如果不是很複雜的Web應用,非關鍵應用,還是可以繼續考慮MyISAM的,這個具體情況可以自己斟酌。

在MySQL資料庫中,常用的引擎主要就是2個:Innodb和MyIASM。

其他的解釋:   首先:

(1)簡單介紹這兩種引擎,以及該如何去選擇。

(2)這兩種引擎所使用的資料結構是什麼。

a.Innodb引擎,Innodb引擎提供了對資料庫ACID事務的支援。並且還提供了行級鎖和外來鍵的約束。它的設計的目標就是處理大資料容量的資料庫系統。它本身實際上是基於Mysql後臺的完整的系統。Mysql執行的時候,Innodb會在記憶體中建立緩衝池,用於緩衝資料和索引。但是,該引擎是不支援全文搜尋的。同時,啟動也比較的慢,它是不會儲存表的行數的。當進行Select count(*) from table指令的時候,需要進行掃描全表。所以當需要使用資料庫的事務時,該引擎就是首選。由於鎖的粒度小,寫操作是不會鎖定全表的。所以在併發度較高的場景下使用會提升效率的。

b.MyIASM引擎,它是MySql的預設引擎,但不提供事務的支援,也不支援行級鎖和外來鍵。因此當執行Insert插入和Update更新語句時,即執行寫操作的時候需要鎖定這個表。所以會導致效率會降低。不過和Innodb不同的是,MyIASM引擎是儲存了表的行數,於是當進行Select count(*) from table語句時,可以直接的讀取已經儲存的值而不需要進行掃描全表。所以,如果表的讀操作遠遠多於寫操作時,並且不需要事務的支援的。可以將MyIASM作為資料庫引擎的首先。

補充2點:

c.大容量的資料集時趨向於選擇Innodb。因為它支援事務處理和故障的恢復。Innodb可以利用資料日誌來進行資料的恢復。主鍵的查詢在Innodb也是比較快的。

d.大批量的插入語句時(這裡是INSERT語句)在MyIASM引擎中執行的比較的快,但是UPDATE語句在Innodb下執行的會比較的快,尤其是在併發量大的時候。

兩種引擎所使用的索引的資料結構是什麼?

答案:都是B+樹!

MyIASM引擎,B+樹的資料結構中儲存的內容實際上是實際資料的地址值。也就是說它的索引和實際資料是分開的,只不過使用索引指向了實際資料。這種索引的模式被稱為非聚集索引。

Innodb引擎的索引的資料結構也是B+樹,只不過資料結構中儲存的都是實際的資料,這種索引有被稱為聚集索引。

6、B樹與B+樹簡明扼要的區別

看了很多講B樹和B+樹的文章,大多都是圍繞各自的特性講的,第一,樹中每個結點最多含有m個孩子(m>=2);第二,……我也是從這些文章里弄懂了各種樹的聯絡與區別,要真寫,我可能還不如人家寫得好。所以就在這裡簡明扼要的用幾張圖記錄一下主要區別吧。 

  為了便於說明,我們先定義一條資料記錄為一個二元組[key,data],key為記錄的鍵值,key唯一;data為資料記錄除key外的資料。

B樹

  每個節點都儲存key和data,所有節點組成這棵樹,並且葉子節點指標為null。

B+樹

  只有葉子節點儲存data,葉子節點包含了這棵樹的所有鍵值,葉子節點不儲存指標。

  後來,在B+樹上增加了順序訪問指標,也就是每個葉子節點增加一個指向相鄰葉子節點的指標,這樣一棵樹成了資料庫系統實現索引的首選資料結構。 

  原因有很多,最主要的是這棵樹矮胖,呵呵。一般來說,索引很大,往往以索引檔案的形式儲存的磁碟上,索引查詢時產生磁碟I/O消耗,相對於記憶體存取,I/O存取的消耗要高几個數量級,所以評價一個數據結構作為索引的優劣最重要的指標就是在查詢過程中磁碟I/O操作次數的時間複雜度。樹高度越小,I/O次數越少。 

  那為什麼是B+樹而不是B樹呢,因為它內節點不儲存data,這樣一個節點就可以儲存更多的key。

  在MySQL中,最常用的兩個儲存引擎是MyISAM和InnoDB,它們對索引的實現方式是不同的。

MyISAM 

  data存的是資料地址。索引是索引,資料是資料。

InnoDB

  data存的是資料本身。索引也是資料。

  瞭解了資料結構再看索引,一切都不費解了,只是順著邏輯推而已。

  1. 解決雜湊衝突的三種方法(拉鍊法、開放地址法、再雜湊法)

上篇部落格我們說到了,什麼是雜湊衝突,其實就是再採用雜湊函式對輸入域進行對映到雜湊表的時候,因為雜湊表的位桶的數目遠小於輸入域的關鍵字的個數,所以,對於輸入域的關鍵字來說,很可能會產生這樣一種情況,也就是,一個關鍵字會對映到同一個位桶中的情況,這種情況就就叫做雜湊衝突,解決雜湊衝突的有三種方案,一種叫做拉鍊法(也叫作連結法、鏈地址法,一個意思),另外三種分別為開發地址法和再雜湊法。

(1)拉鍊法

上篇博文我們舉的例子,HashMap,HashSet其實都是採用的拉鍊法來解決雜湊衝突的,就是在每個位桶實現的時候,我們採用連結串列(jdk1.8之後採用連結串列+紅黑樹)的資料結構來去存取發生雜湊衝突的輸入域的關鍵字(也就是被雜湊函式對映到同一個位桶上的關鍵字)。首先來看使用拉鍊法解決雜湊衝突的幾個操作:

①插入操作:在發生雜湊衝突的時候,我們輸入域的關鍵字去對映到位桶(實際上是實現位桶的這個資料結構,連結串列或者紅黑樹)中去的時候,我們先檢查帶插入元素x是否出現在表中,很明顯,這個查詢所用的次數不會超過裝載因子(n/m:n為輸入域的關鍵字個數,m為位桶的數目),它是個常數,所以插入操作的最壞時間複雜度為O(1)的。

②查詢操作:和①一樣,在發生雜湊衝突的時候,我們去檢索的時間複雜度不會超過裝載因子,也就是檢索資料的時間複雜度也是O(1)的

③刪除操作:如果在拉鍊法中我們想要使用連結串列這種資料結構來實現位桶,那麼這個連結串列一定是雙向連結串列,因為在刪除一個元素x的時候,需要更改x的前驅元素的next指標的屬性,把x從連結串列中刪除。這個操作的時間複雜度也是O(1)的。

拉鍊法的優點

與開放定址法相比,拉鍊法有如下幾個優點:

①拉鍊法處理衝突簡單,且無堆積現象,即非同義詞決不會發生衝突,因此平均查詢長度較短;

②由於拉鍊法中各連結串列上的結點空間是動態申請的,故它更適合於造表前無法確定表長的情況;

③開放定址法為減少衝突,要求裝填因子α較小,故當結點規模較大時會浪費很多空間。而拉鍊法中可取α≥1,且結點較大時,拉鍊法中增加的指標域可忽略不計,因此節省空間;

④在用拉鍊法構造的散列表中,刪除結點的操作易於實現。只要簡單地刪去連結串列上相應的結點即可。

拉鍊法的缺點

指標需要額外的空間,故當結點規模較小時,開放定址法較為節省空間,而若將節省的指標空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的衝突,從而提高平均查詢速度。

使用例子:

HashMap

(2)開發地址法

開放地址法有個非常關鍵的特徵,就是所有輸入的元素全部存放在雜湊表裡,也就是說,位桶的實現是不需要任何的連結串列來實現的,換句話說,也就是這個雜湊表的裝載因子不會超過1。它的實現是在插入一個元素的時候,先通過雜湊函式進行判斷,若是發生雜湊衝突,就以當前地址為基準,根據再定址的方法(探查序列),去尋找下一個地址,若發生衝突再去尋找,直至找到一個為空的地址為止。所以這種方法又稱為再雜湊法。

有幾種常用的探查序列的方法:

①線性探查

dii=1,2,3,…,m-1;這種方法的特點是:衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表。

(使用例子:ThreadLocal裡面的ThreadLocalMap)

②二次探查

di=12,-12,22,-22,…,k2,-k2    ( k<=m/2 );這種方法的特點是:衝突發生時,在表的左右進行跳躍式探測,比較靈活。

③ 偽隨機探測

di=偽隨機數序列;具體實現時,應建立一個偽隨機數發生器,(如i=(i+p) % m),生成一個位隨機序列,並給定一個隨機數做起點,每次去加上這個偽隨機數++就可以了。

(3)再雜湊法

再雜湊法其實很簡單,就是再使用雜湊函式去雜湊一個輸入的時候,輸出是同一個位置就再次雜湊,直至不發生衝突位置

缺點:每次衝突都要重新雜湊,計算時間增加。

7、forward(轉發)和redirect(重定向)有什麼區別

forward和redirect是什麼?

是servlet種的兩種主要的跳轉方式。forward又叫轉發,redirect叫做重定向。

forward(轉發):

1.是伺服器內部的重定向,伺服器直接訪問目標地址的 url網址,把裡面的東西讀取出來,但是客戶端並不知道,因此用forward的話,客戶端瀏覽器的網址是不會發生變化的。

2.關於request: 由於在整個定向的過程中用的是同一個request,因此forward會將request的資訊帶到被重定向的jsp或者servlet中使用。

redirect(重定向):

1.是客戶端的重定向,是完全的跳轉。即伺服器返回的一個url給客戶端瀏覽器,然後客戶端瀏覽器會重新發送一次請求,到新的url裡面,因此瀏覽器中顯示的url網址會發生變化。

2.因為這種方式比forward多了一次網路請求,因此效率會低於forward。

8、set、list、map

List,Set,Map是否繼承自Collection介面? 

答:List,Set是,Map不是。 Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java JDK不能提供直接繼承自Collection的類,Java JDK提供的類都是繼承自Collection的"子介面",如:List和Set。 

注意:Map沒有繼承Collection介面,Map提供key到value的對映。一個Map中不能包含相同key,每個key只能對映一個value。Map介面提供3種集合的檢視,Map的內容可以被當做一組key集合,一組value集合,或者一組key-value對映。 

List按物件進入的順序儲存物件,不做排序或編輯操作。Set對每個物件只接受一次,並使用自己內部的排序方法(通常,你只關心某個元素是否屬於Set,而不關心它的順序--否則應該使用List)。Map同樣對每個元素儲存一份,但這是基於"鍵"的,Map也有內建的排序,因而不關心元素新增的順序。如果新增元素的順序對你很重要,應該使用 LinkedHashSet或者LinkedHashMap.

詳細介紹: 

List特點:元素有放入順序,元素可重複 

Map特點:元素按鍵值對儲存,無放入順序 

Set特點:元素無放入順序,元素不可重複(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的) 

List介面有三個實現類:LinkedList,ArrayList,Vector 

LinkedList:底層基於連結串列實現,連結串列記憶體是散亂的,每一個元素儲存本身記憶體地址的同時還儲存下一個元素的地址。連結串列增刪快,查詢慢 

ArrayList和Vector的區別:ArrayList是非執行緒安全的,效率高;Vector是基於執行緒安全的,效率低 

Set介面有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet 

SortedSet介面有一個實現類:TreeSet(底層由平衡二叉樹實現) 

Query介面有一個實現類:LinkList 

Map介面有三個實現類:HashMap,HashTable,LinkeHashMap 

  HashMap非執行緒安全,高效,支援null;HashTable執行緒安全,低效,不支援null 

SortedMap有一個實現類:TreeMap 

其實最主要的是,list是用來處理序列的,而set是用來處理集的。Map是知道的,儲存的是鍵值對 

set 一般無序不重複.map kv 結構 list 有序 。

List的功能方法

  實際上有兩種List: 一種是基本的ArrayList,其優點在於隨機訪問元素,另一種是更強大的LinkedList,它並不是為快速隨機訪問設計的,而是具有一套更通用的方法。

List : 次序是List最重要的特點:它保證維護元素特定的順序。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這隻推薦LinkedList使用。)一個List可以生成ListIterator,使用它可以從兩個方向遍歷List,也可以從List中間插入和移除元素。

ArrayList : 由陣列實現的List。允許對元素進行快速隨機訪問,但是向List中間插入與移除元素的速度很慢。ListIterator只應該用來由後向前遍歷ArrayList,而不是用來插入和移除元素。因為那比LinkedList開銷要大很多。

LinkedList : 對順序訪問進行了優化,向List中間插入與刪除的開銷並不大。隨機訪問則相對較慢。(使用ArrayList代替。)還具有下列方法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何介面或基類中定義過)使得LinkedList可以當作堆疊、佇列和雙向佇列使用。

Set的功能方法

Set具有與Collection完全一樣的介面,因此沒有任何額外的功能,不像前面有兩個不同的List。實際上Set就是Collection,只是行為不同。(這是繼承與多型思想的典型應用:表現不同的行為。)Set不儲存重複的元素(至於如何判斷元素相同則較為負責)

Set : 存入Set的每個元素都必須是唯一的,因為Set不儲存重複元素。加入Set的元素必須定義equals()方法以確保物件的唯一性。Set與Collection有完全一樣的介面。Set介面不保證維護元素的次序。

HashSet : 為快速查詢設計的Set。存入HashSet的物件必須定義hashCode()。

TreeSet : 儲存次序的Set, 底層為樹結構。使用它可以從Set中提取有序的序列。

LinkedHashSet : 具有HashSet的查詢速度,且內部使用連結串列維護元素的順序(插入的次序)。於是在使用迭代器遍歷Set時,結果會按元素插入的次序顯示。

Map的功能方法

  方法put(Object key, Object value)新增一個“值”(想要得東西)和與“值”相關聯的“鍵”(key)(使用它來查詢)。方法get(Object key)返回與給定“鍵”相關聯的“值”。可以用containsKey()和containsValue()測試Map中是否包含某個“鍵”或“值”。標準的Java類庫中包含了幾種不同的Map:HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap。它們都有同樣的基本介面Map,但是行為、效率、排序策略、儲存物件的生命週期和判定“鍵”等價的策略等各不相同。

  執行效率是Map的一個大問題。看看get()要做哪些事,就會明白為什麼在ArrayList中搜索“鍵”是相當慢的。而這正是HashMap提高速度的地方。HashMap使用了特殊的值,稱為“雜湊碼”(hash code),來取代對鍵的緩慢搜尋。“雜湊碼”是“相對唯一”用以代表物件的int值,它是通過將該物件的某些資訊進行轉換而生成的。所有Java物件都能產生雜湊碼,因為hashCode()是定義在基類Object中的方法。

HashMap就是使用物件的hashCode()進行快速查詢的。此方法能夠顯著提高效能。

Map : 維護“鍵值對”的關聯性,使你可以通過“鍵”查詢“值”

HashMap : Map基於散列表的實現。插入和查詢“鍵值對”的開銷是固定的。可以通過構造器設定容量capacity和負載因子load factor,以調整容器的效能。

LinkedHashMap : 類似於HashMap,但是迭代遍歷它時,取得“鍵值對”的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點。而在迭代訪問時發而更快,因為它使用連結串列維護內部次序。

TreeMap : 基於紅黑樹資料結構的實現。檢視“鍵”或“鍵值對”時,它們會被排序(次序由Comparabel或Comparator決定)。TreeMap的特點在於,你得到的結果是經過排序的。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個子樹。

WeakHashMao : 弱鍵(weak key)Map,Map中使用的物件也被允許釋放: 這是為解決特殊問題設計的。如果沒有map之外的引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收。

IdentifyHashMap : 使用==代替equals()對“鍵”作比較的hash map。專為解決特殊問題而設計。

  1. HashMap、Hashtable、ConcurrentHashMap的原理與區別

如果你去面試,面試官不問你這個問題,你來找我^_^

下面直接來乾貨,先說這三個Map的區別:

HashTable

  • 底層陣列+連結串列實現,無論key還是value都不能為null,執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關優化
  • 初始size為11,擴容:newsize = olesize*2+1
  • 計算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 底層陣列+連結串列實現,可以儲存null鍵和null值,執行緒不安全
  • 初始size為16,擴容:newsize = oldsize*2,size一定為2的n次冪
  • 擴容針對整個Map,每次擴容時,原來陣列中的元素依次重新計算存放位置,並重新插入
  • 插入元素後才判斷該不該擴容,有可能無效擴容(插入後如果擴容,如果沒有再次插入,就會產生無效擴容)
  • 當Map中元素總數超過Entry陣列的75%,觸發擴容操作,為了減少連結串列長度,元素分配更均勻
  • 計算index方法:index = hash & (tab.length – 1)

HashMap的初始值還要考慮載入因子:

  •  雜湊衝突:若干Key的雜湊值按陣列大小取模後,如果落在同一個陣列下標上,將組成一條Entry鏈,對Key的查詢需要遍歷Entry鏈上的每個元素執行equals()比較。
  • 載入因子:為了降低雜湊衝突的概率,預設當HashMap中的鍵值對達到陣列大小的75%時,即會觸發擴容。因此,如果預估容量是100,即需要設定100/0.75=134的陣列大小。
  • 空間換時間:如果希望加快Key查詢的時間,還可以進一步降低載入因子,加大初始大小,以降低雜湊衝突的概率。

HashMap和Hashtable都是用hash演算法來決定其元素的儲存,因此HashMap和Hashtable的hash表包含如下屬性:

  • 容量(capacity):hash表中桶的數量
  • 初始化容量(initial capacity):建立hash表時桶的數量,HashMap允許在構造器中指定初始化容量
  • 尺寸(size):當前hash表中記錄的數量
  • 負載因子(load factor):負載因子等於“size/capacity”。負載因子為0,表示空的hash表,0.5表示半滿的散列表,依此類推。輕負載的散列表具有衝突少、適宜插入與查詢的特點(但是使用Iterator迭代元素時比較慢)

除此之外,hash表裡還有一個“負載極限”,“負載極限”是一個0~1的數值,“負載極限”決定了hash表的最大填滿程度。當hash表中的負載因子達到指定的“負載極限”時,hash表會自動成倍地增加容量(桶的數量),並將原有的物件重新分配,放入新的桶內,這稱為rehashing。

HashMap和Hashtable的構造器允許指定一個負載極限,HashMap和Hashtable預設的“負載極限”為0.75,這表明當該hash表的3/4已經被填滿時,hash表會發生rehashing。

“負載極限”的預設值(0.75)是時間和空間成本上的一種折中:

  • 較高的“負載極限”可以降低hash表所佔用的記憶體空間,但會增加查詢資料的時間開銷,而查詢是最頻繁的操作(HashMap的get()與put()方法都要用到查詢)
  • 較低的“負載極限”會提高查詢資料的效能,但會增加hash表所佔用的記憶體開銷

程式猿可以根據實際情況來調整“負載極限”值。

ConcurrentHashMap

  • 底層採用分段的陣列+連結串列實現,執行緒安全
  • 通過把整個Map分為N個Segment,可以提供相同的執行緒安全,但是效率提升N倍,預設提升16倍。(讀操作不加鎖,由於HashEntry的value變數是 volatile的,也能保證讀取到最新的值。)
  • Hashtable的synchronized是針對整張Hash表的,即每次鎖住整張表讓執行緒獨佔,ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術
  • 有些方法需要跨段,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢後,又按順序釋放所有段的鎖
  • 擴容:段內擴容(段內元素超過該段對應Entry陣列長度的75%觸發擴容,不會對整個Map進行擴容),插入前檢測需不需要擴容,有效避免無效擴容

Hashtable和HashMap都實現了Map介面,但是Hashtable的實現是基於Dictionary抽象類的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴充套件性更好。

HashMap基於雜湊思想,實現對資料的讀寫。當我們將鍵值對傳遞給put()方法時,它呼叫鍵物件的hashCode()方法來計算hashcode,然後找到bucket位置來儲存值物件。當獲取物件時,通過鍵物件的equals()方法找到正確的鍵值對,然後返回值物件。HashMap使用連結串列來解決碰撞問題,當發生碰撞時,物件將會儲存在連結串列的下一個節點中。HashMap在每個連結串列節點中儲存鍵值對物件。當兩個不同的鍵物件的hashcode相同時,它們會儲存在同一個bucket位置的連結串列中,可通過鍵物件的equals()方法來找到鍵值對。如果連結串列大小超過閾值(TREEIFY_THRESHOLD,8),連結串列就會被改造為樹形結構。

在HashMap中,null可以作為鍵,這樣的鍵只有一個,但可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,即可以表示HashMap中沒有該key,也可以表示該key所對應的value為null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個key,應該用containsKey()方法來判斷。而在Hashtable中,無論是key還是value都不能為null。

Hashtable是執行緒安全的,它的方法是同步的,可以直接用在多執行緒環境中。而HashMap則不是執行緒安全的,在多執行緒環境中,需要手動實現同步機制。

Hashtable與HashMap另一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它執行緒改變了HashMap的結構(增加或者移除元素),將會丟擲ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會丟擲ConcurrentModificationException異常。但這並不是一個一定發生的行為,要看JVM。

先看一下簡單的類圖:

從類圖中可以看出來在儲存結構中ConcurrentHashMap比HashMap多出了一個類Segment,而Segment是一個可重入鎖。

ConcurrentHashMap是使用了鎖分段技術來保證執行緒安全的。

鎖分段技術:首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。 

ConcurrentHashMap提供了與Hashtable和SynchronizedMap不同的鎖機制。Hashtable中採用的鎖機制是一次鎖住整個hash表,從而在同一時刻只能由一個執行緒對其進行操作;而ConcurrentHashMap中則是一次鎖住一個桶。

ConcurrentHashMap預設將hash表分為16個桶,諸如get、put、remove等常用操作只鎖住當前需要用到的桶。這樣,原來只能一個執行緒進入,現在卻能同時有16個寫執行緒執行,併發效能的提升是顯而易見的。