本文地址: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. 开始使用Manjaro

    Manjaro是什么? 一个基于Arch系列,开源的linux发行版 Mnajrao官网了解更多,这里不做更多阐述内容 为什么使用Manjaro 第一点,为了方便自己隔离腾讯网游 第二点,更方便的学习 ...

  2. CQOI十二省联考游记

    Day 0 看似稳如老狗的我实则慌得一逼 看了一上午的CRT,一个字没看进去 我反复安慰自己:我才高一,我才高一 但是,明年的联赛会不会跟今年一样高呢? 明年的心态会不会有现在这么好呢? 明年同届的d ...

  3. Java程序员学习Go语言—之一

    转载:https://www.luozhiyun.com/archives/206 GOPATH 工作空间 GOPATH简单理解成Go语言的工作目录,它的值是一个目录的路径,也可以是多个目录路径,每个 ...

  4. HBase学习总结

    一.HBase介绍 1.基本概念 HBase是一种Hadoop数据库,经常被描述为一种稀疏的,分布式的,持久化的,多维有序映射,它基于行键.列键和时间戳建立索引,是一个可以随机访问的存储和检索数据的平 ...

  5. HashMap实现详解 基于JDK1.8

    HashMap实现详解 基于JDK1.8 1.数据结构 散列表:是一种根据关键码值(Key value)而直接进行访问的数据结构.采用链地址法处理冲突. HashMap采用Node<K,V> ...

  6. 一步一步安装配置Ceph分布式存储集群

    Ceph可以说是当今最流行的分布式存储系统了,本文记录一下安装和配置Ceph的详细步骤. 提前配置工作 从第一个集群节点开始的,然后逐渐加入其它的节点.对于Ceph,我们加入的第一个节点应该是Moni ...

  7. Capslock+程序介绍

    一直为编程时方向键不在盲打区域苦恼,今天接触了一个非常好的软件Capslock+. 软件特别小,一共只有九百多K,甚至不能称为软件,只能算一个很小的脚本了.但解决了我非常大的一个难题.安装好软件后可以 ...

  8. 如何优雅地打印一个Java对象?

    你好呀,我是沉默王二,一个和黄家驹一样身高,和刘德华一样颜值的程序员.虽然已经写了十多年的 Java 代码,但仍然觉得自己是个菜鸟(请允许我惭愧一下). 在一个月黑风高的夜晚,我思前想后,觉得再也不能 ...

  9. Flutter开发之Widget学习

    一.Text 组件 属性  textAlign: TextAlign.left,                           -----文本对齐方式  maxLines: 1,        ...

  10. Sqlite命令行基本操作

    SQLite是遵守ACID的关系数据库管理系统,它包含在一个相对小的C程序库中. 与许多其它数据库管理系统不同,SQLite不是一个客户端/服务器结构的数据库引擎,而是被集成在用户程序中. 1.进入命 ...