原帖地址
http://www.javablogging.com/type_safety_in_java_set_and_map/
也许你们大家都仍然记得在java1.4中类型检查和集合中元素的强制转型是多么麻烦,根本没法确定代码中有多少潜在的错误。由于在java1.5中通过泛型的说明这种状况确实得到改善,并且现在的语言开始照顾到了经常打错字的程序员。泛型自身给我们带来了一种新的复杂的设置,但是好像我们有理由相信基本情况下通过这种在java的Sets和Maps中使用,并不会带来类似通配符的并发症,转换应该是安全可靠的。真的是这样吗?
最近我遇到了一个生产代码中的bug发生在一个简单的(真的很简单的)Map的应用中,令人尴尬的是我用很多方法去调试它,结果确实......唉......一个小问题。下面的代码演示了一个bug产生前代码的一个轮廓,在真实的生活中class中代码部分是很好的而且会经常修改的,肯定要比下面的代码多的多:
import java.util.HashMap;
import java.util.Map;
public class EmployeeDataLookup {
// A map storing relation between employee ID and name.
private Map<Integer, String> employeeIdToName;
public EmployeeDataLookup() {
// Create a new EmployeeDataLookup and initialize
// it with employee names and IDs.
employeeIdToName = new HashMap<Integer, String>();
addEmployee(301, "John Doe");
addEmployee(302, "Mary Poppins");
addEmployee(303, "Andy Stevens");
}
public void addEmployee(int employeeId, String employeeName) {
employeeIdToName.put(employeeId, employeeName);
}
// This class is very complicated and has many other methods...
// Lookup method for finding employee name for given employee ID.
public String findEmployeeName(int employeeId) {
return employeeIdToName.get(employeeId);
}
public static void main(String[] args) {
// Create a EmployeeDataLookup instance
EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
// Find the name of an employee with ID = 301
String employeeName = employeeLookup.findEmployeeName(301);
System.out.print("Employee 301 : " + employeeName);
}
}
这有什么问题呢?哦,在一个项目中功能要求发生了变化(有点惊讶,是吧?)和Map中Key的内部数据必须发生了变化。例如公司是合并的供应商之一,因此系统存储employee数据必须做出跟供应商ID兼容的例子。由于供应商用的是13位的IDs,所以我们必须java代码中Integer替换成Long。简单吧?着看起来很简单,我们用类似于eclipse的ide的重构--只要改变第7行的Map key的定义Integer变为Long,让编辑器显示所有的错误,然后修复他们!五分钟搞定:
import java.util.HashMap;
import java.util.Map;
public class EmployeeDataLookup2 {
// A map storing relation between employee ID and name.
private Map<Long, String> employeeIdToName;
public EmployeeDataLookup2() {
// Create a new EmployeeDataLookup and initialize
// it with employee names and IDs.
employeeIdToName = new HashMap<Long, String>();
addEmployee(301, "John Doe");
addEmployee(302, "Mary Poppins");
addEmployee(303, "Andy Stevens");
}
public void addEmployee(long employeeId, String employeeName) {
employeeIdToName.put(employeeId, employeeName);
}
// This class is very complicated and has many other methods...
// Lookup method for finding employee name for given employee ID.
public String findEmployeeName(int employeeId) {
return employeeIdToName.get(employeeId);
}
public static void main(String[] args) {
// Create a EmployeeDataLookup instance
EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
// Find the name of an employee with ID = 301
String employeeName = employeeLookup.findEmployeeName(301);
System.out.print("Employee 301 : " + employeeName);
}
}
这段改变导致了一个错误,你能发现吗?当您运行main方法的时候你会发现,该员工的ID=301的值是“null”,哈!
如果没有发现个问题(或者懒得去尝试)我给你一个提示是去检查25行和26行,如果你能发现它,我们尝试想一下现实生活中的class有1000+的代码和不确定的要改变的Map,由于我们没有编译错误,我们就像大海捞针一样找它,这为什么产生这个bug。
这个问题是在引入了泛型,可能为了向后兼容,一些方法在Collection and Map interface 并没有修改和仍然没有进行类型检查。其中一个我们发现的方法就是Map.get(Object)导致我们上面的错误。经过“简单修改”后我们仍然用Interger keys去访问包含了Long类型的Map,所以我们即使是有301的employee仍然get返回null,而且由于缺乏类型检查并没有警告......
那么我们能得到上面教训?要谨慎关于基本情况,甚至类型检查。记住那些访问方法在Sets,Lists和Maps可能伤害你,最大的痛苦是通过这种方式产生的bug很被人识别,有时候甚至十几人对着代码都难找出它。有一个办法可以找到他们——如果你正在使用静态代码分析工具,大部分这样的bug都可以很容易被识别(如:findBUGs)。
分享到:
相关推荐
在Java编程语言中,集合框架是处理对象组的重要工具,主要包括List、Set和Map三大接口。这些接口由Java Collection Framework提供,它是一个统一的架构,用于存储和操作各种类型的对象。接下来,我们将深入探讨这三...
Array 是 Java 中最基本的集合类,它是固定大小的、线程安全的,可以存储基本类型和对象类型的数据。Arrays 类是 Java 中提供的一个工具类,提供了数组的操作方法,如 equals()、fill()、sort()、binarySearch() 等...
`golang-set`库提供了一种实现,包括线程安全和非线程安全的高性能集,非常适合在Go的并发环境中使用。 首先,我们要理解什么是线程安全和非线程安全。线程安全指的是在多线程环境下,一个函数或方法在同一时刻可以...
Java集合框架是编程中不可或缺的一部分,它提供了多种数据结构,如List、Set和Map,用于高效地存储和操作数据。这些集合各有特点,适用场景也有所不同。 1. List与Set的区别: - List是一个有序的集合,允许重复...
在Java编程语言中,Map和Set是两种非常重要的集合类型。Map主要用于存储键值对,而Set则是一个不包含重复元素的集合。本篇将详细讲解如何利用Map对象来创建Set对象,这对于Java开发者来说是一个实用的经验技巧。 ...
### Java集合Collection、List、Set、Map使用详解 ...以上就是Java集合框架中主要类型的详细介绍,包括它们的特性和使用场景。理解这些集合的特点可以帮助开发者更好地选择合适的容器来满足应用程序的需求。
本篇文章将深入探讨集合框架中的三大核心组件:`List`、`Set`以及`Map`,并通过具体的接口和类来分析它们的特点和应用场景。 #### 集合接口 在Java集合框架中,主要有以下几种关键接口: - **`Collection`接口**:...
在Java编程语言中,集合框架是处理对象集合的重要工具,主要包括了List、Set、Map以及Table等接口及其实现类。这些接口和类各有特点,适用于不同的数据存储和操作场景。 1. **List接口**: - List是Collection的一...
集合框架包括多个接口和类,如Collection、List、Set和Map,它们各自有不同的特性和用途。 1. **集合框架概述** 集合框架是Java中用于存储和管理对象的统一框架。它定义了一系列接口,如Collection、List、Set和...
### Collection、Map、List、Set、...以上就是关于 `Collection`、`Map`、`List`、`Set` 和 `Iterator` 的详细解析,这些概念和类是 Java 编程中非常基础且重要的部分,掌握它们有助于更好地理解和使用 Java 集合框架。
在C++编程中,Python的内置数据结构如List, Set, Map等因其便捷性和灵活性而备受程序员喜爱。为了在C++环境中实现类似的便利性,我们可以创建一个库,模仿Python的这些容器并结合Integer, String, Tuple, Deque等...
本案例集合了ArrayList、Iterator、Vector、Map和Set等关键接口和类的详细实例和解析,帮助开发者深入理解这些核心概念。 首先,ArrayList是Java中最常用的列表实现,它基于动态数组。ArrayList提供了按索引访问...
为了确保`Map`中的键能够正确地存储和检索,需要正确地覆写`hashCode()`和`equals()`方法。这有助于提高哈希表的性能。 #### 五、Set `Set`是不允许重复元素的集合。它主要用于存储无序且唯一的元素。 ##### 5.1 ...
本文将深入探讨标题和描述中提到的一些关键集合类型,包括Map、Set、List、Queue、Stack以及它们的特点和用法。 首先,Collection接口是所有集合类型的父接口,它分为两个主要子接口:List和Set。List接口用于存储...
在Java编程语言中,集合框架是处理对象的重要工具,其中包括List、Set和Map三类主要接口,它们都属于java.util包下的Collection接口的子接口或实现类。这三种类型的集合各有其特性和用途,理解它们之间的区别对于...
泛型是Java 5引入的一项功能,它允许我们在类、接口和方法中使用类型参数,以提高代码的类型安全性和可读性。在我们的场景中,泛型可以帮助我们创建一个通用的工具类,该类可以处理任何类型的Map和实体对象。 接...
它们在很多场景下能够替代Java标准库中的`Collections.unmodifiable*`方法,提供更强的类型安全性和性能。 使用不可变集合有以下好处: - **线程安全**:不可变对象天生就是线程安全的,可以减少在多线程环境中的...
总的来说,Java中的枚举是一种强大的特性,提供了类型安全的常量集合,并结合`EnumSet`和`EnumMap`这两个工具类,可以在处理枚举数据时实现高效的编程。理解并善用这些概念,将有助于编写更加可靠和高效的Java代码。
3. **健壮性**:Java设计时注重安全性和错误处理,例如通过强类型检查和异常处理机制。 4. **多线程**:Java内置了对多线程编程的支持,允许开发者创建同时执行的多个线程。 5. **网络编程**:Java提供了丰富的...