Статьи

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

Я уже писал в блоге на удобном Apache Commons ToStringBuilder, и меня недавно спросили, что представляет собой загадочный текст, появляющийся в сгенерированном выводе String. Коллега, задававший вопрос, правильно предположил, что он рассматривал хеш-код, но он не соответствовал хеш-коду его экземпляра.

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

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

Person.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 созданной ToStringBuilder для этого экземпляра. Однако этого нельзя сказать о экземпляре Employee . Разница в том, что класс Person не переопределяет метод hashCode () и поэтому использует хеш-код идентификатора по умолчанию, в то время как класс Employee переопределяет свой собственный hashCode() (и, следовательно, отличается от хэш-кода идентификатора).

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 реализацию Object.toString () по умолчанию для той же цели. На самом деле, Javadoc даже рекламирует это:

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

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

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

Ссылка: ToString: шестнадцатеричное представление идентификационных хеш-кодов от нашего партнера JCG