当前位置: 首页 > 知识库问答 >
问题:

与equals相比,使用==操作符如何提高性能?

孟高峰
2023-03-14

在Joshua Bloch的有效JAVA中,当我阅读静态工厂方法时,有一个声明如下

静态工厂方法从重复调用中返回相同对象的能力允许类在任何时候对存在的实例保持严格控制。这样做的类称为实例控制类。编写实例控制类有几个原因。实例控件允许类保证它是单实例(第3项)或不可实例化(第4项)。此外,它允许一个不可变类(项目15)保证不存在两个相等的实例:a.equals(b)当且仅当a==b。如果一个类做出此保证,则其客户端可以使用==运算符而不是equals(Object)方法,这可能会提高性能。枚举类型(项目30)提供了这一保证。

为了研究==运算符如何带来性能改进,我必须看看String.java

我看到了这个片段

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

关于绩效改进,他在这里指的是什么?它如何带来性能改进。

他是不是想说

如果每个类都能确保a.equals(b)当且仅当a==b时,这意味着它带来了一个间接要求,即不能有对象引用2个不同的内存空间,并且仍然保存相同的数据,这就是内存浪费。如果它们持有相同的数据,那么它们就是同一个对象。也就是说,它们指向相同的内存位置。

我的推断对吗?

如果我错了,你能指导我理解这一点吗?

共有3个答案

商振
2023-03-14

如果您可以保证一个对象的两个实例不存在,使得它们的语义值是等价的(即如果xy引用不同的实例[x!=y]然后x.equals(y)==falsefor allxandy),那么这意味着您可以简单地比较两个引用的对象是否相等,只需检查它们是否引用同一个实例,这就是==所做的。

==的实现本质上只是比较两个整数(内存地址),通常比. equals()的几乎所有非平凡实现都要快。

值得注意的是,这不是可以为Strings进行的跳转,因为您不能保证String的任何两个实例不等价,例如:

String x = new String("hello");
String y = new String("hello");

自<代码>x!=Y

堵毅然
2023-03-14
匿名用户

引用部分的意思是不可变类可以选择实习其实例。这很容易通过Guava的Interner实现,例如:

public class MyImmutableClass {
    private static final Interner<MyImmutableClass> INTERN_POOL = Interners.newWeakInterner();
    private final String foo;
    private final int bar;

    private MyImmutableClass(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public static MyImmutableClass of(String foo, int bar) {
        return INTERN_POOL.intern(new MyImmutableClass(foo, bar));
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(foo, bar);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;        // fast path for interned instances
        if (o instanceof MyImmutableClass) {
            MyImmutableClass rhs = (MyImmutableClass) o;
            return Objects.equal(foo, rhs.foo)
                    && bar == rhs.bar;
        }
        return false;
    }
}

这里,构造函数是私有的:所有实例都必须通过MyImmutableClass。of()工厂方法,该方法使用内部程序确保如果新实例等于现有实例,则返回现有实例。

Interning只能用于不可变对象,我指的是对象的可观察状态(即其所有外部可访问方法的行为,尤其是equals()hashCode())不会因对象的生命周期而改变的对象。如果插入可变对象,则修改实例时的行为将是错误的。

正如许多其他人已经说过的,您应该仔细选择要实习的对象,即使它们是不可变的。只有当被实习的值集相对于您可能拥有的重复数量较小时才这样做。例如,通常不值得实习intger,因为有40亿可能的值。但值得实习最常用的intger值,事实上,Integer.valueOf()实习生值在-128到127之间。另一方面,枚举非常适合实习(根据定义,它们是被实习的),因为可能的值集很小。

一般来说,对于大多数类,您必须进行堆分析,例如使用jhat(或者,为了插入我自己的项目,Fasthat)来决定是否有足够的重复项来保证实习。在其他情况下,只需保持简单,不要实习。

曹均
2023-03-14

如果每个类都能确保a.equals(b)当且仅当a==b时,这意味着它带来了一个间接要求,即不能有对象引用2个不同的内存空间,并且仍然保存相同的数据,这就是内存浪费。如果它们持有相同的数据,那么它们就是同一个对象。也就是说,它们指向相同的内存位置。

是的,这就是作者的意图。

如果可以(对于给定的类,这对所有人都不可能,特别是它不能适用于可变类)调用==(这是单个JVM操作码)而不是equals(这是一个动态调度的方法调用),它节省了(一些)开销。

例如,它适用于枚举s。

即使有人调用了等于方法(这是一种很好的防御编程实践,您不想养成对IMHO对象使用=的习惯),该方法也可以实现为一个简单的=(而不必查看潜在的复杂对象状态)。

顺便说一句,即使对于“普通”equals方法(如String),在其实现中,最好先检查对象标识,然后快速查看对象状态(正如您所发现的,String#equals就是这样做的)。

 类似资料:
  • 问题内容: 我在基于JBoss的Web应用程序中使用JAXBContext.newInstance操作。据我了解,此操作非常繁重。我只需要Marshaller类的两个唯一实例。 我最初的建议是要有一个静态初始值设定项块,该类将在加载类时仅初始化一次这两个实例: 如果这是一个合理的解决方案,那么我想我会回答自己的问题,但是我想知道这是否是正确的方法? 问题答案: JAXB实现(Metro,Eclip

  • 我正在开发和运行基于groovy的Jenkins管道作业,我们的代码执行多个线程,需要高CPU和内存。 由于JProfiler是一个功能强大的Java评测工具,因此我想在Jenkins管道作业中收集有关我的groovy代码的性能信息,这些作业在代理上运行,因此我将能够排除代码缺陷并优化代码。 安装JProfiler的最佳实践是什么?(远程KVM或在Jenkins或本地代理上?)

  • 本页内容包括: 位运算符 溢出运算符 优先级和结合性(Precedence and Associativity) 运算符函数(Operator Functions) 自定义运算符 除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语言和Objective-C中的位运算符和移位运算。 不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误

  • 测试代码(只是为了遵守SSCCE,显然有更好的方法来改变您自己的不区分大小写的数据模型) 比方说,我希望我的类实现遵循API中建议的强烈建议: 强烈建议,但不是严格要求(x.compareTo(y)==0) == ( x.equals(y))。 那么在中使用可以吗?当然,我们正在确保不会调用作为回报。 类似:什么时候包括什么?

  • 问题内容: 以下是我用于通过电子邮件搜索人的查询 在“电子邮件”上添加索引会加快查询速度吗? 问题答案: 不可以,因为当您使用通配符时,MySQL将无法使用该索引。如果您将LIKE更改为’f%’,那么它将能够使用索引。

  • 问题内容: Docker和OpenShift都是用于实现PaaS服务的框架。 他们如何比较架构和功能? 问题答案: 主要区别在于,Docker作为一个项目仅专注于运行时容器,而OpenShift(作为系统)既包含运行时容器也包括REST API,协调和Web界面,以部署和管理各个容器。 仅比较运行时容器,OpenShift和Docker都使用内核隔离功能将租户进程分开。对于主要通过LXC实现的Do