Java的資料結構
一、Collection
Collection介面有List和Set兩個子介面。
1、List
- ArrayList
- 底層動態資料結構,可儲存重複元素,儲存有序。
- 執行緒不安全的,效率高。
- 初始化一個ArrayList時,不指定容量,預設會建立一個容量10的陣列。隨著新增元素,達到陣列容量上限時,可自動擴容一倍的容量。
- 查詢快,增刪慢。底層動態陣列,陣列的元素在記憶體中地址是連續的。查詢時可直接通過陣列索引找到結果。增刪時,由於是對陣列操作,每增加或刪除元素,陣列內所有的元素記憶體地址都會變動,會比較慢。
- LinkedList
- 底層雙向連結串列資料結構,可儲存重複元素,儲存有序。
- 連結串列上的節點是一個Node,Node中item是當前節點的值,prev指向上一節點,next指向下一節點。
- 查詢慢,增刪快。因為LindedList底層實現是連結串列,連結串列的每個節點在記憶體中的儲存地址是隨機非連續的,節點除了記錄自己的值還要記錄下一個節點甚至上一個節點的指標。查詢時只能遍歷連結串列,equals比較值,找到結果,會比較慢。增加元素,只需要在連結串列末尾新增一個節點,並將上一個節點的next指標指向新增的節點即可。刪除元素,先遍歷連結串列插到要刪除的節點,然後置空此節點,將前後節點的指標關聯起來。
- Vector
- 和ArrayList特性基本相似,執行緒安全的,效率低。
- 還有個子類Stack,一般可用於棧的管理。push方法新增一個物件到棧頂,pop方法彈出棧頂的物件,peek方法檢視棧頂的物件。
2、Set
- HashSet
- 基於HashMap實現,HashSet的值就是HashMap的key,value是PRESENT物件。
- 不可儲存重複元素,儲存無序。
- TreeSet
- 底層紅黑樹資料結構。
- 不可儲存重複元素,儲存有序。有基於元素key的高效排序演算法,儲存順序從小到大。
二、Map
- HashMap
- 執行緒不安全的,效率略高,集中了ArrayList和LinkedList的優點。
- 儲存無需,key值唯一,可儲存一組null。
- HashMap底層是一個hash表,元素為單項鍊表的陣列,jdk1.8之前資料的元素是連結串列,jdk1.8之後,若連結串列長度達到8時,則會將連結串列置換為紅黑樹。
- 初始化HashMap時,若不傳入容量,預設會生成長度16,載入因子0.75的陣列。隨著陣列儲存長度達到16*0.75時,會自動擴容一倍的容量。
- HashMap中,是以鍵值對的形式儲存,通過key的hashcode值,決定該組鍵值對在陣列的具體位置,然後找到對應的單連結串列。每組鍵值對,jdk1.8之前封裝成Entry,jdk1.8之後封裝成了Node,Node中包含key的hash值,key值,value值,下一個節點的指標。
- HashMap當put鍵值對時,首先算出key的hashcode值,去遍歷陣列找對應的下標,找不到,則會在陣列末尾新增一組,儲存該節點。找到了對應下標後,則會遍歷這個位置的連結串列,根據key值判斷是否已有節點,有的話直接用新值覆蓋掉舊值,若沒有節點,在連結串列的末尾儲存該節點。
- 之所以使用單連結串列儲存,是因為儲存的鍵值對越來越多,就有可能出現key生成的hashcode一樣,造成hash衝突,此時,即可把新新增的鍵值對存在這個陣列位置對應的單連結串列內。單連結串列裡的每個節點,key的hashcode是相同的,但是key值不同,他們在陣列的同一個索引位置。
- TreeMap
- 有序的key-value集合,基於紅黑樹實現,不能儲存null。
- 執行緒不安全的。
- Hashtable
- 和HashMap功能基本相似。
- 但執行緒安全的,效率低。
Java基本演算法、排序方式
- 選擇排序:雙重for迴圈,每一趟排序獲取最小數,依次將最小數的位置放在已排好序的序列最後。
int[] arr = {6, 4, 9, 1, 5, 3};
for(int i = 0; i < arr.length - 1; i++) {
int k = i;
for(int j = k + 1; j < arr.length; j++) { // 選最小的值
if(arr[j] < arr[k]) {
k = j; // 記下目前找到的最小值所在的位置
}
}
// 找到本輪迴圈的最小的數以後,再進行交換
if(i != k) { // 交換a[i]和a[k]
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
複製程式碼
- 氣泡排序:雙重for迴圈,依次比較相鄰的兩個元素,將小數放在前面,大數放在後面。
int[] arr = {6, 4, 9, 1, 5, 3};
int temp;
for(int i = 0; i < arr.length - 1; i++) { // 外層迴圈控制排序趟數
for(int j = 0; j < arr.length - i - 1; j++) { // 內層迴圈控制每一趟排序多少次,每一次迴圈把大數滾到佇列末尾
if(arr[j + 1] < arr[j]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
複製程式碼
- 快速排序:選擇一個關鍵值作為基準值。比基準值小的都在左邊序列,比基準值大的都在右邊序列。時間複雜度為O(NlogN)。
public static int[] quickStore(int[] array, int l, int r) {
if (l < r) {
int i = l;
int j = r;
int x = array[l];
while (i < j) {
while (i < j && array[j] >= x) {
// 從右向左找第一個小於x的數
j--;
}
if (i < j) {
array[i++] = array[j];
}
while (i < j && array[i] < x) { // 從左向右找第一個大於等於x的數
i++;
}
if (i < j) {
array[j--] = array[i];
}
}
array[i] = x;
quickStore(array, l, i - 1);
quickStore(array, i + 1, r);
}
return array;
}
複製程式碼
- 二分查詢法:對有序數列查詢,查詢時,先以有序數列的中點位置為比較物件,如果要找的元素值小於該中點元素,則將待查序列縮小為左半部分,否則為右半部分。通過一次比較,將查詢區間縮小一半。如此遞迴執行,直至查詢到物件。
// 二分查詢遞迴實現
public static int binSearch(int array[], int start, int end, int key) {
int mid = (end - start) / 2 + start;
if (array[mid] == key) {
return mid;
}
if (start >= end) {
return -1;
} else if (key > array[mid]) {
return binSearch(array, mid + 1, end, key);
} else if (key < array[mid]) {
return binSearch(array, start, mid - 1, key);
}
return -1;
}
// 二分查詢普通迴圈實現
public static int binSearch(int array[], int key) {
int mid = array.length / 2;
if (key == array[mid]) {
return mid;
}
int start = 0;
int end = array.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (key < array[mid]) {
end = mid - 1;
} else if (key > array[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
複製程式碼