棧:先進后出(底層用數組實現)
棧只有一個開口,先進去的就到最底下,后進來的就在前面,要是拿出去的話,肯定是從開口端拿出去,
所以說先進后出,后進先出。
數據結構:
java實現棧(基於數組):
/** * 棧只有一個開口,先進去的就到最底下,后進來的就在前面,要是拿出去的話,肯定是從開口端拿出去, * 所以說先進后出,后進先出。 */ public class MyStack { //底層實現是一個數組 private long[] arr; /** * 最上層的一個指針 棧頂 */ private int top; /** * 默認的構造方法 */ public MyStack(){ arr = new long[10]; top=-1; } /** * 增加數據 先把指針加一 * 然后指針指向最新的數據 */ public void push(int value){ //top++是top先不自加,在語句完后自加。| ++top先自加 然后使用top的值 arr[++top] = value; } /** * 移除數據 先把最頂層指針執行的數據return 然后指針減一 */ public long pop(){ //--top 是先執行top=top-1,然后再使用top的值 | top-- 是先使用top的值 然后 在執行top=top-1 return arr[top--]; } public static void main(String[] args) { MyStack myStack = new MyStack(); myStack.push(1); myStack.push(2); myStack.push(3); System.out.println(myStack.pop()); } }
main()方法中 3是最后push()進去 是最先pop()出來,先進后出,后進先出。
隊列:先進先出(底層用數組實現)
隊列有隊頭(front)和隊尾(end),數據從隊尾進入隊列,從隊頭出隊列,隊頭(front)指向隊列的第一個數據,隊尾(end)指向隊列中的最后一個數據。
隊列的數據結構
java實現隊列(基於數組)
package javaee.net.cn.tree.ch10; public class MyQueue { //底層使用數組 private long[] arr; //隊頭 private int front; //隊尾 private int end; /** * 默認構造方法 */ public MyQueue(){ arr = new long[10]; front=0; end = -1; } /** * 添加數據 從隊尾插入 */ public void insert(long value){ arr[++end] = value; } /** * 刪除數據,從隊頭刪除 */ public long remove(){ return arr[front++]; } public static void main(String[] args) { MyQueue myQueue = new MyQueue(); myQueue.insert(1L); myQueue.insert(2L); System.out.println(myQueue.remove()); } }
main()方法中 1是先insert()進去 也是先remove()出來,所以說 先進先出。
鏈表實現隊列
JDK中的LinkedList(底層用鏈表實現)
以上隊列是以數組來模擬(比較簡單),JDK API中的Queue隊列底層是以鏈表來實現的。
LinkedList 類實現了Deque(雙端隊列)接口 並且具有
addFirst() addLast() getFirst() getLast() removeFirst() removeLast() 方法
使得LinkList可以作為 棧 隊列 雙向隊列使用
至於LinkedList的實現原理,在這就不做多敘述了 大家可以參考API
鏈表
上面的棧和隊列都是以數組模擬實現,但是JDK中LinkedList底層是鏈表實現的,現在我們看看什么是鏈表
鏈表是由一個個的節點構成的,節點(Node)分為兩個部分,第一個部分(data)保存或者顯示關於節點的信息,另一個部分存儲下一個節點的地址。
鏈表最后一個節點存儲地址的部分指向空值。
節點Node:
/** * 連接點 相當於是車廂 */ public class Node { //數據域 保存數據 public long data; //指針域 保存下一個節點的引用 public Node next; public Node(long value){ this.data=value; } }
而插入一個節點,對於單向鏈表,我們只提供在鏈表頭插入,只需要將next指向原頭節點,當前插入的節點設置為頭節點即可。
/** * 插入一個節點,在頭結點進行插入 */ public void insertFirst(long value){ Node node = new Node(value); node.next=first; first=node; }
刪除一個節點,我們將該節點的上一個節點的next指向該節點的下一個節點。
如果被刪除的是頭結點,直接將頭節點的下一個節點,賦值給頭節點
/** * 刪除方法 根據數據域進行刪除 */ public Node delete(long value){ Node current = first; Node pre = null; while(current.data!=value){ if(current.next==null){ return null; } pre = current; current= current.next; } if(current == first){ first = first.next; }else{ pre.next=current.next; } return current; }
BlockingQueue
普通的隊列沒有考慮線程之間的同步,當多個線程操作同一個隊列時,會導致並發問題。
BlockingQueue接口繼承了Queue接口,BlockingQueue接口為多個線程操作同一個隊列提供了四種處理方案(,對於不能立即滿足但可能在將來某一時刻可以滿足的操作)
拋出異常 | 特殊值 | 阻塞 | 超時 | |
插入 | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
移除 | remove() |
poll() |
take() |
poll(time, unit) |
檢查 | element() |
peek() |
不可用 | 不可用 |
其中方案一和方案二來自Queue接口,方案三和方案四是在BlockingQueue接口中新增的。
比如線程阻塞:
在通過put(e)方法向隊列尾部添加元素時,如果隊列已滿,則當前線程進入阻塞狀態,直到隊列有剩余容量來添加元素,才退出阻塞。
在通過take()方法從隊列頭部刪除並返回元素時,如果隊列為空,則當前線程進入阻塞狀態,直到從隊列中成功刪除並返回元素為止。
在java.util.concurrent包中 BlockingQueue接口主要有以下實現類