数组和链表是最基本的两种线性结构的存储数据结构。他们各自的优缺点:
数组:查找容易,直接使用下标就可以实现,但是插入困难。
链表:插入方便,但是查找困难。
Hash表则有效地将二者的优点结合。个人理解:Hash表就是将数据按照特定的算法制定到一个地址上,hashCode方法实际上返回的就是对象存储的物理位置。
使用Hash表时应注意:负载因子。
负载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了负载因子与当前容量的乘积时,就需要通过调用 rehash 方法将容量翻倍。
默认加载因子为0.75。在时间和空间成本上寻求一种折衷。负载因子过高虽然减少了空间开销,但同时也增加了查询成本。在设置初始容量时应该考虑到映射中所需的条目
数及其负载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以负载因子,则不会发生rehash操作(如果负载因子是默认的0.75,HashMap(16)
的时候,占16个内存空间,实际上只用到了12个,超过12个就扩容。如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容。但是,
用到的空间越多,hashcode重复的可能性就越大,同一个空间里面的元素数目就可能会增加,会增加查找的时间)。
在这里采用挂链法,在数组中的元素存的为链表,经过哈希函数计算出数据的在数组中的存储位置,如果有相同的存储位置则构建链表将新加入的元素加到链表的末尾。
代码如下:
public class Hash {
private Node hashList[];//存放链表的数组
private int size=0;//容量默认为0
private int newSize;//容量
private int number=0; //当前数量(不记加入下拉链表中的数量)
/**
* 构造方法
*/
public Hash(int size){
hashList=new Node[size];
this.size=size;
}
/**
* 获得容量大小的方法
* @return size
*/
public int getSize(){
return size;
}
/**
* 获得哈希编码的方法
* @return 返回一个hash值
*/
public int getHashCode(Object ob){
return ob.hashCode()%5;
}
/**
* 增加元素
* @param ob
*/
public void add(Object ob){
//得到哈希码对应的元素下标
int index=getHashCode(ob);
if(hashList[index]==null){
hashList[index]=new Node(ob);
number++;//统计元素个数
}else{
//加入下拉链表
Node newNode=new Node(ob);
Node no=hashList[index];
newNode.setNext(no);
hashList[index]=newNode;
}
if(number+1>size){
reHash(newSize);
}
}
/**
* 删除元素
* @param ob
*/
public void delete(Object ob){
int index=getHashCode(ob);
//分析在首节点还是在拉链中
//第一种情况:在首节点
if(hashList[index].getOb()==ob){
//如果没有挂链
if(hashList[index].getNext()==null){
//直接删除
hashList[index]=null;
number--;
}else{
//如果有挂链 ,则将拉链中的置首
hashList[index]=hashList[index].getNext();
}
return ;
}
//第二种情况:在拉链中
Node first = hashList[index];
Node last = null;
if(first.getOb().equals(ob)&&first!=null){
last=first;
first=first.getNext();
}
}
/**
* 再哈希
* @param newSize:新的数组长度
*/
public void reHash(int newSize){
Node newList[] =new Node[newSize];
newSize = size *4;
number=0;
//遍历原有数组,将原有数组的每一个元素重新存到新的数组中
for (int i=0;i<size;i++){
if(hashList[i]!=null){
add(hashList[i].getOb());
number++;
//加入挂链中的内容
if(hashList[i].getNext()!=null){
newList[i].next=hashList[i].getNext();
add(hashList[i].getOb());
}
}
}
//放入原hashList中去
hashList=newList;
}
/**
* 打印方法
*/
public void print(){
for (int i=0;i<size;i++){
Node no =hashList[i];
while(no!=null){
System.out.println(no.getOb());
no=no.getNext();
}
}
}
}
/**
* 定义节点
*
*/
class Node{
private Object ob;//节点的值
public Node next;//下一个节点
public Node(Object ob){
this.ob=ob;
}
public Object getOb(){
return ob;
}
public Node getNext(){
return next;
}
public void setNext(Node next){
this.next=next;
}
}
测试代码:
public class HashMain {
public static void main(String[] args){
Hash ha = new Hash(10);
ha.add(1);
ha.add(2);
ha.add(3);
ha.add(4);
ha.add(5);
ha.add(6);
ha.add(7);
ha.add(8);
ha.add(9);
ha.add(10);
ha.add(11);
ha.delete(1);
ha.print();
}
}
得到结果:
5
10
6
11
2
7
3
8
4
9