Статьи

Шестнадцатеричное представление идентификационных хеш-кодов

Я уже писал в блоге на удобном Apache Commons ToStringBuilder, и меня недавно спросили, что представляет собой загадочный текст, который появляется в сгенерированном выводе String. Коллега, задававший вопрос, правильно предположил, что он рассматривал хеш-код, но он не соответствовал хеш-коду его экземпляра. Я объяснил, что ToStringBuilder добавляет к выводу хеш-код идентичности в шестнадцатеричном формате. В этой статье я более подробно рассмотрю использование ToStringBuilder хеш-кода идентификатора, представленного в шестнадцатеричном формате. Даже те, кто не использует ToStringBuilder, могут найти эту информацию полезной в качестве стандартного Java Object.toString ()также использует шестнадцатеричное представление того, что фактически является его хэш-кодом идентичности .

Я начну с очень простого примера Java с использованием ToStringBuilder. В этом примере используются три Java-класса (Person.java, Employee.java и Main.java), которые показаны далее.

Person.java

package dustin.examples;

import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * A simple representation of a Person intended only to demonstrate Apache
 * Commons ToStringBuilder.
 * 
 * @author Dustin
 */
public class Person
{
   /** Person's last name (surname). */
   protected final String lastName;

   /** Person's first name. */
   protected final String firstName;

   /**
    * Parameterized constructor for obtaining an instance of Person.
    * 
    * @param newLastName Last name of new Person instance.
    * @param newFirstName First name of new Person instance.
    */
   public Person(final String newLastName, final String newFirstName)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
   }

   /**
    * Provide String representation of this Person instance.
    * @return My String representation.
    */
   @Override
   public String toString()
   {
      final ToStringBuilder builder = new ToStringBuilder(this);
      builder.append("First Name", this.firstName);
      builder.append("Last Name", this.lastName);
      return builder.toString();
   }
}

Employee.java 

package dustin.examples;

import java.util.Objects;
import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * Simple class intended to demonstrate ToStringBuilder.
 * 
 * @author Dustin
 */
public class Employee extends Person
{
   /** Employee ID. */
   private final String employeeId;

   /**
    * Parameterized constructor for obtaining an instance of Employee.
    * 
    * @param newLastName Last name of the employee.
    * @param newFirstName First name of the employee. 
    * @param newId Employee's employee ID.
    */
   public Employee(
      final String newLastName, final String newFirstName, final String newId)
   {
      super(newLastName, newFirstName);
      this.employeeId = newId;
   }

   /**
    * Provide String representation of me.
    *
    * @return My String representation.
    */
   @Override
   public String toString()
   {
      final ToStringBuilder builder = new ToStringBuilder(this);
      builder.appendSuper(super.toString());
      builder.append("Employee ID", this.employeeId);
      return builder.toString();
   }

   /**
    * Simple object equality comparison method.
    * 
    * @param obj Object to be compared to me for equality.
    * @return {@code true} if the provided object and I are considered equal.
    */
   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final Employee other = (Employee) obj;
      if (!Objects.equals(this.employeeId, other.employeeId))
      {
         return false;
      }
      return true;
   }

   /**
    * Hash code for this instance.
    * 
    * @return My hash code.
    */
   @Override
   public int hashCode()
   {
      int hash = 3;
      hash = 19 * hash + Objects.hashCode(this.employeeId);
      return hash;
   }
}

Main.java (Версия 1) 

package dustin.examples;

import static java.lang.System.out;

/**
 * Simple class enabling demonstration of ToStringBuilder.
 * 
 * @author Dustin
 */
public class Main
{
   /**
    * Main function for running Java examples with ToStringBuilder.
    * 
    * @param args the command line arguments
    */
   public static void main(String[] args)
   {
      final Person person = new Person("Washington", "Willow");
      out.println(person);
      final Employee employee = new Employee("Lazentroph", "Frank", "56");
      out.println(employee);
   }
}

Приведенный выше пример прост и его вывод показан ниже:

Вывод, изображенный выше, показывает строку String, напечатанную для вывода обоих экземпляров, сгенерированного ToStringBuilder. Представление String экземпляра класса Person включает строку «1f5d386», а представление String экземпляра класса Employee содержит строку «1c9b9ca». Эти строки являются шестнадцатеричным представлением хеш-кода каждого объекта.

Строки «1f5d386» и «1c9b9ca» не похожи на целочисленные хэш-коды, которые многие из нас привыкли видеть из-за их шестнадцатеричного представления. В Integer.toHexString (INT) метода [доступная , так как JDK 1.0.2] это удобный метод для печати целого числа в шестнадцатеричном формате и может быть использован для преобразования «нормальной» хэш — коды , чтобы увидеть , если они совпадают, генерируемое ToStringBuilder. Я добавил вызовы к этому методу в хэш-кодах экземпляров в новой версии класса Main.

Main.java (версия 2)

package dustin.examples;

import static java.lang.System.out;

/**
 * Simple class enabling demonstration of ToStringBuilder.
 * 
 * @author Dustin
 */
public class Main
{
   /**
    * Main function for running Java examples with ToStringBuilder.
    * 
    * @param args the command line arguments
    */
   public static void main(String[] args)
   {
      final Person person = new Person("Washington", "Willow");
      out.println(person);
      out.println("\tHash Code (ten): " + person.hashCode());
      out.println("\tHash Code (hex): " + Integer.toHexString(person.hashCode()));

      final Employee employee = new Employee("Lazentroph", "Frank", "56");
      out.println(employee);
      out.println("\tHash Code (ten): " + employee.hashCode());
      out.println("\tHash Code (hex): " + Integer.toHexString(employee.hashCode()));
   }
}

Выполнение вышеизложенного приводит к следующему выводу:

Как показывают выходные данные, шестнадцатеричное представление хеш-кода для экземпляра Person действительно совпадает с показанным в строке, сгенерированной ToStringBuilder для этого экземпляра. Однако этого нельзя сказать о экземпляре Employee. Разница в том, что класс Person не переопределяет метод hashCode () и поэтому использует хеш-код идентификатора по умолчанию, в то время как класс Employee переопределяет свой собственный hashCode () (и, следовательно, отличается от хэш-кода идентификатора).

Третья версия Main выводит хэш-код идентификатора с использованием System.identityHashCode (Object) [более подробно обсуждается в моем блоге Java System.identityHashCode ].

Main.java (Версия 3)

package dustin.examples;

import static java.lang.System.out;

/**
 * Simple class enabling demonstration of ToStringBuilder.
 * 
 * @author Dustin
 */
public class Main
{
   /**
    * Main function for running Java examples with ToStringBuilder.
    * 
    * @param args the command line arguments
    */
   public static void main(String[] args)
   {
      final Person person = new Person("Washington", "Willow");
      out.println(person);
      out.println("\tHash Code (ten): " + person.hashCode());
      out.println("\tHash Code (hex): " + Integer.toHexString(person.hashCode()));
      out.println("\t\tIdentity Hash (ten): " + System.identityHashCode(person));
      out.println("\t\tIdentity Hash (hex): " + Integer.toHexString(System.identityHashCode(person)));

      final Employee employee = new Employee("Lazentroph", "Frank", "56");
      out.println(employee);
      out.println("\tHash Code (ten): " + employee.hashCode());
      out.println("\tHash Code (hex): " + Integer.toHexString(employee.hashCode()));
      out.println("\t\tIdentity Hash (ten): " + System.identityHashCode(employee));
      out.println("\t\tIdentity Hash (hex): " + Integer.toHexString(System.identityHashCode(employee)));
   }

Теперь мы можем сравнить хэш-код идентификатора со строкой, сгенерированной ToStringBuilder.

Последний пример окончательно демонстрирует, что ToStringBuilder включает шестнадцатеричное представление хеш-кода идентификатора системы в свой сгенерированный вывод. Если кто-то хочет использовать шестнадцатеричное представление переопределенного хеш-кода, а не хеш-кода идентификатора, можно использовать экземпляр ToStringStyle (обычно это экземпляр StandardToStringStyle ), а метод setUseIdentityHashCode (boolean) можно вызвать с параметром false. Этот экземпляр ToStringStyle затем может быть передан методу ToStringBuilder.setDefaultStyle (ToStringStyle) .

Примечательно, что методы equals (Object) и hashCode () в классе Employee, показанном выше, были автоматически созданы NetBeans 7.1 . Я был рад видеть, что с моей исходной версией Java для этого проекта, определенной как JDK 1.7 , это автоматическое создание этих двух методов использовало преимущества класса Objects .

В этом посте я использовал вывод, сгенерированный ToStringBuilder, чтобы облегчить обсуждение шестнадцатеричных представлений идентификационных хеш-кодов, но я мог бы просто использовать собственную встроенную реализацию JDK «default» Object.toString () для той же цели. На самом деле, Javadoc даже рекламирует это:


Метод toString для класса Object возвращает строку, состоящую из имени класса, экземпляром которого является объект, символа знака «@» и шестнадцатеричного представления без знака хеш-кода объекта.
Другими словами, этот метод возвращает строку, равную значению:

getClass (). getName () + ‘@’ + Integer.toHexString (hashCode ())

Единственная причина, по которой я не использовал этот пример для начала, заключается в том, что я почти всегда переопределяю метод toString () в своих классах и не получаю эту реализацию «по умолчанию». Однако, когда я использую ToStringBuilder для реализации своих переопределенных методов toString (), я вижу эти шестнадцатеричные представления. Я могу уменьшить использование ToStringBuilder по мере увеличения использования Objects.toString () .

Многие из нас не думают о шестнадцатеричных представлениях или хэш-кодах идентичности в нашей повседневной работе на Java. В этом сообщении в блоге я использовал вывод ToStringBuilder в качестве предлога для того, чтобы взглянуть немного ближе на эти две концепции. Попутно я также кратко рассмотрел метод Integer.toHexString (Object), который полезен для печати чисел в их шестнадцатеричном представлении. Знание о поддержке Java шестнадцатеричного представления важно, потому что оно отображается в выводе toString () , при маркировке цветов , адресов памяти и в других местах.