最近看到了《Thinking in Java》的第17章 容器深入探究,17.6 Set和存储顺序。自己写了写测试代码,加深下理解。主要设计toString()方法(主要是为了方便打印),equals()方法,hashCode()方法,compareTo()方法。

结论

首先明确Set接口有三种不同的实现,HashSet()、TreeSet()、LinkedHashSet()。
HashSet() :快速的定位、读取,会根据hash值来存放,因此读取出来的顺序未必就是插入的顺序。
TreeSet():存入set容器中的内容是按照一定的顺序排列的。
LinkedHashSet():既可以实现快速的定位读取,又可以满足读取出来的顺序就是插入顺序。
除了Integer、String等内部就已经实现了toString()、equals()、hashCode()、compareTo()方法,都实现了Comparable接口,用户自己定义的类的实例(对象),如果要放在set,则要实现equals(),hashCode()、compareTo()方法,实现Comparable接口。

在《Thinking in java》中说:“如果不为你的键覆盖hashCode()和equals()方法,那么使用散列的数据结构(HashSet, HashMap, LinkedHashSet, LinkedHashMap)就无法正确处理你的键”。

因为equals()用来确保你的键是唯一的,而如果你想只覆盖equals()方法,而不覆盖hashCode()方法(你认为可以使用父类Object#hashCode()方法,按照对象的地址来进行hash),但是在Object#hashCode()的源码注释中,有说:如果equals()方法返回true,则每个对象调用hashCode()方法也必须返回相同的hash值

1.equals()方法
用来实现Set中元素的不重复性,如果不覆盖(override)equals()方法,默认使用父类Object的equals方法,则只是比较对象的引用是否相同。下面是Object#equals()方法的实现。

    /*** Indicates whether some other object is "equal to" this one.* <p>* The <code>equals</code> method implements an equivalence relation* on non-null object references:* <ul>* <li>It is <i>reflexive</i>: for any non-null reference value*     <code>x</code>, <code>x.equals(x)</code> should return*     <code>true</code>.* <li>It is <i>symmetric</i>: for any non-null reference values*     <code>x</code> and <code>y</code>, <code>x.equals(y)</code>*     should return <code>true</code> if and only if*     <code>y.equals(x)</code> returns <code>true</code>.* <li>It is <i>transitive</i>: for any non-null reference values*     <code>x</code>, <code>y</code>, and <code>z</code>, if*     <code>x.equals(y)</code> returns <code>true</code> and*     <code>y.equals(z)</code> returns <code>true</code>, then*     <code>x.equals(z)</code> should return <code>true</code>.* <li>It is <i>consistent</i>: for any non-null reference values*     <code>x</code> and <code>y</code>, multiple invocations of*     <tt>x.equals(y)</tt> consistently return <code>true</code>*     or consistently return <code>false</code>, provided no*     information used in <code>equals</code> comparisons on the*     objects is modified.* <li>For any non-null reference value <code>x</code>,*     <code>x.equals(null)</code> should return <code>false</code>.* </ul>* <p>* The <tt>equals</tt> method for class <code>Object</code> implements * the most discriminating possible equivalence relation on objects; * that is, for any non-null reference values <code>x</code> and* <code>y</code>, this method returns <code>true</code> if and only* if <code>x</code> and <code>y</code> refer to the same object* (<code>x == y</code> has the value <code>true</code>).* <p>* Note that it is generally necessary to override the <tt>hashCode</tt>* method whenever this method is overridden, so as to maintain the* general contract for the <tt>hashCode</tt> method, which states* that equal objects must have equal hash codes. ** @param   obj   the reference object with which to compare.* @return  <code>true</code> if this object is the same as the obj*          argument; <code>false</code> otherwise.* @see     #hashCode()* @see     java.util.Hashtable*/public boolean equals(Object obj) {
    return (this == obj);}

代码里的注释也挺重要的,所以也复制过来了。注释主要讲的是:
(1)对于非空Object对象x,y,x.equals(y)返回true,当且 仅当(if and only if) x,y指向了相同的对象。
(2)对于非空Object对象及null,x.equals(null)返回false。
(3)equals方法具有自反性(reflexive),即对于非空Object对象x,x.equals(x)返回true。
(4)equals方法具有对称性(symmetric),即对于非空Object对象x,y,x.equals(y)与y.equals(x)的返回值是一样的。
(5)equals方法具有传递性(transitive),即对于非空Object对象x,y,z,如果x.equals(y) y.equals(z) 均返回true,则x.equals(z)也应该返回true。
(6)equals方法具有一致性(consistent),即对于非空Object对象x,y,x.equals(y)的返回值经过多次调用之后应与第一次的返回值保持一致。
(7)一个不成文的规定:当你覆盖(override)equals()方法时,最好也覆盖(override)hashCode()方法,因为,“相等的对象,其hash code也相等”。 当然了这不是强制规定,只是一个建议而已。覆盖了equals()方法而不覆盖hashCode()方法也可以。
使用IDEA的alt+insert组合键,添加代码时,会发现equals()方法和hashCode()方法时在一起的,如下所示:

2.hashCode()
hashCode()方法时为了实现HashSet和LinkedHashSet而实现的。只有知道对象的hash值,才能根据这个hash值确定 存放在散列表的槽的index。同样,如果不覆盖(override)hashCode()方法,默认使用父类Object的hashCode()方法。下面是Object#hashCode()的实现源码:

    /*** Returns a hash code value for the object. This method is * supported for the benefit of hashtables such as those provided by * <code>java.util.Hashtable</code>. * <p>* The general contract of <code>hashCode</code> is: * <ul>* <li>Whenever it is invoked on the same object more than once during *     an execution of a Java application, the <tt>hashCode</tt> method *     must consistently return the same integer, provided no information *     used in <tt>equals</tt> comparisons on the object is modified.*     This integer need not remain consistent from one execution of an*     application to another execution of the same application. * <li>If two objects are equal according to the <tt>equals(Object)</tt>*     method, then calling the <code>hashCode</code> method on each of *     the two objects must produce the same integer result. * <li>It is <em>not</em> required that if two objects are unequal *     according to the {@link java.lang.Object#equals(java.lang.Object)} *     method, then calling the <tt>hashCode</tt> method on each of the *     two objects must produce distinct integer results.  However, the *     programmer should be aware that producing distinct integer results *     for unequal objects may improve the performance of hashtables.* </ul>* <p>* As much as is reasonably practical, the hashCode method defined by * class <tt>Object</tt> does return distinct integers for distinct * objects. (This is typically implemented by converting the internal * address of the object into an integer, but this implementation * technique is not required by the * Java<font size="-2"><sup>TM</sup></font> programming language.)** @return  a hash code value for this object.* @see     java.lang.Object#equals(java.lang.Object)* @see     java.util.Hashtable*/public native int hashCode();

(1)hashCode()方法是个native方法。
(2)对相同对象多次调用hashCode()方法,返回的整型值应该是一样的。
(3) 规定,如果两个对象调用equals()方法,返回值为true,则这两个对象各自调用hashCode()返回的整型值必须相等。(好像也没这么绝对,至少代码运行不报错,但是执行的结果可能并没有达到预期)
如:
我在Person类中覆盖了equals()方法,而没有覆盖hashCode()方法,看代码如下:

public class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return name.equals(person.name);}//    @Override
//    public int hashCode() {//        return name.hashCode();
//    }@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}
}
        Person p1 = new Person("fan");Person p2 = new Person("hehe");Person p3 = new Person("fan");Person p4 = new Person("axyz");Set<Person> personHashSet = new HashSet<Person>();Collections.addAll(personHashSet, p1, p2, p3, p4);System.out.println(personHashSet);

执行结果如下:

[Person{name='fan'}, Person{name='axyz'}, Person{name='hehe'}, Person{name='fan'}]

结果是不对的。
结果之所以不对主要原因是:
放进去的p1,p2,p3,p4他们的地址是不同的,又没有覆盖hashCode方法,使用默认的Object对象的hashCode,所以hash值不同。
当hash值相同了之后,按照hash表处理冲突的策略,在list或者树结构上按照equals方法来查找需要update(update用于更新值,也就是说已经存在这个值)或者insert(insert用于插入新值,在冲突列表中还不存在这个值)的位置。
HashSet的实现其实是基于HashMap的,其add操作调用的是HashMap的put方法。
IDEA自动生成的equals()方法不是很好,Thinking in java中给的比较好。

    @Overridepublic boolean equals(Object o) {if (this == o) return true;//if (o == null || getClass() != o.getClass()) return false;/*if (!(o instanceof Person)) return false;Person person = (Person) o;return name.equals(person.name);*/return o instanceof Person &&(name.equals(((Person) o).name));}

看上去这个equals方法没有判断其参数o 是否为空,其实 instanceof已经悄悄的检查了参数o是否为null,如果o为null,则返回false。当equals的参数不为null且类型正确,则按照String的equals方法进行比较。
(4)两个不相等(equals方法返回false)的对象,产生的hashCode**不必必须** 相同。但是建议产生不同的hashCode,说是有效率的提升。
3.toString()方法
toString()方法在打印对象时会调用。如果不覆盖(override)toString()方法,默认使用父类Object的toString()方法,这时会打印该类的名字以及该对象散列码的无符号十六进制表示(通过hashCode生成的)。下面是Object#toString()的实现源码:

    /*** Returns a string representation of the object. In general, the* {@code toString} method returns a string that* "textually represents" this object. The result should* be a concise but informative representation that is easy for a* person to read.* It is recommended that all subclasses override this method.* <p>* The {@code toString} method for class {@code Object}* returns a string consisting of the name of the class of which the* object is an instance, the at-sign character `{@code @}', and* the unsigned hexadecimal representation of the hash code of the* object. In other words, this method returns a string equal to the* value of:* <blockquote>* <pre>* getClass().getName() + '@' + Integer.toHexString(hashCode())* </pre></blockquote>** @return  a string representation of the object.*/public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());}

从上面的注释可以看到,推荐所有子类覆盖toString()方法
4.compareTo()方法
用户类要实现Comparable接口。这个方法主要用于将对象存放在TreeSet()时保证顺序的。由于是接口,所以用户类必须要实现这个方法。
下面是Comparable接口的源码:

public interface Comparable<T> {/*** Compares this object with the specified object for order.  Returns a* negative integer, zero, or a positive integer as this object is less* than, equal to, or greater than the specified object.** <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==* -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This* implies that <tt>x.compareTo(y)</tt> must throw an exception iff* <tt>y.compareTo(x)</tt> throws an exception.)** <p>The implementor must also ensure that the relation is transitive:* <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies* <tt>x.compareTo(z)&gt;0</tt>.** <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>* implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for* all <tt>z</tt>.** <p>It is strongly recommended, but <i>not</i> strictly required that* <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any* class that implements the <tt>Comparable</tt> interface and violates* this condition should clearly indicate this fact.  The recommended* language is "Note: this class has a natural ordering that is* inconsistent with equals."** <p>In the foregoing description, the notation* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,* <tt>0</tt>, or <tt>1</tt> according to whether the value of* <i>expression</i> is negative, zero or positive.** @param   o the object to be compared.* @return  a negative integer, zero, or a positive integer as this object*          is less than, equal to, or greater than the specified object.** @throws NullPointerException if the specified object is null* @throws ClassCastException if the specified object's type prevents it*         from being compared to this object.*/public int compareTo(T o);
}

(1)x.compareTo(y),如果x小于y,则返回负数;如果x=y,则返回0;如果x>y,则返回正数。
(2)注释中的sgn是一个数学函数,当参数小于0时返回-1;当参数等于0时返回0;当参数大于0时返回1。
(3)要求sgn(x.compareTo(y)) ==-sgn(y.compareTo(x))
(4)要求传递性 ,即:如果x.compareTo(y)与y.compareTo(z)均返回0,则y.compareTo(z)也必须返回0。
(5)必须要求,如果x.compareTo(y)返回0,则sgn(x.compareTo(z))=sgn(y.compareTo(z))。
(6)强烈推荐,但是不是严格要求,(x.compareTo(y)==0) == (x.equals(y))。也就是说,最好满足这个条件。

结束语

这篇博文总结了一下Set的实现类的特点,以及实现类需要的方法。下篇博文 关于set想说的(二)之Set Demo 写一个Demo。

关于set想说的(一)之Set的实现类及必要的方法相关推荐

  1. 如何在php中显示170cm,人人都能170cm!想要显高?来看最实用的这4个方法!

    原标题:人人都能170cm!想要显高?来看最实用的这4个方法! 后台很多妹子要看的矮个子显高大法,今天来了! 其实,不只是哈比族妹子需要,女生们都有一个大长腿的梦吧! 但很无奈,这基本全靠老天爷赏饭- ...

  2. python帮助生活-想把python运用在实际生活中?那么python查询价格方法可以帮助你...

    [摘要]通过本次课程可以让python学员了解一下python查询价格方法,对代码编程有个感性的认知.也好让大家能够理性选择,不要盲目跟从,选择适合自己当前阶段的学习内容,循序渐进,以兴趣自我探索为向 ...

  3. 单独安装想要的office_安装OFFICE不再求人,最省心的方法

    强烈建议选择office部署工具Office Tool Plus 不建议选择微软官方的office 2016部署工具 因为还需要手动更改代码,不利于新手安装. office tool plus它能够帮 ...

  4. python生活中哪些运用_【想把python运用在实际生活中?那么python查询价格方法可以帮助你】- 环球网校...

    [摘要]通过本次课程可以让python学员了解一下python查询价格方法,对代码编程有个感性的认知.也好让大家能够理性选择,不要盲目跟从,选择适合自己当前阶段的学习内容,循序渐进,以兴趣自我探索为向 ...

  5. 将dll制作成控件_如何将皮料剪切成想要的大小?制作皮具几种裁剪工具和使用方法...

    我们日常所使用的皮具,其实是由很多裁剪好的皮革零件组合制成.而一开始,这些零件都是整张皮革上,为了精确完好裁剪我们所需要的零件.这时候必须使用一种或多种裁切的工具,而各种裁断工具也有其适合及不适合裁切 ...

  6. 不想升级iOS 15系统?iPhone最新屏蔽iOS更新的方法

    苹果在发布新的iOS系统后,总是希望iPhone用户能够尽快升级. 如果不升级的话,即使关闭系统自动更新,在使用手机时就会经常出现烦人的升级弹窗提示.逼死强迫症的更新小红点等. 而且,苹果的iOS系统 ...

  7. 我想问在刚学c语言时总是头痛 有好方法的吗

    逻辑思维一定清楚 ||| 刚开始学都是比较头痛的 多看多问 坚持住 因为没有编程思想 开始是最痛苦的 可能想通就是很简单的问题但要贵在坚持 换个环境要不过段时间再想想 熟能生巧吗 ||| 头痛是正常的 ...

  8. 想知道如何给游戏视频配音?有三个好方法教给你

    现在越来越多的小伙伴会在闲暇之余打打游戏,也会观看一些游戏视频,其中不少游戏视频都有配音.游戏视频配音是一项有趣而富有创造力的任务,它可以让你为游戏画面注入声音,增强观众的体验.无论你是一个游戏爱好者 ...

  9. 想知道怎样p漫画脸??用这两个方法,分分钟出片

    近期发觉身边朋友的社交平台头像都陆续换成了动漫头像,并且仔细观察还发现与本人有几分相似,感觉有些许神奇,于是我向她们了解这是怎么做到的.原来,她们是借助一个照片特效工具,只要把自己的照片上传到工具中, ...

最新文章

  1. JAVA编译成文件的说法_下列关于使用Javac命令编译后生成文件的说法中,正确的是?...
  2. android 数据库表格数据库数据库中,Android SQLite数据库中的表详解
  3. Python报错UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position 1413-1418: ordinal not
  4. Windows系统顽固型文件清除方法
  5. 操作系统 综合应用题知识点更新【章节考试重点(进程同步、处理机调度与死锁、存储管理、设备管理、文件管理)】
  6. “云计算的前世今生·从阿里看云计算”内蒙古师范大学刘晨旭博士专题报告会顺利召开...
  7. sass webpack_如何在Visual Studio和Webpack中编译Sass文件
  8. Angular 学习笔记——拖拽
  9. Python案例:输出指定范围的闰年
  10. 基于DEAP库的NSGA2代码详解
  11. ip计算机网络适配器,如何更改计算机网络ip
  12. 机械键盘POKER2的硬编程
  13. DHCP报文分析及C/C++代码实现
  14. printf用法之打印二进制,八进制,十进制,十六进制
  15. android IO流_Android 开发技术周报 Issue#265
  16. 图像匹配—NCC算法,即归一化互相关匹配
  17. python爬取qq音乐标签_Python爬取qq音乐的过程实例
  18. 计算机等级考试一级在线模拟,全国计算机等级考试一级模拟试题1
  19. PWN入门(3)覆盖堆栈上的变量
  20. Docker基础: Linux内核命名空间之(3)net namespace

热门文章

  1. 视频教程-SQL入门教程-MySQL
  2. python使用scipy报错:“ImportError: DLL load failed: 找不到指定的模块”的解决方案
  3. 当QQ号码也成为稀缺资源
  4. PR片头片尾模板 很炫酷的能源火花粒子logo展示pr开场视频模板
  5. PyQt5中的界面语言翻译
  6. hive中的绣花模型_几何图案 - 面料花型设计 GeoHive_9x_Red - MyDigitex
  7. 携手12年,鸿翼助力首钢股份打造非结构化数据中台
  8. 【中国大学慕课浙江大学【C语言课程】-翁恺教授】学习心得
  9. Vue CLl 基础知识+安装
  10. live800:网站如何添加客服在线咨询功能?