Java基础知识_ArrayList和HashSet的比较,以及HashCode方法
2013-07-14 16:00
701 查看
今天学了一个细节上的内容,那就是ArrayList和HashSet的比较以及Hashcode方法。
通过之前集合框架的学习,我们大致了解了两种集合的区别。List集合的存放是有顺序的,也就是说我们可以指定位置存放或者取出元素,集合中的元素可以重复。而Set集合的存放是无序的,或者说他的存放顺序并非我们指定,且其中的元素不可以重复。
OK,我们先来看看下面的例子:
首先我们有一个用于测试的类ReflectPoint
接着我们创建它的实例对象并放入ArrayList中
输出的结果是几呢?——是4 。
因为ArrayList是允许重复的,所以四个add语句每一个都放入到了集合中。
那么如果我们将上面的ArrayList换成HashSet呢?
输出结果是——3 。
因为最后一个p1没有放到集合里去,编译器在将对象放入集合之前会首先检测一下集合中是否有同样的元素,如果有则不再放入(记住不是覆盖,而是没有放进去)。
但是我们注意到了,在我们创建对象时,p3的初始化值和p1是一样的,那么为什么p3放进集合中去了呢?
因为p1和p3在底层调用比较方法的时候,equals比较的是Hashcode值,而Hashcode值是由内存在计算。
如果我们希望p3和p1相等的话,就需要我们在ReflectPoint中覆盖equals方法和Hashcode方法,就像这样:
这时HashSet集合中的元素数则是我们希望的那样,为2 。
现在我们可以在做一些变动——如果我们只覆盖了equals方法,而没有覆盖Hashcode方法呢?结果会是什么?2还是3?
结果是——看运气吧!也许是2,也许是3.
咦,这就很奇怪了不是么,为什么要看运气呢?
想要回答这个问题,就得先了解Hashcode方法。
如果想要查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?你通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回都否定的信息。如果一个集合中有很多个元素,譬如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。
现在你应该明白了为什么上面的输出结果是2还是3要靠运气,因为你也不知道p1和p3在不在同一个区域中,如果两者的哈希码在同一区域,那么p3就无法放入集合中,反之p3就可以放入集合中。
所以,在实际编程中,我们一般会同时覆盖equals和Hashcode方法以保持他们的一致性——当然,如果你的程序不需要用到Hashcode,你完全没必要去覆盖它。
这里还有个需要我们注意的小问题:
当一个对象被存储进集合中以后,就不要再修改对象中那些参与了哈希值计算的字段了,否则,对象修改后的哈希值,和已经存入HashSet集合的哈希值便不一样了,这个时候,即使使用当前引用的contains方法去HashSet集合中寻找之前被你存入的对象,也将获得无法找到返回的结果,从而造成内存泄漏。
通过之前集合框架的学习,我们大致了解了两种集合的区别。List集合的存放是有顺序的,也就是说我们可以指定位置存放或者取出元素,集合中的元素可以重复。而Set集合的存放是无序的,或者说他的存放顺序并非我们指定,且其中的元素不可以重复。
OK,我们先来看看下面的例子:
首先我们有一个用于测试的类ReflectPoint
package cn.icecino.d1; public class ReflectPoint { public int x; private int y; public ReflectPoint(int x,int y){ this.x = x; this.y = y; } }
接着我们创建它的实例对象并放入ArrayList中
package cn.icecino.d1; import java.util.ArrayList; import java.util.Collection; public class ReflectTest { public static void main(String[] args) { // TODO 自动生成的方法存根 Collection collection = new ArrayList(); ReflectPoint p1 = new ReflectPoint(3,3); ReflectPoint p2 = new ReflectPoint(5,5); ReflectPoint p3 = new ReflectPoint(3,3); collection.add(p1); collection.add(p2); collection.add(p3); collection.add(p1); System.out.println(collection.size()); } }
输出的结果是几呢?——是4 。
因为ArrayList是允许重复的,所以四个add语句每一个都放入到了集合中。
那么如果我们将上面的ArrayList换成HashSet呢?
输出结果是——3 。
因为最后一个p1没有放到集合里去,编译器在将对象放入集合之前会首先检测一下集合中是否有同样的元素,如果有则不再放入(记住不是覆盖,而是没有放进去)。
但是我们注意到了,在我们创建对象时,p3的初始化值和p1是一样的,那么为什么p3放进集合中去了呢?
因为p1和p3在底层调用比较方法的时候,equals比较的是Hashcode值,而Hashcode值是由内存在计算。
如果我们希望p3和p1相等的话,就需要我们在ReflectPoint中覆盖equals方法和Hashcode方法,就像这样:
package cn.icecino.day1; public class ReflectPoint { public int x; private int y; public ReflectPoint(int x,int y){ this.x = x; this.y = y; } @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; } }
这时HashSet集合中的元素数则是我们希望的那样,为2 。
现在我们可以在做一些变动——如果我们只覆盖了equals方法,而没有覆盖Hashcode方法呢?结果会是什么?2还是3?
结果是——看运气吧!也许是2,也许是3.
咦,这就很奇怪了不是么,为什么要看运气呢?
想要回答这个问题,就得先了解Hashcode方法。
如果想要查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?你通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回都否定的信息。如果一个集合中有很多个元素,譬如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。
现在你应该明白了为什么上面的输出结果是2还是3要靠运气,因为你也不知道p1和p3在不在同一个区域中,如果两者的哈希码在同一区域,那么p3就无法放入集合中,反之p3就可以放入集合中。
所以,在实际编程中,我们一般会同时覆盖equals和Hashcode方法以保持他们的一致性——当然,如果你的程序不需要用到Hashcode,你完全没必要去覆盖它。
这里还有个需要我们注意的小问题:
当一个对象被存储进集合中以后,就不要再修改对象中那些参与了哈希值计算的字段了,否则,对象修改后的哈希值,和已经存入HashSet集合的哈希值便不一样了,这个时候,即使使用当前引用的contains方法去HashSet集合中寻找之前被你存入的对象,也将获得无法找到返回的结果,从而造成内存泄漏。
相关文章推荐
- java基础巩固系列(五):ArrayList与HashSet的比较,以及HashCode分析
- java学习之ArrayList和HashSet的比较以及HashCode分析
- java基础之问题:请说出hashCode方法、equals方法、HashSet、HashMap之间的关系
- 【JAVA基础知识总结】Java I/0流概述以及使用方法
- ArrayList 与HashSet的比较,及应用反射读取properties配置文件中的数据进行实例化再调用,以及类加载器的使用;还有HashCode的分析,及导致内存泄露,内存溢出的原因之一
- Java中ArrayList HashSet的使用 以及HashCode的用处
- Java基础知识强化26:Object类之hashCode()方法、getClass()方法
- Java中ArrayList HashSet的使用 以及HashCode的用处
- 【JAVA基础知识总结】Java I/0流概述以及使用方法
- JAVA基础知识以及一个引用方法来求矩形周长的代码
- Java中ArrayList HashSet的使用 以及HashCode的用处
- Java高新技术之ArrayList_HashSet的比较及Hashcode分析
- Java基础知识强化83:System类之gc()方法(垃圾回收)以及和finalize()区别
- Java基础知识——equals方法和HashCode方法介绍
- java-day05-Thread-基础知识以及运用API第一种方法使用Thread类
- ArrayList与HashSet的比较,以及HashCode分析, 内存泄露
- 黑马java学习笔记_ArrayList 和HashSet的比较及HashCode分析和Java中内存泄漏的探讨
- java 高新技术【5】ArrayList_HashSet的比较及Hashcode分析
- Java程序员从笨鸟到菜鸟之(二十三)常见乱码解决以及javaBean基础知识