本节主要介绍键值对表的基本实现方法。
链表法
一种方法就是用链表进行实现。这种方法的基本思想就是用链表储存键值对。当需要寻找一个值时,扫描整个链表,如果有匹配的键,就返回对应的值。当需要插入一个值时,扫描整个链表,如果能够找到匹配的键,就覆盖对应的值,如果没有找到,就在链表的头部增加一个新的键值对。
这种算法查找操作和插入操作的复杂度均为N,性能很差。
代码
public class LinkedST<Key extends Comparable<Key>,Value> {
private class Node {
Key key;
Value value;
Node next;
}
private Node first;
public void insert(Key key, Value value) {
Node node = search(key);
if(node == null) {
node = new Node();
node.key = key;
node.value = value;
node.next = first;
first = node;
} else {
node.value = value;
}
}
public boolean isEmpty() {
return first == null;
}
public Value get(Key key) {
Node node = search(key);
if(node != null)
return node.value;
return null;
}
private Node search(Key key) {
Node node = first;
while(node != null) {
if(node.key.compareTo(key) == 0) {
return node;
}
node = node.next;
}
return null;
}
}
数组法
另一种方法就是用数组进行实现。搜索的时候可以用二分查找。所以这种算法查找的复杂度时logN,但是插入的复杂度依然是N。虽然比链表稍微好一点,但是插入操作的复杂度还是太高,无法满足性能要求。
代码
public class ArrayST<Key extends Comparable<Key>, Value> {
private Key[] keys; // 注意,这里必须要两个数组分别保存key和value。如果将key和value包含在一个对象中,会导致Generic Array Creation。
private Value[] values;
private int N;
public ArrayST(int capacity) {
keys = (Key[])new Comparable[capacity];
values = (Value[])new Object[capacity];
}
private void debugPrint() {
for(int i=0;i<N;i++) {
System.out.println(keys[i]);
}
}
public void insert(Key key, Value value) {
int i = search(key);
// 如果找到了对应的关键字
if(i < N && keys[i].compareTo(key) == 0) {
// 覆盖对应的值
values[i] = value;
} else {
// 插入新的值
for(int j=N-1;j>=i;j--) {
keys[j+1] = keys[j];
values[j+1] = values[j];
}
keys[i] = key;
values[i] = value;
N++;
}
}
public Value get(Key key) {
int i = search(key);
// 如果找到了对应的键
if(i<N && keys[i].compareTo(key) == 0) {
return values[i];
}
// 找不到
return null;
}
public boolean isEmpty() {
return N==0;
}
// 二分查找
private int search(Key key) {
int lo = 0;
int hi = N;
while(lo < hi) { // 注意,这里是lo < hi,而不是lo > hi
int mid = (hi-lo)/2 + lo;
int compare = key.compareTo(keys[mid]);
// 如果key比较小,说明要找的键在左边
if(compare < 0) {
hi = mid;
} else if(compare > 0) {
// 要找的键在右边
lo = mid+1;
} else {
// 正好找到想要的键
return mid;
}
}
// 没有找到,注意:没有找到时要返回key应该插入的位置,而不是N
return lo;
}
}
结论
因此需要一种算法能够实现查找和插入操作的复杂度都很低。