Java / Collections
public static Map<String, String> splitQuery(String urlquery) throws UnsupportedEncodingException { Map<String, String> query_pairs = new LinkedHashMap<String, String>(); String[] partStr= urlquery.split("\\?"); if (partStr.length >1 && partStr[1] != null) { String[] pairs = partStr[1].split("&"); for (String pair : pairs) { int idx = pair.indexOf("="); query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); } } return query_pairs; }
hashcode() method of the Key object being passed as an argument to the get method finds the unique bucket location in backing array and the value from the entry(Key, value) is returned.
one hashcode() (one entry) corresponds to one bucket location.
Java collections API is the implementation of data structures; Java collection is nothing but a java object that point to a group of objects.
When put() method stores the key at the same bucket location collision would have occurred, so the bucket location upgrades itself to store multiple entries by forming a linked data structure, and once key.hashcode() find the bucket location, all the entries are traversed to find the correct key by comparing using equals() method and value from the corresponding entry will be returned.
returns null.
- improves re-usability and interoperability.
- Less programming/development effort.
- Improved code quality and well structured.
- Utility functions to perform processing/manipulation over the collection data e.g.sort, search.
- enhances code readability.
Collections class has its own static method reverse(List list) that could reverse any type of list.
Collections.reverse(myListObject);
In three ways,
Using add(Object obj) method,
ArrayList<String> list = new ArrayList<String>(); list.add("Apple"); list.add("Banana"); list.add("Cherry");
Using Double brace initialization, (an anonymous inner class with an instance initializer.
ArrayList<String> alphabets = new ArrayList<String>() {{ add("Apple"); add("Banana"); add("Chery"); }};
Using,
List<String> fruitList = Arrays.asList("Apple", "Banana", "Cherry");
Under java.util package.
Collection interface is the root interface and it is part of java.util package. Although Collection extends java.lang.Iterable, Iterable is not part of collection hierarchy.
Collections.synchronizedList() can be used to synchronize arraylist. This method returns synchronized list backed by the specified list.
No.
Entry is an inner class of HashMap that stores key-value pair.
poll() and remove() method from Queue is used to remove the object and returns the head of the queue.
However If Queue is empty() then a call to remove() method will throw Exception, whereas a call to poll() method returns null.
Fail-fast Iterators throws ConcurrentModificationException when one thread is iterating over the collection object and other thread structurally modify the Collection either by adding, removing or modifying objects on underlying collection. They are called fail-fast because it immediately throws Exception when they encounter modification. On the other hand, fail-safe Iterators works on a copy of the collection instead of the original collection.
Fail Fast Iterator. | Fail Safe Iterator. |
Fail Fast iterator throws ConcurrentModificationException. | Fail Safe doesn't throw. |
Doesn't clone; works directly on the collection object. | Creates a copy of collection by cloning. |
Doesn't cause memory overhead. | causes memory overhead. |
Examples: HashMap, Vector, ArrayList and HashSet. | Examples: CopyOnWriteArrayList, ConcurrentHashMap, and ConcurrentLinkedQueue. |
Stack, Properties , Vector and Hashtable are synchronized classes (or thread-safe).
The core collection interfaces are : Collection , Set , Queue , List , Map
Set can hold only unique elements where as List can contain duplicate elements.
Set is unordered while List is ordered; List maintains the order of object insertion.
Map object has unique keys each containing some value, while Set contain only unique values.
Class implementing List interface are ArrayList, Vector, LinkedList
Class implementing Set interface : HashSet, TreeSet
Iterator is an interface that provides specification for methods to iterate over any Collection.
Iterator has remove() method while Enumeration doesn't.
Hence, using Iterator we can modify the collection by adding and removing the objects. Enumeration acts as a read only interface, only traverse through the objects.
It uses iterator design pattern. Iterator design pattern allows us to navigate through the collection of objects by using a common interface irrespective on type of collection.
Enumeration is also an example of Iterator design pattern.
equals() and hashCode() method has to be overriden by the object and provide its own implementation.
Queue is a data structure which is based on FIFO (first in first out). e.g. in the real world, who gets into the ticket counter queue gets the ticket and leave the queue.
Stack is a data structure which is based on LIFO (last in first out). e.g. stacked plates, where we need to remove the top plates to access the plate from the middle of the plates. Also Towers of Hanoi problem is an example of stack.
Arrays class of java.util package contains the method asList() which accepts the array as parameter. So,
String[] fruits = {"Apple" , "Orange" , "Banana"}; List fruitsList = Arrays.asList(fruits);
HashMap allows one null key and any number of null values while Hashtable does not allow null as either keys or values.
HashMap is not synchronized or thread-safe whereas Hashtable is synchronized or thread-safe.
Both poll() and remove() method is used to remove head object of the Queue.
The main difference lies when the Queue is empty(). If Queue is empty then poll() method will return null . While in similar case , remove() method will throw NoSuchElementException . peek() method retrieves but does not remove the head of the Queue. If queue is empty then peek() method also returns null.
Using Iterator we can traverse the list of objects only in forward direction . But ListIterator can traverse the collection in both directions that is forward as well as backward.
ListIterator has .add() method whereas Iterator does not have.
Array is static in size while ArrayList is dynamic in size.
Array can contain primitive data types or Objects while ArrayList can only hold Objects and can not contain primitive data types.
HashSet's contains() method is case sensitive and does not allow the use of comparators.
We could use TreeSet instead of HashSet which allow Comparator thus facilitating case-insensitive search and comparison. Using the comparator String.CASE_INSENSITIVE_ORDER we could perform case ignored search.
In the below example search for 'A' at the Hashset fails since the set has the all the elements in lower case. However the same search at the TreeSet finds the element using the String.CASE_INSENSITIVE_ORDER comparator.
public static void main(String[] args) { List<String> inputList = Arrays.asList(new String[] { "a", "b", "c" }); /****************************************/ // HashSet Does not ignore the case. Set<String> hashSet = new HashSet<String>(); hashSet.addAll(inputList); System.out .println("Does Hashset has value A? " + hashSet.contains("A")); /****************************************/ // TreeSet Does not ignore the case. Set<String> treeSetCaseIgnored = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER); treeSetCaseIgnored.addAll(inputList); /****************************************/ System.out.println("Does Hashset has value A? " + treeSetCaseIgnored.contains("A")); /****************************************/ }
Using Collections.swap.
It swaps the elements at the specified positions in a specified list.
swap(List listElement, int i, int k)
This static method of Collections class swap the element between the position i and k at the listElement.
import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class CollectionsSwap { public static void main(String[] args) { List<String> fruitList = Arrays.asList("Apple", "Orange", "Strawberry"); System.out .println("The fruits available in the shopping List based on priority:"); for (String fruit : fruitList) { System.out.println("* " + fruit); } // Swapping the priority between Orange and apple Collections.swap(fruitList, 0, 1); System.out .println("The fruits available in the shopping List (priority changed):"); for (String fruit : fruitList) { System.out.println("* " + fruit); } } }
Java.util.IdentityHashMap implements Map interface and it does not make use of equals() and hashcode() methods to compare objects insteadIdentityHashMap uses equality operator "==" to compare the key and value objects.
The use of the equality operator makes IdentityHashMap perform faster compared to the HashMap and it suits the need where reference equality check is required rather than the logical semantics.
IdentityHashMap is not synchronized.
Although this class implements the Map interface, it violates the general contract that mandates the use of equals method when comparing key/value objects.
This Api is introduced as part of Java 1.4.
Although these implementations had considerable difference, there share similarities with each other.
- None of them allow duplicate elements.
- None of them are synchronized.
- All are Cloneable and Serializable.
- Iterator returned by these implementations are fail-fast i.e We will encounter ConcurrentModificationException if the collection is modified after the creation of Iterator object.
- Last but not least all implement Set interface.
LinkedList is the doubly linked list implementation of List interface whereas ArrayList is the resizable array implementation of List interface.
Retrieval (get (int index)) and search operations are faster in ArrayList compared to LinkedList in terms of performance as ArrayList internally uses array data structure and leverages index based retrieval. In case of LinkedList it traverses through every node from start to end or end to start (closest takes priority) to retrieve the node at the specified index. Array List get (int index) operation performs in constant time o (1) while LinkedList get (int index) operation run time is o (n).
Insertion of elements and removal operations are generally faster in LinkedList rather than ArrayList.
LinkedList can be traversed in reverse direction by using descending Iterator while there is no API available for ArrayList to traverse in backward direction.
Memory overhead is a concern in LinkedList as LinkedList needs to store and maintain the location of next and previous node elements whereas in ArrayList at each index it holds just the actual element or object.
By default, ArrayList creates an empty list with initial capacity of 10 (when capacity not specified) while LinkedList creates an empty list with no initial capacity.
ArrayList class can act as a list only because it implements List only. LinkedList class can act as a list and queue both because it implements List and Deque interfaces.
Both ArrayList and LinkedList implements List interface and their API are identical.
Both allows null as an element and even multiple null is possible as well since List allows duplicates.
Both ArrayList and LinkedList are not synchronized and we could render as synchronized using Collections.synchronizedList method.
Both returns a shallow copy of the original object when clone() method is invoked and the elements itself are not cloned.
Both ArrayList and LinkedList preserve the order of the elements in the way it is added to the ArrayList or LinkedList.
The Iterator and the ListIterator of ArrayList and LinkedList are fail-fast so in case of any modification to the list while iterating throws ConcurrentModificationException.
Enumeration of both ArrayList and LinkedList is fail-fast, any modification on the ArrayList or LinkedList throws concurrent exception.
Collections.sort implementation uses merge sort or tim sort.
The Collection interface specifies groups of objects known as elements. Each concrete implementation of a Collection can choose its own way of how to maintain and order its elements. The semantics and the implications of either cloning or serialization come into play when dealing with actual implementations. Thus, the concrete implementations of collections should decide how they can be cloned or serialized.
Comparable. | Comparator. |
Class whose objects to be sorted must implement this interface and to implement compareTo(Object) method. | Class whose objects to be sorted do not need to implement this interface. Instead a third class can implement this interface to sort and implement compare method. |
Sorting logic must be in same class whose objects are being sorted. Hence this is called natural ordering of objects. | Sorting logic is in separate class. Hence we can write different sorting based on different attributes of objects to be sorted. |
Collections.sort(List). Objects will be sorted on the basis of CompareTo method. | Collections.sort(List, Comparator). Objects will be sorted based on the Compare method in Comparator. |
Comparable located in Java.lang package. | Comparator located in Java.util package. |
Concurrent Collections has better performance than synchronized Collection because they lock only a portion of Map to achieve concurrency and Synchronization.
BlockingQueue implements java.util.Queue interface. BlockingQueue supports operations that wait for the queue to become non-empty when retrieving an element , and wait for space to become available in the queue when storing an element .
BlockingQueue does not accept null elements and it's implementations are thread-safe. Blocking queues are primarily designed for the producer-consumer problems. This concurrent Collection class was added in jdk 1.5.
When an element cannot be added to collection the add method throws an exception while offer method doesn't.
Java 8 uses streams and lambdas to perform filtering in one line of code.
List<Employee> highlyPaidEmployees= employees.stream() .filter(p -> p.getSalary() > 1500000).collect(Collectors.toList());
Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
Collection is used for storing data in different data structures while Stream API is used for computation of data on a large set of Objects.
Collection API we can store a finite number of elements in a data structure. With Stream API, we can handle streams of data that can contain infinite number of elements.
Eager vs. Lazy: Collection API constructs objects in an eager manner. Stream API creates objects in a lazy manner.
Multiple consumption: Most of the Collection APIs support iteration and consumption of elements multiple times. With Stream API we can consume or iterate elements only once.
In Java 8, Predicate a functional interface used as the assignment target for a lambda expression or method reference.
You may use them anywhere where you need to evaluate a condition on group/collection of similar objects such that evaluation can result either in true or false.
public static void main(String[] args) { List<Integer> intList = new ArrayList<Integer>(); intList.add(1); intList.add(2); intList.add(3); intList.add(4); intList.add(5); intList.add(6); intList.forEach(i -> { System.out.println(i); }); // Filter elements that has value greater than 4 Predicate<Integer> filter = i -> i > 4; intList.removeIf(filter); System.out.println("After applying removeIf function"); intList.forEach(i -> { System.out.println(i); }); } }
Lambda expression enable functional programming in Java.
Passing a lambda expression as an object to a method eliminates the overhead involved in passing an anonymous class.
We can also pass a method as parameter to another method using lambda expressions.
Both helps in evaluating lambda expressions. The difference is a predicate takes one argument and returns a boolean value while a function takes one argument and returns an object.
Both generates incremental number by one from start to end point in java 8. The difference is range does not include last number while rangeClosed does.
For example IntStream.range(1,6) produces value from 1 through 5 while rangeClosed generates 1 to 6.
Parallelism.
Parallelization can be achieved just by calling parallel() and it is implemented using fork and join thread pool framework.
The Supplier is a functional nterface that represents an operation that takes no argument and returns an object.
It declares the functional method Object get().
java.util.function.BooleanSupplier is a functional interface whose functional method is boolean getAsBoolean(). This method returns a boolean result.
Java 8 Stream API provides the following capabilities.
To perform Database like Operations such as group by, order by operations.
Enables Parallel Operations there by improving performance.
Enables Functional Style programming so we will focus on what to do rather than how to do.
Enables operations to be performed lazily that optimizes performance.
Collection | Java 8 | Before Java 8 | ||
---|---|---|---|---|
Initial capacity | Load factor | Initial capacity | Load factor | |
ArrayList | 0 (lazily initialized to 10) | 10 | ||
Vector | 10 | 10 | ||
HashSet | 16 | 0.75 | 16 | 0.75 |
HashMap | 16 | 0.75 | 16 | 0.75 |
Hashtable | 16 | 0.75 | 11 | 0.75 |
To identify the structural modification in the collection, fail-fast iterators use an internal flag called modCount
which is updated each time a collection is modified. Fail-fast iterators check this flag while calling next() method to retrieve next value and if it finds that modCount changed after iterator being created, it throws ConcurrentModificationException
.