22 Java - Set

Set

Properties

  • Collection of Objects, but it does not contain duplicate value (only one 'null' value you can insert)

  • Unlike List, Set is not an Ordered collection, means objects inside set does not follow the insertion order.

  • Unlike List, Set cannot be accessed via index.

Internal Implementation - Map

  • Which Data structure is used in Stack internally (as it does not allow duplicate value)? (Map)

  • As order is not gurantee, then what if we want to sort the Set Collection? (TreeSet)

Internally Set is implemented using map.

Methods

All methods declared in the Collections interface are generally only available in Set.

add(E element)

Returns true after inserting an element into the set only if the element is not already present. If the same value is already present, then it returns false.

addAll(Collection c)

performs UNION of 2 Set collection .

set1 = [12, 11, 33, 4]

set2 = [11, 9, 88, 10, 5, 12]

set1 + set2 = [12, 11, 33, 4, 9, 88, 10, 5]

remove(E element)

Remove element from the set.

removeAll(Collection c)

performs DIFFERENCE of 2 Set Collection. Delete the values from set which are present in another set.

set1 = [12, 11, 33, 4]

set2 = [11, 9, 88, 10, 5, 12]

set1 - set2 = [33, 4]

retainAll(Collection c)

performs Intersection of 2 Set collection. Returns element which are present in both

set1 = [12,11,33,4]

set2 = [11, 9, 88, 10, 5, 12]

set1 intersect set2 = [12, 11]

HashSet

  • Data structure used: HashMap

    HashMap<E, Object> map = new HashMap<>();

  • During Add method invocation, it stored the element in the key part and in value it stores the dummy object:

    map.put(element, new Object())

  • What if 2 values get the same hash value? How it's handled? What is load factor? (Refere HashMap)

  • No guarantee that the order will remain constant.

  • HashSet is not threadSafe. newkeySet method present in ConcurrentHashMap class is used to create threadSafe Set.

public class Main {
    public static void main(String[] args) {
        Set<Integer> set1 = new HashSet<>();
        set1.add(12);
        set1.add(11);
        set1.add(33);
        set1.add(4);

        Set<Integer> set2 = new HashSet<>();
        set2.add(11);
        set2.add(9);
        set2.add(88);
        set2.add(10);
        set2.add(5);
        set2.add(12);

        //UNION of 2 sets
        set1.addAll(set2);
        System.out.println("after union");
        set1.forEach(val -> System.out.println(val));

        //Intersection of 2 sets
        set1 = new HashSet<>();
        set1.add(12);
        set1.add(11);
        set1.add(33);
        set1.add(4);

        set2 = new HashSet<>();
        set2.add(11);
        set2.add(9);
        set2.add(88);
        set2.add(10);
        set2.add(5);
        set2.add(12);

        set1.retainAll(set2);
        System.out.println("after intersection:");
        set1.forEach(val -> System.out.println(val));

        //Difference of 2 sets
        set1 = new HashSet<>();
        set1.add(12);
        set1.add(11);
        set1.add(33);
        set1.add(4);

        set2 = new HashSet<>();
        set2.add(11);
        set2.add(9);
        set2.add(88);
        set2.add(10);
        set2.add(5);
        set2.add(12);

        set1.removeAll(set2);
        System.out.println("after Difference");
        set1.forEach(val-> System.out.println(val));

    }
}
after union
33
4
5
88
9
10
11
12
after intersection:
11
12
after Difference
33
4

Thread Safe Version

  • Thread Safe - No

  • Maintains Insertion Order - No

  • Null Elements allowed - Yes (only one)

  • Duplicate elements allowed - No

HashSet is not Thread Safe

public class Main {
    public static void main(String[] args) {

        Set<Integer> set1 = new HashSet<>();
        set1.add(1);
        set1.add(2);

        Iterator<Integer> iterator = set1.iterator();

        while (iterator.hasNext()){
            int val = iterator.next();
            if (val==1){
                //iterator.remove(); // we can remove
                set1.add(9); //we should be able to add in the set as its thread safe
            }
        }
        set1.forEach(val-> System.out.println(val));
    }
}
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1597)
    at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1620)
    at learn.collection.Main.main(Main.java:19)

Example: Thread Safe

public class Main {
    public static void main(String[] args) {

        ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
        Set<Integer> threadSafeSet = concurrentHashMap.newKeySet();
        threadSafeSet.add(1);
        threadSafeSet.add(2);

        Iterator<Integer> iterator = threadSafeSet.iterator();

        while (iterator.hasNext()){
            int val = iterator.next();
            if (val==1){
                //iterator.remove(); // we can remove
                threadSafeSet.add(9); //we should be able to add in the set as its thread safe
            }
        }
        threadSafeSet.forEach(val-> System.out.println(val));
    }
}
/*
1
2
9
*/

Time Complexity

add: O(1)

remove: Amortized O(1)

contains: Amortized O(1)

LinkedHashSet

  • Internally it uses: LinkedHashMap

  • Maintains the insertion Order of the element

  • Its not thread safe

  • Set<Integer> set = Collections.synchronizedMap(new LinkedHashSet<>());

public class Main {
    public static void main(String[] args) {

        Set<Integer> set1 = new LinkedHashSet<>();
        set1.add(1);
        set1.add(77);
        set1.add(82);
        set1.add(63);
        set1.add(5);

        Iterator<Integer> iterator = set1.iterator();

        while (iterator.hasNext()){
            int val = iterator.next();
            System.out.println(val);
        }

    }
}
/*
1
77
82
63
5
*/

TreeSet

  • Internally it uses: TreeMap

  • It cannot store null value

public class Main {
    public static void main(String[] args) {

        Set<Integer> set1 = new TreeSet<>();
        set1.add(1);
        set1.add(77);
        set1.add(82);
        set1.add(63);
        set1.add(5);

        Iterator<Integer> iterator = set1.iterator();

        while (iterator.hasNext()){
            int val = iterator.next();
            System.out.println(val);
        }

    }
}
/*
1
5
63
77
82
*/