Question about the function of generics in Interner

33 views
Skip to first unread message

cloudgu(顾云开)

unread,
Oct 14, 2021, 11:20:07 PM10/14/21
to guava-...@googlegroups.com

What's the difference between the following two cases

before all, I defined a simple class

static class Person {
  String a;

  public Person(String a) {
      this.a = a;
  }

  @Override
  public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Person person = (Person) o;
      return Objects.equals(a, person.a);
  }
}

In first case, i use interner to assign the personso person and person1 will point to the same object

Interner<Person> interner = Interners.newWeakInterner();
person = new Person("cloudgu");
Person person1 = interner.intern(person);

In second case, i assign person to person1 directlyi can get the same result like first case

person = new Person("cloudgu");
Person person1 = person;

I know MapMakerInternalMap will store the Person Object as key and a DummyValue as valuebut it is different from the String Type

when I use String typeif the value of string is the sameMapMakerInternalMap will return the former objectso i can reduce memory usage

Interner<String> interner = Interners.newWeakInterner();
List<String> list = new ArrayList<>();
String s = null;
for (int i = 0; i < 40000000; i++) {
  s = new String("cloudgu");
  String s1 = interner.intern(s);
  list.add(s1);
  if (i % 100000 == 0) {
      System.out.println(i);
  }
}

but if I use Person typewhen i use new to create a Person objecteven if the value of person1 equals person2the map will see those as two different objects and store them seperately.

in this casei can't see the benefits of using interner

Interner<Person> interner = Interners.newWeakInterner();
List<Person> list = new ArrayList<>();
Person person = null;
for (int i = 0; i < 40000000; i++) {
  person = new Person("cloudgu");
  Person person1 = interner.intern(person);
  list.add(person1);
  if (i % 100000 == 0) {
      System.out.println(i);
  }
}

To concludeI was confused about in what kind of situation will developers use the other types rather than String type

in other wordsHow can I achieve the same good results in Person case like String case

 

Louis Wasserman

unread,
Oct 14, 2021, 11:41:17 PM10/14/21
to cloudgu(顾云开), guava-...@googlegroups.com
Looks like you're missing a hashCode method in Person, which might be causing the failure to group them together.

Louis Wasserman
wasserm...@gmail.com


--
guava-...@googlegroups.com
Project site: https://github.com/google/guava
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: https://github.com/google/guava/issues/new
To get help: http://stackoverflow.com/questions/ask?tags=guava
---
You received this message because you are subscribed to the Google Groups "guava-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to guava-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/guava-discuss/45C6A7E7-0E04-487C-A40D-E300FF2DC02C%40tencent.com.

Xeno Amess

unread,
Oct 15, 2021, 10:40:22 PM10/15/21
to Louis Wasserman, cloudgu(顾云开), guava-...@googlegroups.com
Implementing a class who can have two equals instances with different hashcodes?
That is insane actually...

Louis Wasserman <wasserm...@gmail.com> 于2021年10月15日周五 上午11:41写道:

Xeno Amess

unread,
Oct 15, 2021, 10:41:30 PM10/15/21
to Louis Wasserman, cloudgu(顾云开), guava-...@googlegroups.com
please see javadoc Object#hashcode

     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.

Xeno Amess <xeno...@gmail.com> 于2021年10月16日周六 上午10:40写道:

Tim Peierls

unread,
Oct 15, 2021, 11:37:39 PM10/15/21
to Xeno Amess, Louis Wasserman, cloudgu(顾云开), guava-...@googlegroups.com

The point Louis was making was that you need to define Person.hashCode to meet the general contract that you are referring to.

It could be as simple as

    @Override public int hashCode() {
        return a == null ? 0 : a.hashCode();
    }

If you don’t provide something like this, then your class violates the contract, because:

Person a = new Person("a");
Person b = new Person("b");
assert a.equals(b); // true
assert a.hashCode() == b.hashCode(); // fails

—tim


Joachim Durchholz

unread,
Oct 16, 2021, 1:07:15 AM10/16/21
to guava-...@googlegroups.com
Am 15.10.21 um 05:20 schrieb cloudgu(顾云开):
> What's the difference between the following two cases
>
> before all, I defined a simple class
>
> static class Person {
>   String a;
> ​
>   public Person(String a) {
>       this.a = a;
>   }
> ​
>   @Override
>   public boolean equals(Object o) {
>       if (this == o) return true;
>       if (o == null || getClass() != o.getClass()) return false;
>       Person person = (Person) o;
>       return Objects.equals(a, person.a);
>   }
> }

Interning typically means the object go into a cache where they are used
as keys.

If the cache is a TreeMap, they need to implement Comparable (or be
compatible with the Comparator used); if the cache is a HashMap, they
need to implement a hashCode() that covers exactly those members that go
into equals().
This should be documented on all classes that use keys (I don't know if
Interner does this, if it does not, it should).

An object that implements hashCode() and is used as a key in a HashMap
should also make sure that all members that go into hashCode() are
immutable. I can't recall if HashMap documents this; however, any
HashMap variants and all HashMap-using classes should document this as well.
For your code, this means that Person allows modification to a. a should
be declared final to make sure that all accidental assignments to a will
get flagged as errors.

The typical policy, however, is to be more conservative: Make all
classes useful as keys to HashMap by implementing a hashCode() function
(and where you can't do this, place a big fat warning in the JavaDoc to
make sure nobody accidentally uses the class as key).
This is such a common pattern that following any other policy is often
considered insane (as Xeno does), and it's in fact what Object.equals()
and Object.hashCode() ask you to do in their JavaDoc.
As has been noted by others, your code as posted fails to define
hashCode() and would run into deep trouble when used in Interner, or
anywhere else it's used as a HashMap key.

HTH
Jo

顾庭燎

unread,
Oct 16, 2021, 5:45:11 AM10/16/21
to guava-discuss
Thanks for all your answers, I have realized the problem in my code, it went well after I add the hashcode method。 btw, I am the questioner
Reply all
Reply to author
Forward
0 new messages