我可以缓存getClass.hashCode()吗?

问题描述 投票:0回答:2

无论什么原因,我在我的抽象类中实现了以下

hashCode

@MappedSuperclass
abstract Some {

    @Override
    public boolean equals(final Object obj) {
        // ...
    }

    @Override
    public int hashCode() {
        return getClass().hashCode(); // TODO: cache, maybe?
    }
}
  1. 我可以缓存
    getClass().hashCode()
    的值吗?
  2. 在 JVM 中运行时该值是否有可能被更改?
  3. 这是某种过早的优化吗?
java jpa hashcode premature-optimization
2个回答
3
投票
  1. 我可以缓存
    getClass().hashCode()
    的值吗?

你可以,但是...

  1. 在 JVM 中运行时该值是否有可能被更改?

没有可能。 Java 对象的类无法更改,Java

Class
对象的哈希码(这是
getClass()
的结果类型)也无法更改。

  1. 这是不是过早的优化?

可能是1

但是,从性能角度来看,使用对象类的哈希码作为对象的哈希码是一个非常糟糕的主意

这样做意味着该类的所有实例(例如

Some
)将具有相同的哈希码2。这将导致无数的哈希冲突,并使大多数
HashSet
HashMap
操作成为
O(N)
O(logN)
(取决于您的 Java 版本)而不是
O(1)


1 - 我假设您没有做过一堆没有告诉我们的性能分析。如果您已经已经完成了分析,那么也许这不是一个过早的优化。
2 - 我假设

Some::hashCode
方法没有被
Some
的具体子类覆盖为更明智的东西。


1
投票

Stephen C 的回答是一个很好的答案。然而,为了完整起见,我觉得有必要补充一点,如果一个类是由两个不同的类加载器加载的,那么对于该类

getClass().hashCode()
将返回两个不同的值。

为了验证这一点,我编写了一个程序并使用系统类加载器加载一个类,然后使用我自己的自定义类加载器加载一个类。两者返回不同的

hashCode()

首先是我从 https://www.digitalocean.com/community/tutorials/java-classloader:

复制的自定义类加载器的代码
package com.journaldev.classloader;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * Our Custom ClassLoader to load the classes. Any class in the
 * com.journaldev package will be loaded using this ClassLoader.
 * For other classes, it will delegate the request to its Parent
 * ClassLoader.
 */
public class CCLoader extends ClassLoader {

  /**
   * This constructor is used to set the parent ClassLoader
   */
  public CCLoader(ClassLoader parent) {
    super(parent);
  }

  /**
   * Loads the class from the file system. The class file should be located in
   * the file system. The name should be relative to get the file location
   *
   * @param name Fully Classified name of the class, for example, com.journaldev.Foo
   */
  private Class getClass(String name) throws ClassNotFoundException {
    String file = name.replace('.', File.separatorChar) + ".class";
    byte[] b = null;
    try {
      // This loads the byte code data from the file
      b = loadClassFileData(file);
      // defineClass is inherited from the ClassLoader class
      // that converts byte array into a Class. defineClass is Final
      // so we cannot override it
      Class c = defineClass(name, b, 0, b.length);
      resolveClass(c);
      return c;
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * Every request for a class passes through this method. If the class is in
   * com.journaldev package, we will use this classloader or else delegate the
   * request to parent classloader.
   *
   * @param name Full class name
   */
  @Override
  public Class loadClass(String name) throws ClassNotFoundException {
    if (name.startsWith("com.journaldev.")) {
      return getClass(name);
    }
    return super.loadClass(name);
  }

  /**
   * Reads the file (.class) into a byte array. The file should be
   * accessible as a resource and make sure that it's not in Classpath to avoid
   * any confusion.
   *
   * @param name Filename
   * @return Byte array read from the file
   * @throws IOException if an exception comes in reading the file
   */
  private byte[] loadClassFileData(String name) throws IOException {
    InputStream stream = getClass().getClassLoader().getResourceAsStream(
        name);
    int size = stream.available();
    byte buff[] = new byte[size];
    DataInputStream in = new DataInputStream(stream);
    in.readFully(buff);
    in.close();
    return buff;
  }
}

然后是

Test
类和 main 方法,我们将使用它来使用不同的类加载器加载
Test
类:

package com.journaldev.test;

import com.journaldev.classloader.CCLoader;

public class Test {

  public static void main(String[] args) throws Exception {
    System.out.println(new CCLoader(ClassLoader.getSystemClassLoader())
        .loadClass(Test.class.getCanonicalName()).hashCode());
    System.out.println(ClassLoader.getSystemClassLoader()
        .loadClass(Test.class.getCanonicalName()).hashCode());
  }
}

输出显示使用不同类加载器加载的同一类的不同哈希码:

1554547125
1072591677
© www.soinside.com 2019 - 2024. All rights reserved.