关于Java中使用容器的几个注意点
在看老代码时,看到一处使用HashSet的场景,检查了放入HashSet的类型参数,发现这个类型并没有重写equals和hashCode方法,这个后果的严重程度可想而知。就此暂时总结了以下几点,并配合测试代码,共勉!
总结点如下:
1. 使用HashSet/HahsMap时,定义的元素/key的类型必须同时重写equals和hashCode方法。
2. TreeSet来说,只需实现Comparable接口,不需要重写equals和hashCode方法,至少java6是这样
3. 对其他容器(无hash),建议都重写equals方法,否则无法支持查找和删除操作。比如使用PriorityQueue时,若仅实现Comparable只支持对象的插入,只有在自定义类实现了equals方法后,才支持查找、删除等操作了。
其中,最重要的就是第一条,需谨记。若不确定对象会被如何使用,建议对任何自定义类型重写equals、hashCode和toString方法。
测试代码中示例类型的说明:
BasicType
- 一个基础的类,包含三个字段,只重写了toString方法
ComparableType
- 一个可比较的类,只实现了Comparable接口
EqualsType
- 在BasicType的基础上重写了equals方法
HashCodeType
- 在BasicType的基础上重写了hashCode方法
EqualsComparableType
- 在ComparableType的基础上重写了equals方法
HashType
- 在EqualsType基础上重写了hashCode方法
设计的类图结构:
测试用例
对HashSet的测试
使用BasicType对HashSet进行测试
- /**
- * 使用{@link BasicType}对HashSet进行测试,对不同对象,equals为false,hashCode不相等<br>
- * 因此任一{@link BasicType}的对象在HashSet中都是唯一的,见测试{@link #testBasicTypeInHashSet}
- *
- * @see BasicType
- * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet
- */
- @Test
- public void testBasicTypeInHashSet() {
- Set<BasicType> set = new HashSet<BasicType>();
- set.add(new BasicType());
- set.add(new BasicType());
- set.add(new BasicType());
- set.add(new BasicType());
- BasicType t = new BasicType();
- set.add(t);
- set.add(t);
- // size为5,不是我们想要的
- Assert.assertEquals(set.size(), 5);
- }
使用EqualsType对HashSet进行测试
- /**
- * 使用{@link EqualsType}对HashSet进行测试,对不同对象,equals可能为true,hashCode不相等<br>
- * 任一{@link EqualsType}的对象在HashSet中都是唯一的
- *
- * @see EqualsType
- * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet
- */
- @Test
- public void testEqualsTypeInHashSet() {
- Set<EqualsType> set = new HashSet<EqualsType>();
- set.add(new EqualsType());
- set.add(new EqualsType());
- set.add(new EqualsType());
- set.add(new EqualsType());
- EqualsType t = new EqualsType();
- set.add(t);
- set.add(t);
- // size为5,不是我们想要的
- Assert.assertEquals(set.size(), 5);
- }
使用HashCodeType对HashSet进行测试
- /**
- * 使用{@link HashCodeType}对HashSet进行测试,对不同对象,equals可能为false,hashCode可能相等<br>
- * 任一{@link HashCodeType}的对象在HashSet中都是唯一的
- *
- * @see HashCodeType
- * @see test.HashTypeTest#testHashTypeInHashSet testHashTypeInHashSet
- */
- @Test
- public void testHashCodeTypeInHashSet() {
- Set<HashCodeType> set = new HashSet<HashCodeType>();
- set.add(new HashCodeType());
- set.add(new HashCodeType());
- set.add(new HashCodeType());
- set.add(new HashCodeType());
- HashCodeType t = new HashCodeType();
- set.add(t);
- set.add(t);
- // size为5,不是我们想要的
- Assert.assertEquals(set.size(), 5);
- }
使用HashType对HashSet进行测试
- /**
- * 使用{@link HashType}对HashSet进行测试,对不同对象,equals可能为true,hashCode可能相等<br>
- * 此时可以用HashSet去除重复对象(hashCode相等且是equals的),正是我们想要的
- *
- * @see HashType
- */
- @Test
- public void testHashTypeInHashSet() {
- Set<HashType> set = new HashSet<HashType>();
- set.add(new HashType());
- set.add(new HashType());
- set.add(new HashType());
- set.add(new HashType());
- HashType t = new HashType();
- set.add(t);
- set.add(t);
- // 相等值对象被去除,size为1, 正是我们想要的
- Assert.assertEquals(set.size(), 1);
- }
对TreeSet的测试
使用ComparableType对TreeSet进行测试
- /**
- * 使用ComparableType对TreeSet进行测试<br>
- * 使用TreeSet的类必须实现Comparable接口,但不必重写equals和hashCode方法,与TreeSet的内部实现有关
- *
- * @see tijava.container.type.ComparableType
- */
- @Test
- public void testComparableTypeInTreeSet() {
- Set<ComparableType> q = new TreeSet<ComparableType>();
- q.add(new ComparableType('d', 3, null));
- q.add(new ComparableType('d', 4, null));
- q.add(new ComparableType());
- q.add(new ComparableType());
- q.add(new ComparableType());
- q.add(new ComparableType());
- q.add(new ComparableType());
- Assert.assertEquals(q.size(), 3);
- Assert.assertTrue(q.contains(new ComparableType('d', 3, null)));
- q.remove(new ComparableType('d', 3, null));
- //remove ok
- Assert.assertEquals(q.size(), 2);
- }
对PriorityQueue的测试
使用ComparableType对PriorityQueue进行测试
- /**
- * 使用ComparableType对PriorityQueue进行测试<br>
- * 在PriorityQueue中使用自定义类时,若只实现Comparable接口的类型,不支持查找和删除等操作
- *
- * @see test.EqualsComparableTypeTest#testEqualsComparableTypeInPriorityQueue
- * testEqualsComparableTypeInPriorityQueue
- */
- @Test
- public void testComparableTypeInPriorityQueue() {
- Queue<ComparableType> q = new PriorityQueue<ComparableType>();
- q.add(new ComparableType('C', 4, "Empty trash"));
- q.add(new ComparableType('A', 2, "Feed dog"));
- q.add(new ComparableType('B', 7, "Feed bird"));
- q.add(new ComparableType('C', 3, "Mow lawn"));
- q.add(new ComparableType('A', 1, "Water lawn"));
- q.add(new ComparableType('B', 1, "Feed cat"));
- Assert.assertEquals(q.size(), 6);
- Assert.assertFalse(q
- .contains(new ComparableType('C', 4, "Empty trash")));
- Assert.assertFalse(q.remove(new ComparableType('C', 4, "Empty trash")));
- //siz is still 6, not remove success
- Assert.assertEquals(q.size(), 6);
- }
使用EqualsComparableType对PriorityQueue进行测试
- /**
- * 使用EqualsComparableType对PriorityQueue进行测试<br>
- * 在使用PriorityQueue是,在自定义类实现了equals方法后,就支持查找、删除等操作了
- *
- * @see test.ComparableTypeTest#testComparableTypeInPriorityQueue
- * testComparableTypeInPriorityQueue
- */
- @Test
- public void testEqualsComparableTypeInPriorityQueue() {
- Queue<EqualsComparableType> q = new PriorityQueue<EqualsComparableType>();
- q.add(new EqualsComparableType('C', 4, "Empty trash"));
- q.add(new EqualsComparableType('A', 2, "Feed dog"));
- q.add(new EqualsComparableType('B', 7, "Feed bird"));
- q.add(new EqualsComparableType('C', 3, "Mow lawn"));
- q.add(new EqualsComparableType('A', 1, "Water lawn"));
- q.add(new EqualsComparableType('B', 1, "Feed cat"));
- Assert.assertEquals(q.size(), 6);
- Assert.assertTrue(q.contains(new EqualsComparableType('C', 4,
- "Empty trash")));
- Assert.assertTrue(q.remove(new EqualsComparableType('C', 4,
- "Empty trash")));
- // remove ok
- Assert.assertEquals(q.size(), 5);
- }
相关推荐
在Java编程中,容器是用于...总之,理解并掌握这些Java容器的特性和效率差异,能够帮助我们在实际开发中做出更合适的选择,提高程序的性能和可维护性。在具体应用时,还需要结合业务需求和性能测试,才能做出最佳决策。
这个压缩包文件包含了几个关于AWT的实际应用示例,对于初学者来说是非常有价值的参考资料。 首先,我们要理解AWT的基础概念。AWT是基于原生操作系统API的,这意味着它的组件在不同的操作系统上可能会有不同的表现。...
### Cognos在其他JAVA容器中的部署 #### 知识点概述 本文主要介绍如何将Cognos 8部署在TongWeb等不同JAVA容器上。Cognos是一款功能强大的商业智能工具,能够帮助企业实现数据可视化、报表制作等功能。为了确保...
在Java容器API中,有几个重要的方法需要理解: - `size()`:返回集合中元素的数量。 - `isEmpty()`:检查集合是否为空。 - `clear()`:清空集合中的所有元素。 - `contains(Object element)`:检查集合是否包含特定...
在这个“Java初学者 几个小游戏的代码”压缩包中,包含了一系列适合新手学习的Java代码示例。让我们逐一探讨这些小游戏背后的编程知识。 1. **猜数字游戏**: 猜数字游戏通常涉及到随机数生成、循环控制和条件判断...
Java容器主要分为四大类:List、Set、Queue和Map,每种都有其特定的用途和实现方式。下面我们将深入探讨这些容器及其相关的实现版本。 1. **List**: List是一种有序的容器,允许重复元素,并且支持索引访问。Java...
在 Java GUI 应用程序中,`JComboBox` 的使用通常包括以下几个关键步骤: 1. **创建 `JComboBox` 对象**: 创建 `JComboBox` 需要调用其构造函数,可以传入一个 `Object` 数组或 `Vector` 作为选项。例如: ```...
在Java 1.8中,有几个核心的更新和增强,包括: 1. **Lambda表达式**:这是Java 8的一大亮点,引入了函数式编程的概念。Lambda表达式可以用来创建匿名函数,简化了对集合的操作,特别是`Stream API`的使用。例如,`...
总结来说,实现一个简单的IoC容器涉及以下几个步骤: 1. 定义bean的配置(XML或注解)。 2. 解析配置,创建BeanDefinition。 3. 实现BeanFactory接口,包括bean的实例化、依赖注入和生命周期管理。 4. 使用反射处理...
在设计这样的程序时,我们需要注意以下几个关键点: 1. **数据同步**:确保在读写操作时对共享数据的访问是安全的,避免数据不一致。 2. **线程交互**:合理安排线程间的执行顺序,防止读写冲突。 3. **资源管理**...
Java 定时任务是指在 Java 语言中实现的定时执行任务的机制,通过使用 Timer 和 TimerTask 两个类,可以实现定时执行任务的功能。在这个例子中,我们将实现每天定时执行任务的功能,具体来说,就是在每天的凌晨 2 点...
在这个名为"Swingdemo"的压缩包中,我们可以期待找到一些使用Swing库创建的示例程序,这些程序对于初学者来说是非常有价值的实践资源。 Swing库提供了许多组件,用于构建桌面应用程序,如按钮(JButton)、文本框...
在Java工程师面试中,面试者通常需要对以下几个关键知识点有深入理解: 1. **Java基础**:包括语法、面向对象特性(封装、继承、多态)、异常处理、集合框架(List、Set、Map等)、IO流、线程、反射等。 2. **JVM*...
JFrame 有几个重要的方法,例如 setUndecorated(true) 可以删除边框,getRootPane().setWindowDecorationStyle(JRootPane.NONE) 可以采用指定的窗口装饰风格。JFrame 还可以获取屏幕的宽度和高度,例如 Toolkit....
本资料“Java开发学习之Java基础语法注意点共2页.pdf”着重讲解了Java编程的基础语法,这对于初学者或者希望巩固基础知识的开发者来说至关重要。在Java的学习过程中,掌握好基础语法是构建强大编程能力的基石。 ...
Java容器集合是Java中的一种基础数据结构,用于存储和管理数据。其中,equals和hashCode方法是Java容器集合中两个非常重要的方法,本文将详细介绍这两个方法,并结合ArrayList、Vector和LinkedList三个常见的容器...
一个 Java 程序通常由以下几个部分组成: 1. 包名(package):用于标识类所属的包名。 2. 导入语句(import):用于导入其他类或包。 3. 类定义(class):用于定义类的名称、属性和行为。 4. 主方法(main):...
本篇文章将深入探讨如何使用Java实现一个简单的IOC容器。 ### 1. IOC容器的基本概念 IOC的核心思想是“依赖注入”(Dependency Injection,DI)。在传统的编程中,对象A依赖于对象B,通常在A的构造函数或者初始化...
Java容器是Java开发中非常重要的一部分,它提供了存储和管理对象的方式。在Java开发中,容器是指可以存储其他对象的类,或者说是集合,即将多个对象组合在一起形成一个整体。下面我们将详细介绍Java容器的概念、分类...