1

Whatever the reason, I have the following hashCode implemented in my abstract class.

@MappedSuperclass
abstract Some {

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

    @Override
    public int hashCode() {
        return getClass().hashCode(); // TODO: cache, maybe?
    }
}
  1. Can I cache the value of the getClass().hashCode()?
  2. Is there any possibility of the value being changed while running in a JVM?
  3. Is this some kind of the premature optimization shit?
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184

2 Answers2

3
  1. Can I cache the value of the getClass().hashCode()?

You can, but ...

  1. Is there any possibility of the value being changed while running in a JVM?

No possibility. The class of a Java object cannot change, and the hashcode of a Java Class object (which is the result type for getClass()) cannot change.

  1. Is this premature optimization?

Probably yes1.

However, using the hashcode of the object's class as the hashcode of an object is a very bad idea from a performance perspective.

Doing that means that all instances of the class (e.g. Some) will have the same hashcode2. That will lead to a bazillion hash collisions, and make most HashSet and HashMap operations O(N) or O(logN) (depending on your Java version) rather than O(1).


1 - I am assuming you have not done a bunch of performance analysis that you haven't told us about. If you had already done the analysis, then maybe this is not a premature optimization.
2 - I am assuming that the Some::hashCode method is not overridden to something more sensible by concrete subclasses of Some.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • That's a good answer. I have one confusion: Can there be multiple instances of a `Class`, in case of multiple class loaders loading `Some` class. If yes, then In that case, getClass().hashcode() may return different values – Aqeel Ashiq Feb 17 '23 at 05:03
  • I was talking about multiple class loaders loading `Some` class. Because `getClass().hashcode()` is in `Some` class. So if `Some` class is loaded by multiple class loaders, will `getClass().hashcode()` return different values for both of them? – Aqeel Ashiq Feb 17 '23 at 05:22
  • 1
    Ah ... I misread. If multiple class loaders load `Some`, then they will be different classes (from the perspective of the Java type system), and they with have different `Class` objects and their hashcodes will probably be different. – Stephen C Feb 17 '23 at 05:27
1

Stephen C's answer is a good answer. However, for the sake of completeness, I feel the need to add to that, that if a class is loaded by, let's say, two different class loaders, then for that class getClass().hashcode() will return two different values.

For verifying that, I wrote a program and loaded a class with System class loader, and then with my own custom class loader. Both return different hashcode():

First the code for Custom class loader I copied from 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;
  }
}

Then the Test class and main method which we will use for loading the Test class with different class loaders:

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());
  }
}

Output shows different hashcodes for the same class loaded with different Class Loaders:

1554547125
1072591677
Aqeel Ashiq
  • 1,988
  • 5
  • 24
  • 57