在数据结构中,ArrayList,LinkedList,HashTable是我们在java经常使用的。在这里我简要的分析一下这三种数据结构:
1.ArrayList:
ArrayList 是采用数组方式存储数据的,是根据索引来访问元素的,可以根据需要自动扩展内部数据长度,以便增加和插入元素,允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快插入数据慢。
2.LinkedList:
LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。
如果只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用ArrayList也可以。如果是对其它指定位置的插入、删除操作,最好选择LinkedList。因为它的速度相对来说会比较快。
3.HashTable:
哈希表是一种数据结构,提供快速的插入和查找功能。哈希表基于数组存储数据,因此能在O(1)时间内定位数据。关键字值通过哈希函数映射为数组下标。缺点就是数组创建后容量固定,如果数据较多需要不断扩展其长度。
如何将关键字转换为数组下标?这个操作是通过哈希函数完成的。比如,下面就是一个简单的哈希函数,
int hash(int key){
return key % array.length;//通过取余,返回值数组下标
}
有时候,有些哈希函数对于不同的键值可能会生成相同的哈希码值。所以需要解决冲突问题,下面有两种方法:
1.开放地址法:通过在哈希表中再找一个空位来解决此问题。
此法又分为三种方法:
1)线性探测,即上面使用的这种方法,哈希函数将关键字范围压缩到数组的范围,对数组长度取余即可,+1,+2,+3…以此类推进行取余。
2)二次探测的过程是这样,+1,+2,+4,+9…以此类推。
3)再哈希,用不同的哈希函数对关键字再做一次哈希化。
2.链地址法:在哈希表每个单元中设置链表。某个数据项的关键值仍然映射到哈希表的单元中,而数据项本身插入这个单元的链表中其他同样映射到这个位置的数据项只需要加入到链表中。
那么接下来,这里有一些简要实现三个数据结构的代码:
ArrayList:
public class ArrayList { private int changdu; //数组实际填充大小 private int yichu; //数组将溢出时的扩充步长 Object[] data =new Object[100]; //默认数组长度为10 //无参的构造方法,默认扩充步长为100 public ArrayList(){ yichu=1000; } //带初始数组长度与扩充步长的构造方法 public ArrayList(int chushi,int yichu){ data=new Object[chushi];//初始数组以及其长度 this.yichu=yichu; } //给数组添加新数据 public void add(Object obj){ if(changdu>=data.length){ //判断有无溢出,溢出则增加数组长度yichu,并将原数组数据放入新数组 Object[] data2=new Object[data.length+yichu]; for(int i=0;i<data.length;i++){ data2[i]=data[i]; } data=data2;//指向新的数组 } data[changdu]=obj;//把新添加的对象放到数组的最后一位 changdu++; } //获得指定数组位置的内容 public Object get(int index){ if(index<0||index>data.length){ System.out.println("超出数组限定范围"); return null; } return data[index]; }; //删除指定位置的元素 public void remove(int index){ Object[] data2 = new Object[data.length-1]; for (int i=0;i<index;i++){ data2[i]=data[i]; } for (int i=index+1;i<data.length;i++){ data2[i-1]=data[i];//以创建新的一个数组的方式去删除 } data=data2;//指向原来的数组,即就是原来的数组发生了这些变化了 Object obj =data[index]; } //获得数组大小 public int size(){ return changdu; } //在指定位置插入元素 public void insert(Object obj,int index){ Object[] data2 =new Object[data.length+1]; //新建一数组才可从数组中间插入,此时新数组长度需至少加一 for(int i=0;i<index;i++){ data2[i]=data[i]; } data2[index]=obj; for(int i=index+1;i<data2.length;i++){ data2[i]=data[i-1]; } data=data2;//指向原来的数组,即就是原来的数组发生了这些变化了 } }
LinkedList:
public class Link { public static LinkNode front = null;// 第一个节点 public LinkNode last = null;// 最后一个节点 /* * 插入结点 */ public void add(Object obj) { // 创建一个新的结点 LinkNode node = new LinkNode(obj); if (null == front) {// 如果链表为null front = node; last = front; } else { last.setChild(node); node.setParent(last); last = node; } } /* * 在指定的索引下插入结点 */ public void insertIndexobj(int index,Object obj){ if(this.getLength()<index||index<0){ throw new RuntimeException("下标越界:"+index+",,size:"+this.getLength()); }else{ //创建一个新的结点 LinkNode newNode=new LinkNode(obj); //得到当前索引位置的结点 LinkNode node=this.getLinkNode(index); if(index==0){ front=newNode; }else{ //得到父结点 LinkNode fNode=node.getParent(); //设置新的引用关系 fNode.setChild(newNode); newNode.setParent(fNode); } //设置新的引用关系 newNode.setChild(node); node.setParent(newNode); } } /* * 根据索引删除结点 */ public void deleteLinkNode(int index){ if(this.getLength()<index||index<0){ throw new RuntimeException("下标越界:"+index+",,size:"+this.getLength()); }else{ //得到当前索引位置的节点 LinkNode node=this.getLinkNode(index); //得到父节点 LinkNode fNode=node.getParent(); //得到子节点 LinkNode cNode=node.getChild(); if(fNode==null){ front=cNode; }else if(cNode==null){ front.setChild(null); }else{ fNode.setChild(cNode); cNode.setParent(fNode); } } } /* * 根据索引取出节点 */ public LinkNode getLinkNode(int index){ if(this.getLength()<index||index<0){ throw new RuntimeException("下标越界:"+index+",,size:"+this.getLength()); }else{ int num=0; LinkNode node=front; while(num!=index){ node=node.getChild(); num++; } return node; } } /* * 得到链表的长度 */ public int getLength(){ int count=0; if(front==null){ return count; } LinkNode node=front.getChild(); while (null!=node){ count++; node=node.getChild(); } return count+1; } }
public class LinkNode { private Object obj;// 节点内的的数据对象 private LinkNode child;// 对下一个节点的引用 private LinkNode parent;// 对上一个节点的引用 // 对创建节点对象的时候就传入节点中的数据对象 public LinkNode(Object obj) { this.obj = obj; } public Object getObj() { return obj; } public void setObj(Object obj){ this.obj=obj; } public LinkNode getChild(){ return child; } public void setChild(LinkNode child){ this.child=child; } public LinkNode getParent(){ return parent; } public void setParent(LinkNode parent){ this.parent=parent; } }
HashTable:
public class Hash{ private Object[] sk; //存关键字的数组 private int size; //数组大小 //无参的构造函数 public Hash(){ sk= new Object[16]; //默认数组长度是16 size = 0; } //带参数的构造函数 public Hash(int arraylenght,int size){ sk=new Object[arraylenght]; this.size=size; } //哈希函数 public int hash(Object obj){ return Math.abs(obj.hashCode())%sk.length; } //处理冲突的哈希函数 public int hashForCollision(Object obj){ int newhash = Math.abs(obj.hashCode())%(sk.length-1);//这里调用里JDK里自有的hashcode方法后实现映射 if(newhash%2==0){ return newhash + 1;//如果整除的话就会加一返回 } return newhash; } //判断哈希表里面是否已有某对象 public boolean contains(Object obj){ int i = hash(obj); while (sk[i] != null){ if(sk[i].equals(obj)){ return true; } i = (i + hashForCollision(obj))%sk.length; } return false; } //添加新元素 public void add(Object obj){ //先判断是否需要扩容,若已有数据占原数组一半,则扩容 if(size>=sk.length/2){ this.rehash(); } int i = hash(obj);//获取其哈希值 while(sk[i] != null){ if(obj.equals(sk[i])){ return; } //判断该索引处是否已占用,若占用则调用hashforcollision生成新地址 i = (i + hashForCollision(obj))%sk.length; } sk[i] = obj; size ++; } //扩大哈希表为原表的四倍,并把原来的哈希表添加到新表中 public void rehash(){ Hash ht = new Hash(); ht.sk = new String[this.sk.length * 4]; for(int i = 0; i < this.sk.length; i ++){ if((this.sk[i] != null)){ ht.add(this.sk[i]); } } this.sk = ht.sk; this.size = ht.size; } //删除某个元素 public void remove(Object obj){ if(this.contains(obj)){ int i = this.getIndex(obj); this.sk[i] = null; } } //得到某对象在哈希表中的位置 public int getIndex(Object obj){ int i = this.hash(obj); while(this.sk[i] != null){ if(this.sk[i].equals(obj)){ return i; } i = (i + this.hashForCollision(obj))%this.sk.length; } return -1; } //获取哈希表中某元素 public Object get(Object obj){ if(this.contains(obj)){ int i = this.getIndex(obj); return sk[i] ; } return null; } //输出哈希表中所有元素 public void print(){ for(int i = 0; i < sk.length; i ++){ System.out.println(i+":"+sk[i]); } } //哈希表存储元素的个数 public int size(){ return this.size; } //哈希表的长度 public int length(){ return this.sk.length; } }