Java Set

In this post, I will introduce HashSet and LinkedHashSet in Java as well as their corresponding methods.

Set is inherited from Collection. To be more specific, it is a collection which does not contain duplicated elements. In addition, there is no index in the set and therefore it does not support random access and can not be iterated through normal for loops.


HashSet

  • There is no order in the HashSet.
  • It is implemented by hash table data structure and therefore its access efficiency is super high.

Common Methods

public static void main(String[] args) {
    Set<Integer> set = new HashSet<>();

    // Using add()
    set.add(1);
    set.add(3);
    set.add(2);
    set.add(1); // It is not stored in the set

    // Loop the set by iterator
    Iterator<Integer> it = set.iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }

    // Loop the set by enhanced for loop
    for (Integer i : set) {
        System.out.println(i);
    }
}

Hash Code

// Source Code
public native int hashCode();

’native’ indicates that ‘hashCode() invokes the method of the local operating system.

public static void main(String[] args) {
    // hashCode() returns the logic address of an object (randomly assigned)
    Person p1 = new Person();
    int h1 = p1.hashCode();
    System.out.println(h1); // 1175962212
    System.out.println(p1); // Person@4617c264

    Person p2 = new Person();
    int h2 = p2.hashCode();
    System.out.println(h2); // 918221580
    System.out.println(p2); // Person@36baf30c

    // String class overrides 'hashCode()' of Object
    String s1 = new String("abc");
    String s2 = new String("abc");
    System.out.println(s1.hashCode()); // 96354
    System.out.println(s2.hashCode()); // 96354
}

Storing User Defined Types in the Hash Set

public static void main(String[] args) {
    HashSet<Student> set = new HashSet<>();
    Student p1 = new Student("Jason", 21);
    Student p2 = new Student("Jason", 21);
    Student p3 = new Student("Jason", 21);
    set.add(p1);
    set.add(p2);
    set.add(p3);
    System.out.println(set);
    // [Student{name='Jason', age=21}, Student{name='Jason', age=21}, Student{name='Jason', age=21}]
    // p1, p2, p3 have different logic addresses and their equals() method all return 'false' (Before overriding, equals() only compares the address of objects). Therefore, p1, p2, p3 can be added to the HashSet.
}

What can we do if we assume the students with the same name and the same age are just one student? Well, we need to override ‘hashcode()’ and ’equals()’ methods.

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return age == student.age && Objects.equals(name, student.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

LinkedHashSet

  • LinkedHashSet maintains the order of elements. It has an extra linked list implemented to keep the order.

  • It inherits from HashSet.

Demo

public static void main(String[] args) {
    HashSet<String> set = new HashSet<>();
    set.add("My");
    set.add("My");
    set.add("name");
    set.add("is");
    set.add("Jason");
    System.out.println(set); // [name, is, Jason, My]

    LinkedHashSet<String> linkedSet = new LinkedHashSet<>();
    linkedSet.add("My");
    linkedSet.add("My");
    linkedSet.add("name");
    linkedSet.add("is");
    linkedSet.add("Jason");
    System.out.println(linkedSet); // [My, name, is, Jason]
}