Showing posts with label Collection Framework. Show all posts
Showing posts with label Collection Framework. Show all posts

Tuesday, June 20, 2023

Demystifying Java Collections: Set, HashSet, and LinkedHashSet

java,Collection Framework,set,hashset,programming,software development,technology
The Collection Framework offers a set of interfaces and classes for storing and manipulating groups of objects. The Set interface, along with its common implementations such as HashSet and LinkedHashSet, is one of the most commonly used types in this framework.

Set(I)

The Set is the Collection's child interface. Set(I) contains no new methods. As a result, we can call Collection interface methods.
java,Collection Framework,ArrayList,List,set,hashset,treemap

Here are some scenarios in which a Set collection could be useful:
  • If you have a collection of elements that may contain duplicates, you can use a Set to remove the duplicates. Simply add all of the elements to a Set and then retrieve the unique elements from the Set.
  • When comparing the equality of two collections of elements, we can use the Set. You can create two Set objects from the two collections and then compare their equality.
  • When we want to perform operations on a collection of objects without creating duplicates, we can use a Set collection. Set operations such as union, intersection, and difference are possible.

HashSet

HashSet is a class that extends AbstractSet and implements the Set interface. This means that HashSet inherits all of AbstractSet's methods and implements the Set interface's required methods. Internally, HashSet stores elements in a HashTable. Each element is stored in the hash table as a key, with the value.

Java first determines the element's hash code using its hashCode() method before inserting the element into a HashSet. The new element is compared to the existing element using the equals() method. The new element is not included in the set if they are equal. The new element is included in the set if they are not equal. HashSet accepts NULL elements. In fact, since a hash set forbids duplication, there can only be one null element in a hash set.

👉 Here are some HashSet constructors:
// Creates a new HashSet object that is empty and has an initial capacity of 16 
// and a fill ratio or load factor of 0.75 by default.
HashSet hashSet = new HashSet();

// Creates an empty HashSet object with the specified initial capacity 
// and a fill ratio or load factor of 0.75 by default.
HashSet hashSet = new HashSet(int capacity);

// Creates a new HashSet object with the specified initial capacity and load factor or fill capacity.
HashSet hashSet = new HashSet(int capacity, float loadFactor);

// Creates an empty HashSet object from a Collection object. This converts the Collection object to a HashSet.
HashSet hashSet = new HashSet(Collection collection);


👉 Here's some code that shows how to use Set and HashSet:
import java.util.*;
public class HashSetDemo {
    public static void main(String[] args) {
        Set _set = new HashSet();
        _set.add("John Snow");
        _set.add(41);
        _set.add("john@gmail.com");
        _set.add("Japan");
        System.out.println("Size of the Set object : " + _set.size());

        Object[] objects = _set.toArray();
        for (int i = 0; i < objects.length; i++) {
            System.out.println(objects[i]);
        }

        if (_set.contains(41)) {
            System.out.println("Set contains specified element.");
        }
    }
}
In the preceding code snippet, we created a HashSet object and assigned it to a Set. We can do this because HashSet is the implementation class for the Set interface. The Set object is then filled with elements of various types. To convert the Set object into an array object, we use the toArray() method in line 13. HashSet inherited the toArray() method from the Collection interface. The index is then used to print the array values.

When we run this code, we can see that the elements are not printed in the order that we entered them. Despite the fact that we print the elements using the index. This means that elements in HashSet are not kept in order.

👉 Here's another HashSet code snippet with a Generic type:
import java.util.*;
public class HashSetGenericDemo {
    public static void main(String[] args) {
        Set<String> _set = new HashSet<>();
        _set.add("John Snow");
        // _set.add(41);
        _set.add("41");
        _set.add("john@gmail.com");
        _set.add("Product Manager");
        _set.add("813 Howard Street");
        System.out.println(_set);

        System.out.println(_set.add("CA 12345"));
        System.out.println(_set.add("CA 12345"));

        Set<String> anothetSet = new HashSet<>();
        anothetSet.add("Alex White");
        anothetSet.add("42");
        anothetSet.add("Senior Manager");
        anothetSet.add("alex@gmail.com");
        anothetSet.add("159 Howard Street");

        _set.addAll(anothetSet);
        System.out.println(_set);
    }
}
We create a generic HashSet of type String and populate it with String elements. To store other primitive data types, we must use the generic type.

In lines 14 and 15, we attempted to add the same element to the HashSet. If we run the code, we can see that the output for line 14 is true, but the output for line 15 is false. Because the element isn't in the HashSet, the add() method returns true for line 14. However, because the element is already present in the HashSet, line 15 returns false, indicating that we cannot add duplicate elements to the HashSet.

In line 17, we added another HashSet. In line 24, use the addAll() method to insert this new HashSet into our previous HashSet.


👉 Here are some specific scenarios where a Set may be useful. If you have a collection of elements that may contain duplicates, you can use a Set to remove the duplicates. Simply add all of the elements to a Set and then retrieve the unique elements from the Set.
import java.util.*;
public class HashSetDuplicateDemo {
    public static void main(String[] args) {
        // LIST WITH DUPLICATE NAMES
        List<String> nameList = Arrays.asList("Jane", "Amanda", "Joshua", "Megan", "Jane", 
        		"Emily", "Scott", "Joshua", "Rebecca", "Daniel", "Victoria", "Megan", "Rebecca");
        System.out.println("Original name list " + nameList + " of size " + nameList.size());
        
        // CREATE A HASHSET OBJECT WITH THE LIST
        Set<String> _set = new HashSet<String>(nameList);
        System.out.println("Unique name list " + _set + " of size " + _set.size());
    }
}


👉 We can use Set to see if two collections have any common elements, which indicates an intersection. From the two collections, we can create HashSet objects and then use the retainAll() method to find the intersection of the two collections. If the resulting set is empty, the two collections share no elements; otherwise, the two collections share elements. Here's an example:
import java.util.*;
public class HashSetIntersectionDemo {
    public static void main(String[] args) {
        List<String> nameListOne = Arrays.asList("Jane", "Amanda", "Joshua", "Megan");
        List<String> nameListTwo = Arrays.asList("Emily", "Scott", "Rebecca", "Daniel", "Victoria");
        List<String> nameListThree = Arrays.asList("Jane", "Emily", "Joshua", "Rebecca","Victoria", "Amanda");

        // Set<String> _setOne = new HashSet<String>(nameListOne);
        // Set<String> _setTwo = new HashSet<String>(nameListTwo);
        // Set<String> _setThree = new HashSet<String>(nameListThree);

        Set<String> _resultOne = new HashSet<String>(nameListOne);
        _resultOne.retainAll(nameListTwo);
        System.out.println(_resultOne.isEmpty() == true ? "No common element found in the HashSets."
                : "HashSets has common elements.");
        System.out.println("Result has " + _resultOne + " elements of size " + _resultOne.size());
        System.out.println();

        Set<String> _resultTwo = new HashSet<String>(nameListTwo);
        _resultTwo.retainAll(nameListThree);
        System.out.println(_resultTwo.isEmpty() == true ? "No common element found in the HashSets."
                : "HashSets has common elements.");
        System.out.println("Result has " + _resultTwo + " elements of size " + _resultTwo.size());
    }
}


👉 Using the Set, we can also perform the union operation. If we have one or more collection objects with duplicate elements, we can use these collections to create a single Set object and the duplicate elements will be removed automatically. This means that we're making a union out of the collection objects. Here's an illustration.
import java.util.*;
public class HashSetUnionDemo {
    public static void main(String[] args) {
        List<String> nameListOne = Arrays.asList("Jane", "Amanda", "Joshua", "Megan");
        List<String> nameListTwo = Arrays.asList("Emily", "Scott", "Rebecca", "Daniel", "Victoria");
        List<String> nameListThree = Arrays.asList("Jane", "Emily", "Joshua", "Rebecca","Victoria", "Amanda");

        Set<String> _resultOne = new HashSet<String>(nameListOne);
        _resultOne.addAll(nameListTwo);
        _resultOne.addAll(nameListThree);
        System.out.println("Union has " + _resultOne + " elements of size " + _resultOne.size());
    }
}


👉 Set can be used to store a collection of unique objects. Assume you have the following Booking objects:
import java.util.*;
public class HashSetObjectDemo {
    public static void main(String[] args) {
        List<Booking> bookings = new ArrayList<>();
        bookings.add(new Booking(10, "Joshua")); bookings.add(new Booking(15, "Victoria"));
        bookings.add(new Booking(19, "Rebecca")); bookings.add(new Booking(8, "Amanda"));
        bookings.add(new Booking(20, "Victoria")); bookings.add(new Booking(19, "Rebecca"));
        System.out.println("No of Booking List " + bookings.size());

        Set<Booking> setBooking = new HashSet<Booking>(bookings);
        System.out.println("No of Booking Set " + setBooking.size());
        for (Booking booking : setBooking) {
            System.out.println(booking);
        }
    }
}
class Booking {
    int seatNo;
    String name;

    public Booking() { }

    public Booking(int seatNo, String name) {
        this.seatNo = seatNo;
        this.name = name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + seatNo;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Booking other = (Booking) obj;
        if (seatNo != other.seatNo)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Booking [seatNo=" + seatNo + ", name=" + name + "]";
    }
}
In this example, the Booking class overrides the equals() and hashCode() methods so that two Booking objects are considered equal if they have the same name and seatNo. The Set ensures that there are no duplicate Booking objects in the collection.


LinkedHashSet

LinkedHashSet is a class that extends HashSet and implements the Set interface. In addition to the hash table, the LinkedHashSet keeps a linked list. This linked list allows the elements to be iterated in the order they were added to the set. Duplicates are not allowed in LinkedHashSet.

👉 Here's an example of how LinkedHashSet can be used:
import java.util.*;
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("John Snow");
        linkedHashSet.add("Peter Johnson");
        linkedHashSet.add("Madison Hernandez");
        linkedHashSet.add("John Snow");
        linkedHashSet.add("Audrey Davis");
        linkedHashSet.add("Victoria Davis");
        linkedHashSet.add("Isaac Thompson");
        linkedHashSet.add("Chloe Martinez");
        System.out.println("LinkedHashSet has " + linkedHashSet + " elements of size " + linkedHashSet.size());

        linkedHashSet.remove("Audrey Davis");
        System.out.println("\nAfter the removal, LinkedHashSet has " + linkedHashSet + " elements of size " + linkedHashSet.size());

        List<String> nameList = Arrays.asList("Daniel Hall", "Madison Hernandez", "Rebecca Turner", "Isabelle Taylor");
        linkedHashSet.addAll(nameList);
        System.out.println("\nAfter the addition of new collection, LinkedHashSet has " + linkedHashSet + " elements of size " + linkedHashSet.size());
        System.out.println();
    }
}
In the example above, the elements are printed in the same order in which they were added in line 13, and the duplicate element "John Snow" is removed from the LinkedHashSet object. We removed an element from the LinkedHashSet in line 15. In line 19, we added a new list of elements to the set, which includes some common elements from the LinkedHashSet object. When we print the elements, we can see that the list object's duplicate elements are ignored. 


Happy coding!!! 😊
in


Sunday, May 21, 2023

Deep Dive into Java Collections: List And ArrayList

java,Collection Framework,ArrayList,List,programming,software development,technology
In this tutorial, we'll look at how to use Java's Collection Framework to organize and work with data. Different types of collection interfaces and the classes that implement them are available in the Collection Framework. These interfaces can be separated into: the Collection interface and the Map interface. We can build an ordered or index-based collection object using the Collection interface. On the other hand, we can make key-value-paired collection objects using the Map interfaces.

Collection(I)

The Collection interface is referred to as the Collection Framework's root interface. This interface contains a few general-purpose abstract methods, and its child classes or interfaces automatically have access to these methods. Important methods are as follows:
boolean add(Object object)
boolean remove(Object object)
boolean retainAll(Collection collection) : all the objects will be removed except those present in collection
boolean contains(Object object)
int size()
Object[] toArray()
Iterator iterator();
The Collection interface has no implementation class. The List and Set interfaces extend the Collection interface.

java,programming,ArrayList,List,Software Development

List(I)

The Java Collection Framework's List interface stores an ordered collection of elements. This interface contains methods for adding, removing, and accessing collection elements. Among the most essential methods in the List interface are:
// Adds an element at the specified index
void add(int index, Object object)

// Inserts the provied collection object at the specified index
boolean addAll(int index, Collection collection)

// Removes an element from the specified index
Object remove(int index)

// Retrieves an element from the specified index
Object get(int index)

// this will replace the element present at specified index with the object provided and returns the old object
Object set(int index, Object object)

// returns the index of the object provided
int indexOf(object object)

// returns the index of the object provided
int lastIndexOf(object object)
Insertion order is preserved in the List. This means that the elements will be kept in the order in which they were inserted. We can keep duplicate elements in it.

Because List is an abstract interface, you cannot directly create an instance. You must instead make an instance of a class that implements the List interface. ArrayList, LinkedList, and Vector are some classes that implement the List interface.

ArrayList

The ArrayList is a class in java.util package that implements the List interface. It offers dynamic arrays that can expand as needed. ArrayLists can be created with a fixed capacity and will automatically resize when that capacity is reached. We can insert heterogeneous objects into them and inserting NULL values is also possible.

👉 Here are some ArrayList constructors:
// We can use this constructor to create an empty ArrayList with the default capacity (10). 
// When the ArrayList is full, a new ArrayList with a new capacity is created.
public ArrayList()

// We can use this constructor to create an ArrayList with the specified capacity.
public ArrayList(int capacity)

// We can use this constructor to create an ArrayList with the specified Collection object.
// Using this constructor, we can convert any Collection object to ArrayList.
public ArrayList(Collection collection)

👉 Here's some code that shows how to use List and ArrayList:
import java.util.*;
public class ArrayListDemo {
    public static void main(String[] args) {
        System.out.println("ArrayList using Homogeneous elements : ");
        
        List nameList = new ArrayList();
        nameList.add("Sally Becton");
        nameList.add("Larry Bowen");
        nameList.add("Elizabeth A. Fabian");
        nameList.add("Maria Juarez");
        
        Object[] nameArray = nameList.toArray();
        for (int i=0; i<nameArray.length; i++) {
            System.out.println(i + " : " + nameArray[i]);
        }
    }
}
We created an ArrayList object and assigned it to a List in the preceding code snippet. We can do this because ArrayList is the List interface's implementation class. Then we populate the ArrayList with some homogeneous elements (String type). In line 13, we use the toArray() method to convert the ArrayList into an array object. This toArray() method was inherited by ArrayList from the Collection interface. The array values are then printed using the index.

👉 Consider the following code snippet:
import java.util.*;
public class ArrayListHeteroDemo {
    public static void main(String[] args) {
        System.out.println("ArrayList using Heretogeneous elements : ");
        
        ArrayList differentList = new ArrayList();
        differentList.add(4362);
        differentList.add("Aberdeen");
        differentList.add(true);
        differentList.add(13.97);
        differentList.add("wsalmon@example.com");
        
        for (int i=0; i<differentList.size(); i++) {
            System.out.println(i + " : " + differentList.get(i));
        }
        
        System.out.println("\nIndex of element \'13.97\' is : " + differentList.indexOf(13.97));
    }
}
In this case, we've added some heterogeneous objects to the ArrayList. The ArrayList elements are then printed using the get(int index) method and retrieve the index of an existing element using the indexOf(Object obj) method.

👉 Here's an example of the generic type ArrayList:
import java.util.*;
public class ArrayListGenericDemo {
    public static void main(String[] args) {
        List<String> genericList = new ArrayList<>();
        
        try {
            genericList.add("Sally Becton");
            genericList.add("Larry Bowen");
            genericList.add("Elizabeth A. Fabian");
            
            //genericList.add(100.77);
            genericList.add(String.valueOf(123.77));
            //System.out.println(genericList);
            
            System.out.println("ArrayList elements : ");
            for(String str : genericList) {
                System.out.println(str);
            }
            
            //double _value = genericList.get(3);
            System.out.println("\nDouble value : " + Double.parseDouble(genericList.get(3)));
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            //System.out.println(genericList);
        }
    }
}
We create a String-type generic ArrayList and populate it with String elements. In line 12, we insert a double element; in line 13, we insert another double element, but this time we convert it to a String. If we run this code, we will get a compile-time error that says "incompatible types: double cannot be converted to String" for line 12. There will be no complaints about line 13. As a result, when we make an ArrayList generic, we can't store other primitive or custom data types in it. We must use the generic type to store other primitive data types.

ArrayList also supports the RandomAccess interface. This is why we can quickly retrieve values from the large ArrayList from any position.

However, inserting or deleting any element from a large ArrayList will take time. Because inserting an element in the middle of a large ArrayList necessitates shifting the other elements to insert the element at the specified index. As a result, this will also take time.

Synchronized ArrayList

ArrayList objects are non-synchronized by default. However, using the Collections class's synchronizedList() method, we can create a synchronized version of the ArrayList object, meaning multiple threads can access the list concurrently without causing any inconsistencies or errors.

👉 Here's an example:
import java.util.*;
public class ArrayListSynchronizedDemo {
    public static void main(String[] args) {
        String _status = "AVAILABLE";
        
        List<String> seatlList = new ArrayList<>();
        seatlList.add("Seat no 1 is " + _status);
        seatlList.add("Seat no 2 is " + _status);
        seatlList.add("Seat no 3 is " + _status);
        seatlList.add("Seat no 4 is " + _status);
        seatlList.add("Seat no 5 is " + _status);
        seatlList.add("Seat no 6 is " + _status);
        seatlList.add("Seat no 7 is " + _status);
        seatlList.add("Seat no 8 is " + _status);
        seatlList.add("Seat no 9 is " + _status);
        seatlList.add("Seat no 10 is " + _status);
        
        System.out.println("Available seats : " + seatlList + "\n");
        
        // Create a synchronized ArrayList from existing list
        List<String> synchronizedList = Collections.synchronizedList(seatlList);
        
        // Creating multiple threads to change seat status
        Thread t2_booked = new Thread(new SeatStatusModifier(synchronizedList, 2, "BOOKED"));
        t2_booked.setName("t2_booked");
        
        Thread t5_booked = new Thread(new SeatStatusModifier(synchronizedList, 5, "BOOKED"));
        t5_booked.setName("t5_booked");
        
        Thread t3_booked = new Thread(new SeatStatusModifier(synchronizedList, 3, "BOOKED"));
        t3_booked.setName("t3_booked");
        
        Thread t6_unavailable = new Thread(new SeatStatusModifier(synchronizedList, 6, "UN-AVAILABLE"));
        t6_unavailable.setName("t6_unavailable");
        
        Thread t8_booked = new Thread(new SeatStatusModifier(synchronizedList, 8, "BOOKED"));
        t8_booked.setName("t8_booked");
        
        Thread t9_booked = new Thread(new SeatStatusModifier(synchronizedList, 9, "BOOKED"));
        t9_booked.setName("t9_booked");
        
        Thread t2_canceled = new Thread(new SeatStatusModifier(synchronizedList, 2, "CANCELED"));
        t2_canceled.setName("t2_canceled");
        
        // starts all the threads
        t2_booked.start();
        t5_booked.start();
        t3_booked.start();
        t8_booked.start();
        t9_booked.start();
        t2_canceled.start();
        t6_unavailable.start();
    }
}

// A runnable class to modify the Seat Status
class SeatStatusModifier implements Runnable {
    private List<String> list;
    int seatNo;
    String status;
    
    public SeatStatusModifier(List<String> list, int seatNo, String status) {
        this.list = list;
        this.seatNo = seatNo;
        this.status = status;
    }
    
    @Override
    public void run() {
        synchronized (list) {
            System.out.println("\n" + java.time.LocalDateTime.now() + " : Make seat no "+ (this.seatNo) + " : " + this.status);
            list.set(this.seatNo-1, "Seat no " + (this.seatNo) + " is : " + this.status);
            System.out.println(Thread.currentThread().getName() + ": " + list);
        }
    }
}
We created a non-synchronized ArrayList with ten elements in the preceding code. Each element represents a seat with a corresponding status. Then we use the Collections.synchronizedList() method to create a synchronized ArrayList, which takes an ArrayList as a parameter and returns a synchronized version of the list.

We've made a couple of Threads, each with its own SeatStatusModifier class that implements the Runnable interface and overrides the run() method. We have a synchronized block in the run() method. The ArrayList object, mainly the seat status, is modified within a synchronized block to ensure thread-safe access, and the modified list elements are printed. Because the list is synchronized, multiple threads can safely modify it without causing inconsistencies or errors.

It should be noted that the order of the modified elements in the list may vary depending on how the threads access the list.

👉 It is important to note that using a synchronized ArrayList can add some performance overhead, so it should only be used when necessary, such as when multiple threads need to access the same list concurrently.

Happy coding!!! 😊
in

Popular posts