深入arraylist 和 hashset

arraylist 和 hashset


arrylist看名字我们可以理解为一个链表,我们知道,链表存放东西滴时候是有顺序的存放的(当然是放入的先后顺序),按照C++中的思路去理解一下,我们放东西进去的时候可以将同一个东东放进去两次,我们放入了一次,就会new出一块空间给我们来存放那个东东,再放,我们再给一个空间,所以同一个数据,我们可以放入多次。
hashset,我们先要了解一下hash算法,我以前用C写过一个简单的hash查找算法。
抛出一个问题:用数组的概念说下hash算法,我们定义一个数组,数组有下标0、1、2、3....我们定义一个的数组,里面放入1000个数字,这1000个数字的大小在0到10000之间。
我们思考一下:
普通存放的话,我们可以将数字按大小顺序排列,这样的排列的话,查找的时候需要去遍历这个数组才能找到。
用hash表的话,我们定义一个很大的数组a[10000],将里面的元素全部初始化为-1,将这1000个数字按照他们的大小去与数组的角标去对应,例如我们有一个数字 500 我们就将它存入a[500]中,有一个数字是8923,我们就将它放入到a[8923]中,这样放入1000个数字(当然不能有重复,如果有重复的话放进去的东西是一样的啦,哈哈哈),这样放完,我们按顺序将不是-1的数字取出来,然后肯定是有序的啦,它们是按照角标来的嘛。不得不承认我们这里浪费了很多空间(当然可以优化,我们这儿就不做深入了),因为我们本来只要申请一个大小为1000的数组就可以了,但是,查找的时候你会发现它的妙处:如果你要查找3981这个数,我们只要直接看a[3981]这个元素是不是-1,不是的话,那它就存在,如果是-1,说明没有这个数字。不管多大的数组查找复杂度都为 1,速度是不是有了飞越?


不知道到这里有没有对hash表有一个比较深刻的印象(当然我这里举的是个最简单的栗子,没有进行深入和任何优化的处理,只是为了让大家对hash有一个了解,当然如果大家要深入完全可以考虑用链表去存放结构等等,这里就不说了)
了解了hash算法,我们再来看我们今天的话题arrylist和hashset
arrylist其实就类似于我们的第一种做法(当然它并没有进行排序,它是第一次存放的数字放a[0]第二次存放的数字放a[1],以此类推的),所以我们完全可以放入两个相等的数字,或者说是同一个数字。
hashset就用了我们的hash算法,里面有一个叫做hashcode的玩意儿。在Object类中,我们封装了一个hashcode的方法,当然这个方法,它是得到一个int值,我们可以将它理解为一块内存地址(哦,对了,对于C不是很熟悉的朋友可能对内存不是非常熟悉,但我除了这么说,也不知道该怎么去具体的表述了)我们这里可以这么理解,我们可以这么理解,我们new一个对象,就是分配了一块内存去存放这个对象,很容易理解了吧,那对于同一个元素,它的hashcode值就肯定一样了。这里不得不说的还有一个equals方法,如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。这样我们就可以让我们需要让他们的hashcode的值相等的两个元素的值相等。


我们举个程序栗子看下哦:
先定义一个我们要用的类:
public class ReflectPoint
{
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}


@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}


private int x;
public int y;

public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}


再写一个测试类:
import java.util.Collection;
import java.util.HashSet;


public class Test2 {


public static void main(String[] args)
{
Collection c = new HashSet();
ReflectPoint p1 = new ReflectPoint(3,3);
ReflectPoint p2 = new ReflectPoint(3,5);
ReflectPoint p3 = new ReflectPoint(3,3);

c.add(p1);
c.add(p2);
c.add(p3);
c.add(p1);
System.out.println(c.size());
}
}


代码没几行,也很好看懂下面我做下简单介绍:
当我们没有定义hashCode和equals方法的时候,输出为三,显然,我们比较的时候是因为p1的equals是相等的,所以只存放进去一个。当我们重载了equals和hashCode方法后,输出为2,这时候p1和p3也相等了,能明白了吧。




有一个内存泄漏问题:
之前对于java的内存泄漏我一直很诧异,因为java并没有指针,它的内存泄漏是怎么样的呢,这里有个栗子:
我们将测试类改为如下代码:
import java.util.Collection;
import java.util.HashSet;


public class Test2 {


public static void main(String[] args)
{
Collection c = new HashSet();
ReflectPoint p1 = new ReflectPoint(3,3);
ReflectPoint p2 = new ReflectPoint(3,5);
ReflectPoint p3 = new ReflectPoint(3,3);

c.add(p1);
c.add(p2);
c.add(p3);
c.add(p1);
p2.y = 10;//修改值
c.remove(p2);//移除
System.out.println(c.size());
}
}


输出结果为2,很诧异吧:我们明明把hashCode值不一样的那个点删掉了,为什么还是2呢?其实我们由于改了p2的一个值,所以没有能够成功的移除p2,下面我们再看一段代码
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;


public class Test2 {


public static void main(String[] args)
{
Collection c = new HashSet();
ReflectPoint p1 = new ReflectPoint(3,3);
ReflectPoint p2 = new ReflectPoint(3,5);
ReflectPoint p3 = new ReflectPoint(3,3);

c.add(p1);
c.add(p2);
c.add(p3);
c.add(p1);
System.out.println(c.contains(p2));
System.out.println(c.contains(new ReflectPoint(3,5)));
p2.y = 10;
System.out.println(c.contains(p2));
System.out.println(c.contains(new ReflectPoint(3,5)));
System.out.println(c.contains(new ReflectPoint(3,10)));



c.remove(p2);
System.out.println(c.size());
}
}


输出结果:
true
true
false
false
false
2
这个把我自己也搞迷糊了,前两个true没有问题,第三个false也可以理解,但是连看下面三个false和那个2,我灰常费解:当我们修改了p2的值以后 p2不在表中了,表中也没有(3,5)这个点,也没有(3,10)这个点,但是size却是2,非常费解,这个问题留下下回分解吧



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值