Java Interview – Collection

 

1. What is a Collection?

A Collection is a group of individual objects represented as a single unit. Java provides Collection Framework which defines several classes and interfaces to represent a group of objects as a single unit.

2. Why do we need Collection?

Arrays are not dynamic. Once an array of a particular size is declared, the size cannot be changed. When it’s full and you need to add a new element, a new array has to be created with a bigger size and all the elements from the old array have to be copied to the new array.

Collections are used in situations where data is dynamic. Collections allow adding an element, deleting an element, and other operations. There are a number of Collections in Java but you need to know how to choose the right Collection for the right context.

3. Advantages of Collection?

  • Reduces programming effort: A programmer doesn’t have to worry about the design of Collection, and he can focus on its best use in his program.
  • Increases program speed and quality: Increases performance by providing high-performance implementations of useful data structures and algorithms.

Dynamic Size: Collections provide dynamic sizing, allowing you to add, remove, or modify elements without worrying about managing the underlying data structure’s size manually. This flexibility is especially useful when the number of elements is unknown or may change over time.

Data Organization: Collections offer various data structures, such as lists, sets, maps, queues, etc., that can organize and store data in different ways to meet specific requirements. For example, ArrayList maintains an ordered collection of elements, HashSet ensures uniqueness, and HashMap provides key-value pair storage.

Type Safety: Collections in Java provide type safety by enforcing type constraints at compile-time. Generics allow you to specify the type of elements that a collection can hold, preventing runtime errors caused by incompatible data types.

3. What are the important methods in the Collection interface?

The most important methods declared in the Collection interface are the methods to add and remove an element. add method allows adding an element to a collection and delete method allows deleting an element from a collection.

size() method returns the number of elements in the collection. Other important methods defined as part of the collection interface are shown below.

interface Collection<E> extends Iterable<E>
{
  boolean add(E paramE);
  boolean remove(Object paramObject);

  int size();
  boolean isEmpty();
  void clear();

  boolean contains(Object paramObject);
  boolean containsAll(Collection<?> paramCollection);
  
  boolean addAll(Collection<? extends E> paramCollection);
  boolean removeAll(Collection<?> paramCollection);
  boolean retainAll(Collection<?> paramCollection);
  

  Iterator<E> iterator();

  //A NUMBER OF OTHER METHODS AS WELL..
}

 

4. Can you explain the List interface?

List interface extends Collection interface. So, it contains all methods defined in the Collection interface. In addition, the List interface allows operation specifying the position of the element in the Collection.

The most important thing to remember about a List interface – any implementation of the List interface would maintain the insertion order. When an element A is inserted into a List (without specifying position) and then another element B is inserted, A is stored before B in the List.

When a new element is inserted without specifying a position, it is inserted at the end of the list of elements.

However, We can also use the void add(int position, E param E); method to insert an element at a specific position.

Listed below are some of the important methods in the List interface (other than those inherited from Collection interface):

Image result for java list"

interface List<E> extends Collection<E>
{
  boolean addAll(int paramInt, Collection<? extends E> paramCollection);

  E get(int paramInt);
  E set(int paramInt, E paramE);

  void add(int paramInt, E paramE);
  E remove(int paramInt);

  int indexOf(Object paramObject);
  int lastIndexOf(Object paramObject);

  ListIterator<E> listIterator();
  ListIterator<E> listIterator(int paramInt);
  List<E> subList(int paramInt1, int paramInt2);
}

5. Can you explain the Map interface?

First and foremost, the Map interface does not extend the Collection interface. So, it does not inherit any of the methods from the Collection interface.

A Map interface supports Collections that use a key-value pair. A key-value pair is a set of linked data items: a key, which is a unique identifier for some item of data, and the value, which is either the data or a pointer to the data. Key-value pairs are used in lookup tables, hash tables and configuration files. A key-value pair in a Map interface is called an Entry.

Image result for java map"

Put method allows to add a key, value pair to the Map.

V put(K paramK, V paramV);

Get method allows to get a value from the Map based on the key.

V get(Object paramObject);Other important methods in Map Inteface are shown below

interface Map<K, V>
{
  int size();
  boolean isEmpty();

  boolean containsKey(Object paramObject);
  boolean containsValue(Object paramObject);

  V get(Object paramObject);
  V put(K paramK, V paramV);
  V remove(Object paramObject);

  void putAll(Map<? extends K, ? extends V> paramMap);
  void clear();

  Set<K> keySet();
  Collection<V> values();
  Set<Entry<K, V>> entrySet();

  boolean equals(Object paramObject);
  int hashCode();

  public static abstract interface Entry<K, V>
  {
    K getKey();
    V getValue();
    V setValue(V paramV);
    boolean equals(Object paramObject);
    int hashCode();
  }
}
 

6. What is the difference between Set and SortedSet?

Image result for java set"

SortedSet Interface extends the Set Interface. Both Set and SortedSet do not allow duplicate elements.

The main difference between Set and SortedSet is – an implementation of SortedSet interface maintains its elements in a sorted order. Set interface does not guarantee any Order. For example, If elements 4,5,3 are inserted into an implementation of Set interface, it might store the elements in any order. However, if we use SortedSet, the elements are sorted. The SortedSet implementation would give an output 3,4,5.

Important Operations in the SortedSet interface which are not present in the Set Interface are listed below:

public interface SortedSet<E> extends Set<E> {
    
    SortedSet<E> subSet(E fromElement, E toElement);
    SortedSet<E> headSet(E toElement);
    SortedSet<E> tailSet(E fromElement);
    
    E first();
    E last();

    Comparator<? super E> comparator();
}

7. What is HashSet?

HashSet implements the Set interface. So, HashSet does not allow duplicates. However, HashSet does not support ordering. The order in which elements are inserted is not maintained.

Set<String> hashset = new HashSet<String>();

hashset.add("Sachin");
System.out.println(hashset);//[Sachin]

hashset.add("Dravid");
System.out.println(hashset);//[Sachin, Dravid]

Let’s try to add Sachin to the Set now. Sachin is Duplicate. So will not be added. returns false.

hashset.add("Sachin");//returns false since element is not added
System.out.println(hashset);//[Sachin, Dravid]

8. What is a LinkedHashSet? How is it different from HashSet?

LinkedHashSet implements the Set interface and exposes similar operations to a HashSet. The difference is that LinkedHashSet maintains the insertion order. When we iterate a LinkedHashSet, we would get the elements back in the order in which they were inserted.

Image result for LinkedHashSet"

 

 

9. What is TreeSet? How is it different from HashSet?

TreeSet implements Set, SortedSet and NavigableSet interfaces. TreeSet is similar to HashSet except that it stores element’s in Sorted Order. The ordering of the elements is maintained by a set using their natural ordering whether or not an explicit comparator is provided.

Image result for Treeset java"

Set<String> treeSet = new TreeSet<String>();

treeSet.add("Sachin");
System.out.println(treeSet);//[Sachin]
Notice that the list is sorted after inserting Dravid.

//Alphabetical order
treeSet.add("Dravid");
System.out.println(treeSet);//[Dravid, Sachin]
Notice that the list is sorted after inserting Ganguly.

treeSet.add("Ganguly");
System.out.println(treeSet);//[Dravid, Ganguly, Sachin]
Sachin is Duplicate. So will not be added. returns false.

treeSet.add("Sachin");//returns false since element is not added
System.out.println(treeSet);//[Dravid, Ganguly, Sachin]

10. What is the difference between Map and SortedMap?

SortedMap interface extends the Map interface. In addition, an implementation of SortedMap interface maintains keys in a sorted order.

Methods are available in the interface to get a ranges of values based on their keys.

public interface SortedMap<K, V> extends Map<K, V> {
    Comparator<? super K> comparator();

    SortedMap<K, V> subMap(K fromKey, K toKey);

    SortedMap<K, V> headMap(K toKey);

    SortedMap<K, V> tailMap(K fromKey);

    K firstKey();

    K lastKey();
}

11. Explain the Queue interface

Queue Interface extends Collection interface. Queue Interface is typically used for implementation holding elements in order for some processing. Queue interface offers methods peek() and poll() which get the element at head of the queue. The difference is that poll() method removes the head from queue also. peek() would keep head of the queue unchanged.

interface Queue<E> extends Collection<E>
{
  boolean offer(E paramE);
  E remove();
  E poll();
  E element();
  E peek();
}

12. Explain the Iterator interface?

Iterator interface enables us to iterate (loop around) a collection. All collections define a method iterator() that gets an iterator of the collection.

hasNext() checks if there is another element in the collection being iterated. next() gets the next element.

public interface Iterator<E> {

    boolean hasNext();

    E next();
}

13. Explain ArrayList?

ArrayList implements the list interface. So, ArrayList stores the elements in insertion order (by default). Elements can be inserted into and removed from ArrayList based on their position.

Let’s look at how to instantiate an ArrayList of integers.

List<Integer> integers = new ArrayList<Integer>();

Code like below is permitted because of auto boxing. 5 is auto boxed into Integer object and stored in ArrayList.Add method (by default) adds the element at the end of the 

list.integers.add(5);//new Integer(5)

ArrayList can have duplicates.

//Iterate an ArrayList using Iterator.
Iterator<String> arraylistIterator = arraylist.iterator();
while (arraylistIterator.hasNext()) {
    String str = arraylistIterator.next();
    System.out.println(str);//Prints the 4 names in the list on separate lines.
}

Sort an ArrayList

List<String> numbers = new ArrayList<String>();
numbers.add("one");
numbers.add("two");
numbers.add("three");
numbers.add("four");
System.out.println(numbers);//[one, two, three, four]

//Strings - By Default - are sorted alphabetically
Collections.sort(numbers);

System.out.println(numbers);//[four, one, three, two]
//Sort and ArrayList using Comparable
class Cricketer implements Comparable<Cricketer> {
    int runs;
    String name;

    public Cricketer(String name, int runs) {
        super();
        this.name = name;
        this.runs = runs;
    }

    @Override
    public String toString() {
        return name + " " + runs;
    }

    @Override
    public int compareTo(Cricketer that) {
        if (this.runs > that.runs) {
            return 1;
        }
        if (this.runs < that.runs) {
            return -1;
        }
        return 0;
    }
}

List<Cricketer> cricketers = new ArrayList<Cricketer>();
cricketers.add(new Cricketer("Bradman", 9996));
cricketers.add(new Cricketer("Sachin", 14000));
cricketers.add(new Cricketer("Dravid", 12000));
cricketers.add(new Cricketer("Ponting", 11000));
System.out.println(cricketers);
//[Bradman 9996, Sachin 14000, Dravid 12000, Ponting 11000]
Collections.sort(cricketers);
System.out.println(cricketers);
//[Bradman 9996, Ponting 11000, Dravid 12000, Sachin 14000]

Sort and ArrayList using Comparator

class DescendingSorter implements Comparator<Cricketer> {

    //compareTo returns -1 if cricketer1 < cricketer2
    //                   1 if cricketer1 > cricketer2
    //                   0 if cricketer1 = cricketer2

    //Since we want to sort in descending order, 
    //we should return -1 when runs are more
    @Override
    public int compare(Cricketer cricketer1,
            Cricketer cricketer2) {
        if (cricketer1.runs > cricketer2.runs) {
            return -1;
        }
        if (cricketer1.runs < cricketer2.runs) {
            return 1;
        }
        return 0;
    }

}
Collections
        .sort(cricketers, new DescendingSorter());

System.out.println(cricketers);
//[Sachin 14000, Dravid 12000, Ponting 11000, Bradman 9996]

14. Convert Array to List

String values[] = { "value1", "value2", "value3" };
List<String> valuesList = Arrays.asList(values);
System.out.println(valuesList);//[value1, value2, value3]

15. Convert List to Array

List<String> list = Arrays.asList("C", "C++", "Java");
String[] array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array));
List<String> list = Arrays.asList("C", "C++", "Java");
String[] array = list.stream().toArray(String[]::new);
System.out.println(Arrays.toString(array));
// or
List<String> list = Arrays.asList("C", "C++", "Java");
String[] array = list.stream().toArray(n -> new String[n]);
System.out.println(Arrays.toString(array));

16. What is Vector? How is it different from List?

Vector has the same operations as an ArrayList. However, all methods in Vector are synchronized. So, we can use Vector if we share a list between two threads and we would want them synchronized.

17. What is LinkedList? How is it different from ArrayList?

Linked List extends List and Queue. Other than operations exposed by the Queue interface, LinkedList has the same operations as an ArrayList. However, the underlying implementation of Linked List is different from that of an ArrayList.

ArrayList uses an Array kind of structure to store elements. So, inserting and deleting from an ArrayList are expensive operations. However, searching in an ArrayList is faster than LinkedList.

LinkedList uses a linked representation. Each object holds a link to the next element. Hence, insertion and deletion are faster than ArrayList. But searching is slower.

18. What are the important methods in the NavigableSet interface?

//Find the highest number which is lower than 25
System.out.println(numbersTreeSet.lower(25));//5

//Find the highest number which is lower than or equal to 25
System.out.println(numbersTreeSet.floor(25));//25

//Find the lowest number higher than 25
System.out.println(numbersTreeSet.higher(25));//35

//Find the lowest number higher than or equal to 25
System.out.println(numbersTreeSet.ceiling(25));//25

 

19. What the different implementations in the Map interface?

java interview questions - map interface

Image result for map interface java

20. What is HashMap?

HashMap implements Map interface – thereby supporting key-value pairs. Let’s look at an example.

Map<String, Cricketer> hashmap = new HashMap<String, Cricketer>();
hashmap.put("sachin",
        new Cricketer("Sachin", 14000));
hashmap.put("dravid",
        new Cricketer("Dravid", 12000));
hashmap.put("ponting", new Cricketer("Ponting",
        11500));
hashmap.put("bradman", new Cricketer("Bradman",
        9996));
get method gets the value of the matching key.
System.out.println(hashmap.get("ponting"));//Ponting 11500

//if key is not found, returns null.
System.out.println(hashmap.get("lara"));//null
If existing key is reused, it would replace existing value with the new value passed in.

//In the example below, an entry with key "ponting" is already present. 
//Runs are updated to 11800.
hashmap.put("ponting", new Cricketer("Ponting",
        11800));

//gets the recently updated value
System.out.println(hashmap.get("ponting"));//Ponting 11800

21. What is TreeMap? How is different from HashMap?

TreeMap is similar to HashMap except that it stores keys in sorted order. It implements the NavigableMap interface and SortedMap interfaces along with the Map interface.

Map<String, Cricketer> treemap = new TreeMap<String, Cricketer>();

treemap.put("sachin",
        new Cricketer("Sachin", 14000));
System.out.println(treemap);
//{sachin=Sachin 14000}
We will now insert a Cricketer with key dravid. In sorted order,dravid comes before sachin. So, the value with key dravid is inserted at the start of the Map.

treemap.put("dravid",
        new Cricketer("Dravid", 12000));
System.out.println(treemap);
//{dravid=Dravid 12000, sachin=Sachin 14000}
We will now insert a Cricketer with key ponting. In sorted order, ponting fits in between dravid and sachin.

treemap.put("ponting", new Cricketer("Ponting",
        11500));
System.out.println(treemap);
//{dravid=Dravid 12000, ponting=Ponting 11500, sachin=Sachin 14000}

treemap.put("bradman", new Cricketer("Bradman",
        9996));
System.out.println(treemap);
//{bradman=Bradman 9996, dravid=Dravid 12000, ponting=Ponting 11500, sachin=Sachin 14000}

 

22. What is a PriorityQueue?

A PriorityQueue is used when the objects are supposed to be processed based on the priority. It is known that a queue follows First-In-First-Out algorithm, but sometimes the elements of the queue are needed to be processed according to the priority, that’s when the PriorityQueue comes into play. The PriorityQueue is based on the priority heap. The elements of the priority queue are ordered according to the natural ordering, or by a Comparator provided at queue construction time, depending on which constructor is used.

  • PriorityQueue doesn’t permit null.
  • We can’t create PriorityQueue of Objects that are non-comparable
  • PriorityQueue are unbound queues.
  • The head of this queue is the least element with respect to the specified ordering. If multiple elements are tied for least value, the head is one of those elements — ties are broken arbitrarily.
  • The queue retrieval operations poll, remove, peek, and element access the element at the head of the queue.
  • It inherits methods from AbstractQueue, AbstractCollection, Collection and Object class.
PriorityQueue<Integer> priorityQueue = 
                          new PriorityQueue<Integer>(); 
priorityQueue.offer(24);
priorityQueue.offer(15);
priorityQueue.offer(9);
priorityQueue.offer(45);

System.out.println(priorityQueue);//[9, 24, 15, 45]
Peek method examples

//peek method get the element with highest priority.
System.out.println(priorityQueue.peek());//9
//peek method does not change the queue
System.out.println(priorityQueue);//[9, 24, 15, 45]

//poll method gets the element with highest priority.
System.out.println(priorityQueue.poll());//9
//peek method removes the highest priority element from the queue.
System.out.println(priorityQueue);//[24, 15, 45]

//This comparator gives high priority to the biggest number.
Comparator reverseComparator = new Comparator<Integer>() {
    public int compare(Integer paramT1,
            Integer paramT2) {
        return paramT2 - paramT1;
    }

};

23. What is the Collections class?

This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, “wrappers”, which return a new collection backed by a specified collection, and a few other odds and ends.

The java.util.Collections class is a utility class in Java that provides various static methods for working with collections (interfaces and classes that implement the java.util.Collection interface). It contains methods to perform operations such as searching, sorting, shuffling, reversing, and synchronizing collections. The methods in the Collections class operate on the collection itself and do not modify the original collection; instead, they return modified views or create new collections.

The methods of this class all throw a NullPointerException if the collections or class objects provided to them are null.

java interview questions - choosing right collection

  • static int binarySearch(List, key)
  • Can be used only on sorted list
  • static int binarySearch(List, key, Comparator)
  • static void reverse(List)
  • Reverse the order of elements in a List.
  • static Comparator reverseOrder();
  • Return a Comparator that sorts the reverse of the collection current sort sequence.
  • static void sort(List)
  • static void sort(List, Comparator)

 

24. What are the differences between synchronized and concurrent collections?

Synchronized collections are implemented using synchronized methods and synchronized blocks. Only one thread can executing any of the synchronized code at a given point in time. This places severe restrictions on the concurrency of threads – thereby affecting performance of the application. All the pre Java 5 synchronized collections (HashTable & Vector, for example) use this approach.

Post Java 5, collections using new approaches to synchronization are available in Java. These are called concurrent collections. More details below.

java interview questions - synchronized vs concurrent collections

 

25. What is the initial capacity of Collection?

An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.

As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put).

The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations.

List doubles its size by 50%.

26. When does Collection throw UnSupportedOperationException?

All Java Collections extend Collection interface. So, they have to implement all the methods in the Collection interface. However, certain Java collections are optimized to be used in specific conditions and do not support all the Collection operations (methods). When an unsupported operation is called on a Collection, the Collection Implementation would throw an UnsupportedOperationException.

Arrays.asList returns a fixed-size list backed by the specified array. When an attempt is made to add or remove from this collection an UnsupportedOperationException is thrown. Below code throws UnsupportedOperationException.

List<String> list=Arrays.asList(new String[]{"ac","bddefe"});
list.remove();//throws UnsupportedOperationException

27. What is the difference between fail-fast and fail-safe iterators?

Fail Fast Iterators throw a ConcurrentModificationException if there is a modification to the underlying collection is modified. This was the default behavior of the synchronized collections of pre Java 5 age.

Fail Safe Iterators do not throw exceptions even when there are changes in the collection. This is the default behavior of the concurrent collections, introduced since Java 5.

28. Explain about the stream API?

java interview questions on streams

Streams are introduced in Java 8. In combination with Lambda expressions, they attempt to bring some of the important functional programming concepts to Java.

A stream is a sequence of elements supporting sequential and parallel aggregate operations. Consider the example code below. Following steps are done:

  • Step I : Creating an array as a stream
  • Step II : Use Lambda Expression to create a filter
  • Step III : Use map function to invoke a String function
  • Step IV : Use sorted function to sort the array
  • Step V : Print the array using forEach
Arrays.stream(new String[] {
 "Ram", "Robert", "Rahim"
})
 .filter(s - > s.startsWith("Ro"))
 .map(String::toLowerCase)
 .sorted()
 .forEach(System.out::println);

In general any use of streams involves

  • Source – Creation or use of existing stream : Step I above
  • Intermediate Operations – Step II, III and IV above. Intermediate Operations return a new stream
  • Terminal Operation – Step V. Consume the stream. Print it to output or produce a result (sum,min,max etc).

Intermediate Operations are of two kinds

  • Stateful : Elements need to be compared against one another (sort, distinct etc)
  • Stateless : No need for comparing with other elements (map, filter etc)

29. What are the atomic operations in Java?

Atomic Access Java Tutorial states “In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn’t happen at all. No side effects of an atomic action are visible until the action is complete”.

Let’s assume we are writing a multi threaded program. Let’s create an int variable i. Even a small operation, like i++ (increment), is not thread safe. i++ operation involves three steps.

  1. Read the value which is currently stored in i
  2. Add one to it (atomic operation).
  3. Store it in i

In a multi-threaded environment, there can be unexpected results. For example, if thread1 is reading the value (step 1) and immediately after thread2 stores the value (step 3).

To prevent these, Java provides atomic operations. Atomic operations are performed as a single unit without interference from other threads ensuring data consistency.

A good example is AtomicInteger. To increment a value of AtomicInteger, we use the incrementAndGet() method. Java ensures this operation is Atomic.

30. What is BlockedQueue?

BlockedQueue interface is introduced in Java specifically to address specific needs of some Producer Consumer scenarios. BlockedQueue allows the consumer to wait (for a specified time or infinitely) for an element to become available.

31. Comparator vs Comparable

The method returns a number indicating whether the object being compared is less than, equal to or greater than the object being passed as an argument.
Comparable is used for default sorting. You have a default sorting that you can’t change. Once you set it, it’s there and you can change it at runtime.

Comparable:

  • Comparable is an interface that allows objects to be compared and sorted based on their natural ordering.
  • When a class implements the Comparable interface, it defines a natural order for its objects.
  • The Comparable interface has a single method: compareTo(Object o). This method compares the current object with the specified object and returns a negative integer, zero, or a positive integer to indicate the object’s relative order.
  • The natural ordering defined by Comparable is used by default in sorting methods like Collections.sort() or Arrays.sort().
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    // Constructor and other methods

    @Override
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
}

 

@Override
public int compareTo(Player otherPlayer) {
     return (this.getRanking() - otherPlayer.getRanking());
}

Order depending on the comparison. Pay attention here to the comparison.(this.getRanking() - otherPlayer.getRanking());// ascending(otherPlayer.getRanking() – this.getRanking());// descending

Comparator:

  • Comparator is an interface that allows for custom comparison and sorting of objects that do not implement the Comparable interface or when a different sorting order is required.
  • With a Comparator, you can define multiple comparison rules and sorting orders for objects of a class.
  • The Comparator interface has a single method: compare(Object o1, Object o2). This method compares two objects and returns a negative integer, zero, or a positive integer to indicate the objects’ relative order.
  • The Comparator interface provides flexibility in sorting by allowing you to define different comparison logic for different scenarios.
  • Comparator implementations can be used with sorting methods by explicitly passing the Comparator object as a parameter.
public class PersonAgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return Integer.compare(p1.getAge(), p2.getAge());
    }
}

 

Comparator

public class PlayerRankingComparator implements Comparator<Player> {
  
    @Override
    public int compare(Player firstPlayer, Player secondPlayer) {
       return (firstPlayer.getRanking() - secondPlayer.getRanking());
    }
}

You can Create different comparators(sortings) and use them according to your needs. With comparable you are stuck to one type of sorting.

  • Sometimes, we can’t modify the source code of the class whose objects we want to sort, thus making the use of Comparable impossible
  • Using Comparators allows us to avoid adding additional code to our domain classes
  • We can define multiple different comparison strategies which isn’t possible when using Comparable

In most real-life scenarios, we want sorting based on different parameters. For example, as a CEO, I would like to sort the employees based on Salary, an HR would like to sort them based on age. This is the situation where we need to use Java Comparator interface because Comparable.compareTo(Object o) method implementation can provide default sorting and we can’t change it dynamically. Whereas with Comparator, we can define multiple methods with different ways of sorting and then chose the sorting method based on our requirements.

  1. Comparable interface can be used to provide single way of sorting whereas Comparator interface is used to provide different ways of sorting.
  2. For using Comparable, Class needs to implement it whereas for using Comparator we don’t need to make any change in the class.
  3. Comparable interface is in java.lang package whereas Comparator interface is present in java.util package.
  4. We don’t need to make any code changes at client side for using Comparable, Arrays.sort() or Collection.sort() methods automatically uses the compareTo() method of the class. For Comparator, client needs to provide the Comparator class to use in compare() method.

Comparable is used to define the natural ordering of objects within a class, while Comparator is used to define custom ordering or multiple comparison rules for objects. If a class implements Comparable, its natural ordering is used by default for sorting. If a custom ordering or multiple sorting options are needed, a Comparator can be implemented and passed explicitly to sorting methods.

32. How to sort by multiple fields?

public int compare(Member a, Member b){
	// sort by age
	int order = a.getAge() - b.getAge();

	if(order==0){
		// if ages are the same, compare name
		order = a.getName().compareTo(b.getName());
	}

	if(order==0){
		// if ages and names are the same, compare gender
		order = a.getGender().compareTo(a.getGender());
	}

	return order;
}

33. What is the importance of hashCode() and equals() methods ?

hashCode() and equals() are used for object comparison, hashing, and indexing purposes.

The hashCode() and equals() methods are closely related and must be implemented consistently. When overriding equals(), you should also override hashCode() to ensure that equal objects have the same hash code. This is crucial when using objects in hash-based collections to maintain data integrity and retrieve objects correctly based on equality.

hashCode() Method:

  • The hashCode() method returns an integer value, known as the hash code, which represents the object’s internal address or identity.
  • Hash codes are used extensively in hashing-based data structures like hash tables, hash sets, and hash maps to determine the object’s storage location or bucket.
  • Objects that are equal according to the equals() method must have the same hash code. However, objects with the same hash code are not necessarily equal according to equals().
  • It is important to override the hashCode() method when overriding the equals() method to ensure consistency between the two methods. This is necessary to maintain the contract between equality and hash-based collections.
  • The hashCode() method should be implemented in a way that distributes hash codes evenly across different objects to avoid collisions and improve the performance of hash-based data structures.

 

equals() Method:

The equals() method is used to compare the equality of two objects. It determines whether two objects have the same content or state.

The default implementation of equals() in the Object class compares object references for identity equality, which checks if two object references point to the same memory location.

By overriding the equals() method, you can provide your own implementation to define equality based on specific criteria, such as comparing the values of certain attributes.

The equals() method must satisfy the following properties:

  • Reflexivity: An object must be equal to itself (x.equals(x) should return true).
  • Symmetry: If x.equals(y) returns true, then y.equals(x) should also return true.
  • Transitivity: If x.equals(y) and y.equals(z) both return true, then x.equals(z) should also return true.
  • Consistency: Multiple invocations of equals() on the same objects should consistently return true or false, provided that the object’s state doesn’t change.
  • Null Handling: x.equals(null) should return false, and it should not throw an exception.

It is recommended to override the equals() method whenever a custom class defines its notion of equality based on attribute values.

  1. equals(Object otherObject) – As method name suggests, is used to simply verify the equality of two objects. It’s default implementation simply check the object references of two objects to verify their equality. By default, two objects are equal if and only if they are stored in the same memory address.
  2. hashcode() – Returns a unique integer value for the object in runtime. By default, integer value is mostly derived from memory address of the object in heap (but it’s not mandatory always).
  3. This hash code is used for determining the bucket location, when this object needs to be stored in some HashTable like data structure.

Contract

It is generally necessary to override the hashCode() method whenever equals() method is overridden, so as to maintain the general contract for the hashCode() method, which states that equal objects must have equal hash codes.

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
    This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.
    However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

hashCode() and equals() is used to compare two objects to determine whether they are the same or equal or not.
In HashMap, hashCode() is used to calculate the bucket and therefore calculate the index.
HashMap uses equals() to compare the key whether the are equal or not. If equals() method return true, they are equal otherwise not equal.

HashMap in Java uses the hashCode and equals methods to determine the index of the key-value pair. These methods are also used when we request the value of a specific key. If these methods are not implemented correctly, two different keys might produce the same hash value and thus, will be considered as equal by the collection. Furthermore, these methods are also used to detect duplicates. Thus, the implementation of both methods is crucial to the accuracy and correctness of the HashMap.

 

 

 

 

 




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *