本文地址:https://www.cnblogs.com/oberon-zjt0806/p/12367370.html

本文遵循CC BY-NC-SA 4.0协议,转载请注明出处

特别说明

本文的基本语境是Java如果需要C#版本请看这里

还有一个故事……(平行世界篇)

这是一个关于另外一个平行世界——Java中的相似的故事……

文艺复兴.jpg……

还有一个美丽的梦幻家园:java.util

在Java中,巧了,也有泛型的数据容器。不过,Java的容器和C#的组织方式有些不同,C#是单独开了一个System.Collections及子命名空间专门用于给容器类使用,而Java则是把容器连同其他的工具类一起丢到了java.util这一个大包中。

不过,容器的这部分内容似乎在Java里叫做JCF(Java Collections Framework)

而且,Java似乎不存在非泛型版本的容器,尽管据说SE 5之前的容器普遍存在类型安全性问题(当然已经是过去了……),此外,Java还提供了对应于一些容器的功能接口(而且是泛型接口),方便自定义容器类型,例如,List<E>是列表容器的接口而不是泛型容器,其对应的泛型容器是ArrayList<E>

Pigeon p = new Pigeon("咕咕咕"); // class Pigeon extends Bird
Cuckoo c = new Cuckoo("子规"); // class Cuckoo extends Bird List<Bird> birds = new List<Bird>() { { add(p); add(c); } }; // 错误,List是容器接口,不能直接实例化
ArrayList<Bird> flock = new ArrayList<Bird>() { { add(p); add(c); } }; // 正确,这是一个泛型为Bird的ArrayList容器
List<Bird> avians = new ArrayList<Bird>() { { add(p); add(c); } }; // 正确,ArrayList<E>实现了List<E>,可视为List<E>的多态

匿名内部类(AIC)

这个神奇的初始化写法在Java术语里叫做匿名内部类(AIC,Anonymous Inner Class),在Java中AIC是被广泛使用而且屡试不爽的,主要是用于简化Java代码。AIC的出现使得从一个抽象的接口或抽象类(无法实例化,不提供实现)快速重构一个简单具体类(可以实例化,具有实现)变得非常容易而无需另开文件去写类,而不会造成太大的性能影响(因为AIC是随用随丢的)。

不过AIC有个不算副作用的副作用,因为AIC往往需要实现(甚至可能是大量改写)接口或抽象类的方法,因此可能会在嵌套层数特别多的上下文中使得原本就比较混乱的局面更加混乱(特别是采用了不当的缩进策略的时候,因为AIC的写法本身在大多数情形下就包含了相当多的嵌套),导致代码可读性严重下降,看起来不是很直观,有碍观瞻。

此外,如果某个AIC频繁地出现,那么AIC就不那么适用了,这种情况下建议把当前的AIC改成一个具名的类。

并且还有一个善战的达拉崩巴:HashSet

更加巧合的是,在java.util里也有一个HashSet<E>,功能也是作为一个哈希集使用,也就是说它也满足如下两点:

  1. 元素是唯一
  2. 元素是无序

What a COINCI-DANCE~~

而且,也是要分两种情况,值类型下,只要两个值相等,那么第二个元素就不会被添加:

int i = 5;
int j = 5; HashSet<int> integers = new HashSet<int>();
integers.add(i); // i被添加到integers中
integers.add(j); // 没有变化,integers中已经有5了

而对于引用类型来说,和C#类似,也采用引用一致性判断:

// 为了简单这里不封装了,直接上字段
class Student {
public int id;
public String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
} public class Program {
public static void main(String[] args) {
Student s1 = new Student(1, "Tom");
Student s2 = new Student(2, "Jerry");
Student s3 = s1;
Student s4 = new Student(1,"Tom");
HashSet<Student> students = new HashSet<Student>();
students.add(s1); // s1被加入students中
students.add(s2); // s2被加入students中
students.add(s3); // 没有变化,s1已存在
students.add(s4); // s4被加入到students中,尽管s4和s1长得一样,但引用不一致
}
}

我甚至是差不多拿上篇文章中的代码,几乎没怎么改23333

但是,和上次一样的问题,尽管s4s1引用不一致,但实际场合下,我们倾向于把它们当作同一个人,那么怎么办呢??

还有另外一个故事(不是虚假传说)

不是虚假传说-序言

嗯,这个不是虚假的故事,这就是正经的解决方案,放心大胆的读吧!!

还有一对涂满毒药的夺命双匕:equals和hashCode

当然,Java里所有对象都继承自java.lang.ObjectObject,而Java对象也有两种相等判别方式:==Object.equals

而且,这俩判别方式一模一样,值类型下只要值相等就可以,而对于引用类型,==判别的是引用一致性

但是为什么这次标题里没有==的故事了??

一直就没有,那是你的错觉,上一篇的==还是虚假的故事呢,而且原因也很简单:

Java里运算符不允许重载

而且Object里没有之前的ReferenceEquals,所以==就是引用一致性的兜底判别,没法重载的话那就免谈了,不过equals是父类方法,当然是可以重载的。

那hashCode呢??

和隔壁的System.Object.GetHashCode()类似地,这边也有一个java.lang.Object.hashCode(),作用也是类似的,返回一个用作哈希值的数。

而且更加巧合的是,这里的Object.equals()hashCode()也没什么关系,单独改写其中一个函数对另外一个函数也都没什么影响。

最最巧合的是,和隔壁一样,Java也建议equalshashCode要改都改

不过之前是因为非泛型容器(比如Hashtable),而这次是真真正正的为了泛型容器。

HashSet<E>正是使用equalshashCode作为双重判据,HashSet<E>认为equals返回true,且两者hashCode相等的时候,就认为是相同的元素而不被

那把骑士圣剑呢??

非常遗憾,这里没有那种东西java.util并没有提供类似于IEqualityComparer<T>的东西,而HashSet<E>也不提供getComparator()这种方法……

java.util只提供这个东西——interface Comparator<T>,其作用和C#中的IComparer<T>差不多,因为Java不让重载运算符,因此Comparator<T>提供了compare方法进行的大小比较,而且只是用于比较排序而已。

然后崩巴也准备开启营救公主的冒险

最后把程序改写成这个样子:

import java.util.HashSet;

class Student {
public int id;
public String name;
public Student(int id,String name) {
this.id = id;
this.name = name;
} @Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return id == ((Student)obj).id && name.equals(((Student)obj).name);
} @Override
public int hashCode() {
return id;
}
} public class HSetTest {
public static void main(String[] args) {
Student s1 = new Student(1,"Tom");
Student s2 = s1;
Student s3 = new Student(1,"Tom");
@SuppressWarnings("serial")
HashSet<Student> students = new HashSet<Student>() {
{
add(s1); // s1被添加到students中
add(s2); // 没有变化,s1已存在
add(s3); // 没有变化,s3被认为和s1逻辑上相等
}
}; for(Student s : students) {
System.out.println(String.format("%d.%s",s.id,s.name));
}
}
}

输出结果:

1.Tom

Java中HashSet的重复性与判等运算重载的更多相关文章

  1. C#中HashSet的重复性与判等运算重载

    目录 一个故事-- 一个繁荣的遥远国度:泛型容器 但是我也不确定容器里能放些什么东西啊 一个英勇的皇家骑士:HashSet 值类型的HashSet 引用类型的HashSet 另外一个--故--事?? ...

  2. Java中的二进制及基本的位运算

    Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二 ...

  3. Java中HashSet的解读

    一. HashSet源代码 HashSet 的实现   对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的 ...

  4. Java中HashSet和HashMap

    Set中存储元素为什么不重复(即使hashCode相同)? HashSet中存放自定义类型元素时候,需要重写对象中的hashCode方法和equals方法, HashSet中存放自定义类型元素时候,需 ...

  5. Java中HashSet,HashMap和HashTable的区别

    HashMap.HashSet.HashTable之间的区别是Java程序员的一个常见面试题目,在此仅以此博客记录,并深入源代码进行分析: 在分析之前,先将其区别列于下面 1:HashSet底层采用的 ...

  6. java中HashSet详解(转)

    HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...

  7. java中HashSet详解

    HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...

  8. java集合(4)- java中HashSet详解

    HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...

  9. java中hashSet原理

    转自: http://blog.csdn.net/guoweimelon/article/details/50804799 HashSet是JavaMap类型的集合类中最常使用的,本文基于Java1. ...

随机推荐

  1. playbooks框架与子文件编写规范

    Test Playbooks框架 ​ 详细目录testenv文件 ​ 主任务文件main.yml ​ 任务入口文件deploy.yml ​ Ansible连接playbooks需要添加ssh协议认证 ...

  2. 投影方式- Unity3D游戏开发培训

    投影方式- Unity3D游戏开发培训   作者:Jesai 2018-02-12 20:33:13 摘  要 透视投影是3D渲染的基本概念,也是3D程序设计的基础.掌握透视投影的原理对于深入理解其他 ...

  3. Nginx. 用http访问https跨域

    用http 访问 https域名, 报跨越问题 解决方法: 在nginx相应服务的转发配置下添加: add_header 'Access-Control-Allow-Origin' 'http://i ...

  4. powershell Google Firefox

    $firefox = @{ DisplayName = "Mozilla Firefox"; filename = "Firefox Setup 68.0b7.msi&q ...

  5. Spring MVC中的拦截器Interceptor

    谈谈spring中的拦截器 在web开发中,拦截器是经常用到的功能.它可以帮我们验证是否登陆.预先设置数据以及统计方法的执行效率等等.今天就来详细的谈一下spring中的拦截器.spring中拦截器主 ...

  6. re模块的使用

    re模块下的函数 compile(pattern):创建模式对象 import re pat = re.compile('D') m = pat.search('CBA') #等价于re.search ...

  7. ios---运用MJRefresh组件设置下拉刷新

    #import "XMGTopicViewController.h" #import <AFNetworking.h> #import <MJExtension. ...

  8. 关于Windows Server 服务器 安装tomcat部署Java Web 项目的问题

    我遇到的问题是:不知道怎么配置,感觉在服务器上部署一个web项目,应该是很高大上,步骤应该很繁琐,但是,事实却不是那样.配置反而挺简单. tomcat配置:在tomcat安装目录中的conf文件夹下有 ...

  9. mybatis从数据库中取数据且分组,返回分组数据

    mapper.xml文件 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PU ...

  10. qt creator源码全方面分析(2-1-1)

    目录 C++的策略/二进制兼容性问题 定义 ABI注意事项 可做与不可做 库程序员的技巧 位标志 使用d指针 故障排除 在没有d指针的情况下将新数据成员添加到类中 添加已重新实现的虚函数 使用新类 向 ...