前言
在Java編程語言中,最基本的結構就是兩種,一種是數組,一種是模擬指針(引用),所有的數據結構都可以用這兩個基本結構構造。
本文主題就是通過數組的方式實現Map的key-value存儲(以下稱爲ArrayMap),然後在後續章節通過ArrayMap與Java自帶的HashMap進行對比,來直觀的瞭解Hash算法相對比數組存儲性能的優勢。
實現思路
ArrayMap的主要功能在Map接口中已經有過定義,實現Map接口後繼承AbstractMap,避免所有功能都得重新寫,完善功能部分功能。主要需要完善的功能如下:
- V put(K key, V value): 放置key-value對象,同時要考慮到數組的動態擴容。
- V get(Object key): 根據key獲取value。
- int size():返回Map中元素的數量。AbstractMap中的size是需要把Map全部轉化成Set然後計算size,比較消耗性能。ArrayMap中通過成員變量記錄下map的元素數量。
- void clear():清空map。
- entrySet(): 將元素轉化成一個Set集合。
實現代碼
public class ArrayMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
/**
* 默認數組長度
*/
private static final int DEFAULT_LENGTH = 16;
/**
* 數據存儲的數組
*/
private Node[] tables;
/**
* map內元素的數量
*/
private int index;
public ArrayMap() {
this(DEFAULT_LENGTH);
}
/**
* @param length 初始化長度
*/
public ArrayMap(int length) {
if (length <= 0) {
throw new IllegalArgumentException("非法參數");
}
tables = new Node[length];
}
/**
* 從Map中獲取元素
*
* @param key 獲取的對象
* @return 查詢得到的對象,若是不存在,則返回Null
*/
@Override
public V get(Object key) {
V value = null;
for (int i = 0; i < index; i++) {
Node<K, V> node = tables[i];
K nodeKey = node.getKey();
if ((nodeKey == null && key == null) || (key != null && key.equals(nodeKey))) {
return node.getValue();
}
}
return value;
}
/**
* 在Map中放置Key-Value對
*
* @param key 放置的Key
* @param value 放置的Value
* @return 返回value
*/
@Override
public V put(K key, V value) {
if (index >= tables.length) {
throw new ArrayIndexOutOfBoundsException("數組越界");
}
for (int i = 0; i < index; i++) {
Node<K, V> node = tables[i];
K nodeKey = node.getKey();
if ((nodeKey == null && key == null) || (key != null && key.equals(nodeKey))) {
node.setValue(value);
return value;
}
}
tables[index] = new Node(key, value);
if (++index == tables.length) {
resize();
}
return value;
}
@Override
public int size() {
return index;
}
@Override
public void clear() {
index = 0;
int parLength = tables.length;
tables = new Node[parLength];
}
/**
* 尚未實現的方法
*
* @return null
*/
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> set = new HashSet<>();
for (int i = 0; i < index; i++) {
Node<K, V> node = tables[i];
set.add(node);
}
return set;
}
/**
* 數組擴容
*/
private void resize() {
int parLength = tables.length;
Node<K, V>[] newTable = new Node[parLength * 3 / 2];
for (int i = 0; i < parLength; i++) {
newTable[i] = tables[i];
}
tables = newTable;
}
class Node<K, V> implements Entry<K, V> {
private K key;
private V value;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
this.value = value;
return value;
}
@Override
public String toString() {
return key + "=" + value;
}
}
}
單元測試:
public class ArrayMapTest {
@Test
public void testArray(){
Map<String, String> map = new ArrayMap<>();
for(int i=0;i<10;i++){
map.put("floow"+i,"value"+i);
}
map.put("floow1","33");
map.put(null,"tt");
map.put("tt",null);
//測試put/get
Assert.assertEquals("tt",map.get(null));
Assert.assertEquals(null,map.get("tt"));
Assert.assertEquals("33",map.get("floow1"));
//測試size()
Assert.assertEquals(12,map.size());
//測試clear()
map.clear();
Assert.assertEquals(0,map.size());
Assert.assertTrue(map.isEmpty());
}
@Test
public void testArrayIterator(){
Map<String, String> map = new ArrayMap<>();
for(int i=0;i<10;i++){
map.put("floow"+i,"value"+i);
}
Set<Map.Entry<String,String>> entrySet = map.entrySet();
Assert.assertEquals(10,entrySet.size());
}
}
ArrayMap vs HashMap
實現好ArrayMap後,我們需要來對比下ArrayMap和HashMap的性能差別。首先從實現方式上看,ArrayMap就已經看出很多性能問題了,比如put和get都需要遍歷,但是究竟差多少這個需要通過測試纔能有比較直觀的認知。
1.put測試
測試需要考慮Map初始數據量以及操作次數,通過小、中、大三個級別的初始數量來對比下操作消耗時間。
A:ArrayMap
H:HashMap
時間單位:ms
測試次數 | A(1K) | H(1K) | A(1w) | H(1w) | A (10w) | H(10w) |
---|---|---|---|---|---|---|
1k次 | <1 | <1 | 5 | <1 | 58 | <1 |
1w次 | 30 | <1 | 63 | <1 | 735 | 1 |
10w次 | 4827 | 5 | 5150 | 6 | 10994 | 5 |
2.Get測試
A:ArrayMap
H:HashMap
時間單位:ms
測試次數 | A(1K) | H(1K) | A(1w) | H(1w) | A (10w) | H(10w) |
---|---|---|---|---|---|---|
1k次 | 9 | 1 | 7 | <1 | 52 | <1 |
1w次 | 4 | <1 | 41 | <1 | 546 | 1 |
10w次 | 35 | 2 | 516 | 1 | 5320 | 3 |
從性能測試的結果看,數組實現的ArrayMap較HashMap有較大的性能差距,並且隨着Map中的元素數量增多,差距越大。
HashMap的性能受元素數量的影響不大,可能是由於hash碰撞較少,因此性能接近O(1)。