数组和链表是最基本的两种线性结构的存储数据结构。他们各自的优缺点:
数组:查找容易,直接使用下标就可以实现,但是插入困难。
链表:插入方便,但是查找困难。
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