`
shannon977
  • 浏览: 20168 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
最近访客 更多访客>>
社区版块
存档分类
最新评论

==代替Object#equals() - 加速在容器类中搜索元素速度的可能性

阅读更多

== 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)来实现。

分享到:
评论
1 楼 liugm1 2013-06-25  
学习下,一种好思路。不过没运行测试过,我觉得现代cpu的运算速度应该可以减少String与Symbol两种方式的速度差别吧。

相关推荐

    Java集合容器面试题(2022最新版)-重点.docx

    - **概念**:集合是指一组不重复的元素的无序组合,在Java中主要由`Collection`和`Map`接口及其子接口和实现类构成。 - **特点**: - 存储的是对象而非原始类型。 - 元素不重复。 - 无序性。 - 动态大小。 ####...

    JAVA学习笔记(全面)

    这部分内容涵盖了Java标准库中的一些重要类和接口,包括`Object`类、`String`类、`StringBuilder`类、集合框架等。 - **`Object`类**:所有Java类的父类,提供了如`toString()`、`equals(Object obj)`等通用方法。 ...

    提高C#编程50要点

    - **概念**:`DataSet`是一个内存中的数据容器。 - **应用场景**: - 数据查询。 - 数据缓存。 #### 42. 使用 Attributes - **概念**:属性为类或方法添加元数据。 - **应用场景**: - 自动化工具。 - 配置信息...

    java面试题经典

    - **对Servlet的依赖性强**:由于紧密地结合了Servlet容器,这可能导致在非Servlet环境中部署和测试时遇到困难。 #### Hibernate的优缺点 **优点:** - **高性能**:利用Java反射机制,Hibernate能够高效地管理和...

    阿里开发规范(集合与并发处理)

    **强制规定**:在 `subList` 场景中,需要注意对原始集合元素的增删操作,这些操作都可能导致子列表的遍历、增删产生 `ConcurrentModificationException` 异常。 **说明**:在使用 `subList` 方法时,对原始列表的...

    1剑盛二面准备试题.txt1剑盛二面准备试题.txt

    18. **Java容器类**:Java容器类主要包括两大类,Collection和Map。Collection下面包括List、Set等接口,Map下面包括HashMap、TreeMap等实现类。 19. **Collection和Collections的区别**:Collection是一个接口,是...

    Java经典问题答案(带书签)

    - 集合框架中容器简单用法:集合框架提供了多种容器类,如List、Set、Map等,用于存储和操作对象集合。 九、网络编程: - 正则表达式获取字符串中ip地址:可以使用正则表达式匹配字符串中的IP地址模式。 十、高新...

    WebLogic Web Development

    - **axman对equal的深入研究**:讨论了Java中的equals()方法和Object类的默认实现,以及何时应该重写这个方法来实现正确的对象比较。 - **为什么要始终使用PreparedStatement代替Statement**:PreparedStatement...

    JAVA代码编写的建议30条

    在编程世界中,Java语言以其跨平台性和强大的库支持成为了许多开发者的选择。为了写出高效、可维护、易于理解的Java代码,遵循一定的编码规范和最佳实践至关重要。以下是一些关于"JAVA代码编写的建议30条"的具体内容...

Global site tag (gtag.js) - Google Analytics