也叫散列、哈希。html
主要用於信息安全領域中的算法,把長度不一樣的信息轉化爲雜亂的128位的編碼,找到一種數據內容與地址之間的映射關係。java
注意:不一樣的輸入可能會散列成相同的輸出node
咱們最熟悉的Object類中就提供了hashcode的方法。算法
public native int hashCode();
Java集合的實現底層大都是基本數據結構的又一層封裝。數組
數組:尋址容易,插入和刪除困難安全
鏈表正好相反。數據結構
HashMap正好將兩者互補了一下,推出了鏈表+數組的組合方式,也叫鏈表散列、「拉鍊法」。性能
結構示意圖:測試
放入元素時,根據key值經過hashcode找到對應數組的位置,放入橫向數組的某個格子中。由於前面說到hashcode值不能保證惟一,若是以後hashcode值對應的數組位置中已經有值,就放到相連的鏈表中。優化
查找元素也是按這個過程來進行。
代碼實現:
注意:每一個Node中都持有下一個節點的引用。
由上面的數據結構介紹,能夠看出,在查找的時候,儘可能避免查找鏈表可以大大提升存取效率。
目標:元素儘量均勻分佈,這樣查找的時候沒必要查找鏈表,效率很高。
思路一:
取模運算,實現是能夠實現,但取模運算消耗大、效率不高。
思路二:
首先,&運算比取模運算效率高。
hashmap採用的是下面這種與運算。
大同小異,都是爲了減小碰撞,避免hash到同一個位置,使元素分佈更均勻。在實現的基礎上,考慮性能問題。
ArrayList和LinkedList的大體區別以下:
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
2.對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。
3.對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。
上代碼:
static final int N=50000; static long timeList(List list){ long start=System.currentTimeMillis(); Object o = new Object(); for(int i=0;i<N;i++) { list.add(0, o); } return System.currentTimeMillis()-start; } static long readList(List list){ long start=System.currentTimeMillis(); for(int i=0,j=list.size();i<j;i++){ } return System.currentTimeMillis()-start; } static List addList(List list){ Object o = new Object(); for(int i=0;i<N;i++) { list.add(0, o); } return list; } public static void main(String[] args) { System.out.println("ArrayList添加"+N+"條耗時:"+timeList(new ArrayList())); System.out.println("LinkedList添加"+N+"條耗時:"+timeList(new LinkedList())); List list1=addList(new ArrayList<>()); List list2=addList(new LinkedList<>()); System.out.println("ArrayList查找"+N+"條耗時:"+readList(list1)); System.out.println("LinkedList查找"+N+"條耗時:"+timeList(list2)); }
當咱們在集合中裝5萬條數據,測試運行結果以下:
顯然咱們能夠看出ArrayList更適合讀取數據,linkedList更多的時候添加或刪除數據。
ArrayList內部是使用可増長數組實現的,因此是用get和set方法是花費常數時間的,可是若是插入元素和刪除元素,除非插入和刪除的位置都在表末尾,不然代碼開銷會很大,由於裏面須要數組的移動。
LinkedList是使用雙鏈表實現的,因此get會很是消耗資源,除非位置離頭部很近。可是插入和刪除元素花費常數時間。
鏈表結構,一般包含表頭,節點1,節點2...節點n,其中節點又包含了數據內容和下個節點的地址。和數組結構(應該叫作順序表吧大概......)不同,鏈表並不用佔據連續的內存,它們的區別就很少說了,相信你們都知道。
說說怎麼實現吧,既然要用引用的方式來代替指針,那麼就須要一個特別的類結構:須要同名的成員保存下一個節點的信息。
public class Node { private String data; private Node nextNode; public String getData() { return data; } public void setData(String data) { this.data = data; } public Node getNextNode() { return nextNode; } public void setNextNode(Node nextNode) { this.nextNode = nextNode; } }
該怎麼使用呢?讓咱們來初始化一個鏈表吧!
private Node InitNode() { // 當前節點 Node curNode = new Node(); // 構建頭結點 Node head = new Node(); head.setData("head"); head.setNextNode(null); // 當前節點位於頭結點 curNode = head; // 新增第一個節點 Node n1 = new Node(); // 獲取到當前節點,使得的下一個節點設置爲n1 curNode.setNextNode(n1); n1.setData("node1"); n1.setNextNode(null); // 當前節點位於第一個節點 curNode = n1; // 第二個節點 Node n2 = new Node(); curNode.setNextNode(n2); n2.setData("node2"); n2.setNextNode(null); curNode = n2; // 第三個節點 Node n3 = new Node(); curNode.setNextNode(n3); n3.setData("node3"); n3.setNextNode(new Node()); curNode = n3; // 第四個節點 Node n4 = new Node(); curNode.setNextNode(n4); n4.setData("node4"); n4.setNextNode(new Node()); curNode = n4; return head; }
注意curNode的變更,使得當前節點總落在最後一個節點上,下次插入時就不須要知道前面一個節點的名字了,經過curNode就能夠直接插入了。
到底成功了沒有,咱們來遍歷一下。
LinkMain m = new LinkMain(); Node testNode = m.InitNode(); Node iter = testNode.getNextNode(); while (null != iter) { if (null != iter.getData()) { System.out.println(iter.getData()); } iter = iter.getNextNode(); }
輸出結果以下:,
其中testNode是這樣的:
----------------------------分割線---------------------------------------
OK,搞定了初始化和遍歷,讓咱們來試試插入一個節點吧,需求是在某個鏈表中,第N個位置插入一個節點temp:
新增原理:對於temp節點來講
紅色表明的是以前鏈接,黑色的是以後應該作的。
private Node addNode(Node head, int n, Node temp) { int i = 0; while (null != head) { if (i == n) { temp.setNextNode(head.getNextNode()); head.setNextNode(temp); return head; } else { head = head.getNextNode(); i++; } } return head; }
新增後再遍歷一下
// 新增一個節點
Node temp = new Node(); temp.setData("tempNode"); temp.setNextNode(null); Node n3 = m.addNode(testNode, 2, temp); iter = testNode.getNextNode(); while (null != iter) { if (null != iter.getData()) { System.out.println(iter.getData()); } iter = iter.getNextNode(); }
那麼效果如何呢?
其中testNode的內容應該是
OK,結果正確。
----------------------------分割線---------------------------------------
鏈表的刪除節點功能
一開始還搞不定刪除,後來畫圖分析了下,終於解決,放上代碼。
難點是設置P,Q兩點的指向。其中q是p的下個節點。
應該先找到須要刪除的位置。
原理很簡單,就是要繞過q來鏈接p和後後(q後面的節點)節點。
刪除前:
刪除後: