== vs. Object#equals() to accelerate Collection#contains()
问题的描述
众所周知,在需要将对象进行大量比较(equals)的场景,比如List#contains()的大量调用中,Object#equals(Object)实现的效率是很重要的。
提高对象比较效率的途径之一是用地址比较来代替内容比较。比如String#equals(Object)实现的内部逻辑应该是先进行地址比较,看是不是同一个对象;否则再进行内容比较。
但是String#equals(Object)还是不能彻底摆脱内容比较。
以String为例,我们来讨论用纯粹地址比较来实现Object#equals(Object)的可能性。
String的内部实现
其实,Java编译对字符串赋值的处理和String#intern()提供了将字符串对象放在常量池(Constant Pool)里,并且内容相同的字符串共享同一对象的可能 -- 它们的引用指向同一片地址。
如果这种常量池和共享的模式做彻底了,对字符串的比较就可以用纯粹地址比较。
但是,考虑到有些字符串,比如仅仅是用于日志打印输出,其实生存周期很短。所以并不是所有的字符串都需要以共享的方式放在常量池中,它们也应被允许生存在Heap中,甚至Eden中,能迅速被回收。而且有重复对象(redundance)。
这种灵活性,使String无法做纯粹的地址比较。
Symbol vs. String
虽然无法控制JVM底层内存管理机制,但我们仍然可以模拟常量池,并对对象做纯粹的地址比较。
package trial;
import java.util.HashMap;
import java.util.Map;
public class Symbol {
private final static Map<String, Symbol> symbolPool = new HashMap<String, Symbol>();
public static Symbol newInstance(String content) {
String internContent = content.intern();
Symbol symbol = symbolPool.get(internContent);
if (symbol == null) {
symbol = new Symbol(internContent);
symbolPool.put(internContent, symbol);
}
return symbol;
}
private String stringValue;
private Symbol(String content) {
this.stringValue = content;
}
@Override
public int hashCode() {
return this.stringValue.hashCode();
}
@Override
public String toString() {
return stringValue;
}
}
特征
使用Symbol的前提是,需要大量对象比较。而且因为实际的需要,即便不放在常量池中,对象的生存周期也较长。
用Symbol做容器(Colletion)类的元素, 能够起到同时降低空间复杂度和时间复杂度的效果。
因为元素域(range of element)可能无限,但元素的值域(the range of element content value)是有限的。或者说元素的个数可以很多,但它们的值很多是重复的。这样通过对象共享,可以降低内存消耗。
另一方面,Symbol#equals(Object)比String#equals(Object)快很多,至少快一倍,而且随着字符串内容长度的增加,Symbol#equals(Object)速度不变,而String#equals(Object)会成倍降低。附件中是我测试的代码。
其他可能
为什么不能通过重载和实现具体的容器类(AddressCompareList),达到用==来比较对象元素的效果呢?因为这种方案无法保证另一个前提,即传入的相同对象共享地址。
建议
但是在上面给出的例子中,为了方便,Symbol以String对象为成员,其实是一种浪费。
以后的JDK可以新提供一个Symbol类。用Char数组为核心成员,按String的实现,把它作为常量String(the Sharing String cached in Constant Pool)来实现。
分享到:
相关推荐
- **概念**:集合是指一组不重复的元素的无序组合,在Java中主要由`Collection`和`Map`接口及其子接口和实现类构成。 - **特点**: - 存储的是对象而非原始类型。 - 元素不重复。 - 无序性。 - 动态大小。 ####...
这部分内容涵盖了Java标准库中的一些重要类和接口,包括`Object`类、`String`类、`StringBuilder`类、集合框架等。 - **`Object`类**:所有Java类的父类,提供了如`toString()`、`equals(Object obj)`等通用方法。 ...
- **概念**:`DataSet`是一个内存中的数据容器。 - **应用场景**: - 数据查询。 - 数据缓存。 #### 42. 使用 Attributes - **概念**:属性为类或方法添加元数据。 - **应用场景**: - 自动化工具。 - 配置信息...
- **对Servlet的依赖性强**:由于紧密地结合了Servlet容器,这可能导致在非Servlet环境中部署和测试时遇到困难。 #### Hibernate的优缺点 **优点:** - **高性能**:利用Java反射机制,Hibernate能够高效地管理和...
**强制规定**:在 `subList` 场景中,需要注意对原始集合元素的增删操作,这些操作都可能导致子列表的遍历、增删产生 `ConcurrentModificationException` 异常。 **说明**:在使用 `subList` 方法时,对原始列表的...
18. **Java容器类**:Java容器类主要包括两大类,Collection和Map。Collection下面包括List、Set等接口,Map下面包括HashMap、TreeMap等实现类。 19. **Collection和Collections的区别**:Collection是一个接口,是...
- 集合框架中容器简单用法:集合框架提供了多种容器类,如List、Set、Map等,用于存储和操作对象集合。 九、网络编程: - 正则表达式获取字符串中ip地址:可以使用正则表达式匹配字符串中的IP地址模式。 十、高新...
- **axman对equal的深入研究**:讨论了Java中的equals()方法和Object类的默认实现,以及何时应该重写这个方法来实现正确的对象比较。 - **为什么要始终使用PreparedStatement代替Statement**:PreparedStatement...
在编程世界中,Java语言以其跨平台性和强大的库支持成为了许多开发者的选择。为了写出高效、可维护、易于理解的Java代码,遵循一定的编码规范和最佳实践至关重要。以下是一些关于"JAVA代码编写的建议30条"的具体内容...