Monday, January 31, 2022

Java Stream - Collectors.teeing() With Examples

In the Java Stream API, Collectors class implements Collector and provides many useful reduction operations like Collectors.groupingBy(), Collectors.partitioningBy(). There is also a Collectors.teeing() method added in Java 12 that takes two downstream Collectors as method argument and passes each stream element through these two downstream Collectors, there is also a third argument, a merging function that merges the results of both collectors into the final result.

It is a handy utility method for performing certain tasks where more processing is required with in a single method call.

Syntax of Collectors.teeing() method

Collector<T,?,R> teeing(Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)

Here parameters are-

  • downstream1- First downstream collector
  • downstream2- Second downstream collector
  • merger- Function which merges two results into a single one

Collectors.teeing() Java examples

1. If you want to get the average of numbers in a List. You can do that in a single method call by using Collectors.teeing(). First downstream Collector argument can do the job of counting elements, second Collector argument can do the job of getting the sum of elements and the merger operation can do the job of calculating average.

import java.util.List;
import java.util.stream.Collectors;

public class CollectorsTeeing {

  public static void main(String[] args) {
    List<Integer> numList = List.of(16, 27, 19, 75, 56);
    Double average = numList.stream().collect(Collectors.teeing(
        Collectors.counting(), Collectors.summingDouble(n -> n), 
        (count, sum) -> sum/count));
    System.out.println("Average of elements in the list- " + average);    

  }
}

Output

Average of elements in the list- 38.6

2. Using Collectors.teeing() method to get the employee with the maximum salary and employee with the minimum salary from the List of Employee objects.

Employee class

public class Employee {
  private String empId;
  private int age;
  private String name;
  private char gender;
  private int salary;
  Employee(String empId, int age, String name, char gender, int salary){
    this.empId = empId;
    this.age = age;
    this.name = name;
    this.gender = gender;
    this.salary = salary;
  }
  public String getEmpId() {
    return empId;
  }

  public int getAge() {
    return age;
  }

  public String getName() {
    return name;
  }

  public char getGender() {
    return gender;
  }

  public int getSalary() {
    return salary;
  }
  @Override
  public String toString() {
      return "Emp Id: " +  getEmpId() + " Name: " + getName() + " Age: " + getAge();
  }
}

Here first downstream collector gets the employee with max salary and the second downstream collector gets the employee with min salary. Merger function creates a list to store both employee objects.

public class CollectorsTeeing {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 12000), 
        new Employee("E006", 28, "Amy", 'F', 14000)); 
    List<Optional<Employee>> list = empList.stream().collect(Collectors.teeing(
        Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)), 
                Collectors.minBy(Comparator.comparingInt(Employee::getSalary)), 
                   (emp1, emp2) -> {
                      List<Optional<Employee>> e = new ArrayList<>();
                        e.add(emp1);
                        e.add(emp2);
                        return e;
                   }));
    System.out.println("Employee with max salary- " + (list.get(0).isPresent()? list.get(0).get().getName():null));
    System.out.println("Employee with min salary- " + (list.get(1).isPresent()? list.get(1).get().getName():null));  

  }
}

Output

Employee with max salary- Amy
Employee with min salary- Ram

That's all for this topic Java Stream - Collectors.teeing() With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream - Collectors.joining() With Examples
  2. Java Stream - Collectors.partitioningBy() With Examples
  3. Java Stream - Collectors.summingInt(), summingLong(), summingDouble()
  4. Java Stream - flatMap() With Examples
  5. Java Stream - Convert Stream to Set

You may also like-

  1. How to Get The Inserted ID (Generated ID) in JDBC
  2. Buffered Streams in Java IO
  3. Array in Java With Examples
  4. Java Program to Delete File And Directory Recursively
  5. Angular Reactive Form Validation Example
  6. Angular HttpClient - Set Response Type as Text
  7. Namespace And Variable Scope in Python
  8. Spring MVC Radiobutton And Radiobuttons Form Tag Example

Sunday, January 30, 2022

Heap Memory Allocation in Java

In the post JVM run-time data areas we have already got a brief idea about the memory areas used while running a Java application. In this post we’ll talk about Java Heap memory space in detail – How Heap memory is allocated, how garbage collection happens, heap memory tuning and heap memory profiling.


Heap memory allocation in Java

Heap is the JVM run-time data area where the Java objects reside. Apart from Java objects, memory for instance variables and arrays is also allocated on the heap. Heap is created on the JVM start-up and shared among all Java Virtual Machine threads.

Heap memory in Java is divided into two areas (or generations)-

  • Young Space (generation)- The young generation consists of eden and two survivor spaces. Most objects are initially allocated in eden.
  • Old Space (or Tenured generation)– When objects (which have survived garbage collection) residing in young space have reached a certain age threshold they are moved to old space.

These generations have their own memory pool allocated by the JVM.

Java heap memory generations
Heap memory areas

Why is heap memory managed in generations

Here let’s deviate a little and try to understand why memory is managed in generations and what benefit do we get out of it. In one line it can be explained as these division into generations makes garbage collection more efficient.

As you must be knowing Garbage collection in Java is an automatic storage management system that frees space in the heap by reclaiming memory for objects that are not having any reference. Note that, an object is considered garbage when it can no longer be reached from any pointer in the running program.

A very simplistic garbage collection algorithms will iterate over every reachable object. Any objects left over are considered garbage. With this algorithm the time taken is proportional to the number of live objects in the whole heap.

In order to minimize the time it takes to do garbage collection the approach taken by JVM is known as "generational collection". This approach works on the weak weak generational hypothesis, which states that most objects survive for only a short period of time.

In order to optimize for this hypothesis, memory is managed in generations. Initially objects are allocated in the young generation (Eden space) and most objects die there.

When the young generation fills up, it results in a minor collection (Minor GC) in which only the young generation is collected, that way rather than iterating over the objects in the whole heap only a small portion of the Heap (Young generation) is inspected for dead objects. If the hypothesis stated above holds true then most of the objects will die there and reclaimed with in the younger generation.

Only a small portion of the objects will survive the garbage collection in younger generation and after a certain time lapse will move to tenured generation. Eventually, the tenured generation will fill up and must be garbage collected, that will result in a major collection (Major GC), in which the entire heap is collected.

How does garbage collection work on the heap

Now that you know that Heap is divided into generations and how this division helps garbage collector to run more efficiently as GC has to go through only a part of the heap space and within that less space iteration most of the objects (Remember most of the objects die young!) can be garbage collected.

Let’s see in more detail how garbage collection works across these generations and what happens when minor GC is triggered and what happens when major GC is triggered.

  • Within the young generation initially any new objects are allocated to the eden space. Both survivor spaces (S0 and S1) are initially empty.
  • A minor garbage collection is triggered when eden space is filled up. All the unreferenced objects are garbage collected and reference objects are moved to the first survivor space (S0). One survivor space is empty at any time.
  • When the minor garbage collection is triggered next time, all the unreferenced objects are garbage collected and reference objects are moved to the survivor space. Note that this time referenced objects are moved to the second survivor space (S1). There is one more step; objects from the previous minor GC on the first survivor space (S0) have their age incremented and get moved to S1. Note that one survivor space is empty at any time.
  • This process of clearing eden space, moving the referenced objects to one of the survivor spaces, incrementing the age for the surviving objects keeps on repeating with every minor GC. There is also a check for object’s age reaching a certain threshold. Objects that reach the threshold are moved from young generation to the old generation.
  • With every minor GC aged objects will be moved from young to old generation space.
  • When that movement of object fills up the tenured space that triggers a major GC in which the entire heap is collected. Major garbage collection lasts much longer than minor collections because a significantly larger number of objects are involved. Apart from garbage collecting the objects, major GC also compacts the space as it is defragmented from cleaning of the objects.

Refer Garbage Collection in Java to know more about garbage collection process and available garbage collectors in Java

VM Heap Size tuning options in Java

Heap memory in Java will have three things – live objects, dead objects and some portion of the memory which is still free. The JVM heap size determines the frequency of garbage collection and the time spent on collecting garbage.

If you set a large heap size frequency of garbage collection will be less but the time spent on collecting garbage will be more because of the large size (means having more objects to inspect). On the other hand if you do the opposite then time spent on collecting garbage will be less but frequency will increase as smaller heap will fill faster.

An acceptable heap size is application-specific and should be adjusted using the provided options after analyzing the actual time and frequency of garbage collections.

Java heap size options

  1. Task: Setting initial heap size

    Option: -Xms

    As example: -Xms40m

  2. Task: Setting maximum heap size

    Option: -Xms

    As example: -Xmx512m

    At initialization of the JVM, the entire space for the heap is reserved. The size of the space reserved can be specified with the -Xmx option. If the value provided with the -Xms parameter is smaller than the value provided with the -Xmx parameter, then all of the space that is reserved for the heap is not committed to the virtual machine. The different generationso of the heap (Young and tenured) can grow to that limit (provided with -Xmx) as and when needed.

    It is recommended that you set initial heap size (-Xms) equal to the maximum heap size (-Xmx) to minimize garbage collections.

  3. If you have not given same values for parameters -Xms and -Xmx then the virtual machine will grow or shrink the heap at each garbage collection to try to keep the proportion of free space to live objects within a specific range. The options to set this target ranges are-

  4. Task: To maintain minimum percentage heap free space

    Option: -XX:MinHeapFreeRatio=<minimum>

    As example: -XX:MinHeapFreeRatio=40

  5. Task: To maintain maximum percentage heap free space

    Option: -XX:MaxHeapFreeRatio=<maximum>

    As example: -XX:MaxHeapFreeRatio=70

    With the parameters as used in the example let's try to understand these options better. If the percent of free space in a generation falls below 40%, then the generation will be expanded to maintain 40% free space, up to the maximum allowed size of the generation. Similarly, if the free space exceeds 70%, then the generation will be contracted so that only 70% of the space is free, subject to the minimum size of the generation.

  6. After heap size setting parameters another option that affects GC performance is the proportion of the heap dedicated to the young generation. If you set the young generation to be bigger, minor collections will occur less often. But that would mean a smaller tenured generation, which will increase the frequency of major collections.

    Three options for tuning the young generation are-

  7. Task: Setting the Young generation heap size

    Option: -XX:NewSize

    It is recommended to set -XX:NewSize to be one-fourth the size of the heap size.

  8. Task: Set the maximum size of the Young Generation heap size.

    Option: -XX:MaxNewSize

  9. Task: Controlling the ratio between young and tenured generation

    Option: -XX:NewRatio

    As example if you set -XX:NewRatio=3 that would mean the ratio between the young and tenured generation is 1:3. The size of the eden + survivor spaces will be one-fourth of the total heap size.

  10. You can also tune the size of the survivor spaces, for that you can use the parameter SurvivorRatio.

  11. Task: Tune the size of the survivor spaces

    Option: -XX:SurvivorRatio

  12. As example if you set -XX:SurvivorRatio=6 that would mean the ratio between eden and a survivor space is 1:6. Which means each survivor space will be one-sixth the size of eden, thus one-eighth the size of the young generation.

Heap memory profiling

Though there are many tools available to profile the memory, I am mentioning one I have already used Java VisulalVM. It’s free and comes bundled with JDK itself. Another tool you will find in the same location is jconsole which is also a monitoring tool.

To launch you just need to go to bin directory of your installed JDK folder and launch jvisualvm from there.

On the left side pane it will show the running Java applications, just click on the one you want to inspect.

Here I am demonstrating its use with a very simple application where I have created a thread and in that thread I am creating 5000 objects of another class with some thread pause (by using sleep method) in between. For this program I changed the -Xms and -Xmx so that the heap is small.

First image shows the heap when the program is just started, that’s why you see a very little variance in the used heap.

Second image is a snapshot of the heap memory when garbage collection occurred that is why you can see a sudden dip in the used heap memory. If you see at 3:50 PM in the graph you will see a GC activity which has taken 1.3% of CPU time.

Third image also shows a GC activity. At 3:52 PM you can see a barely visible blue mark showing GC activity and a corresponding dip in the used heap memory.

So using VisualVM GUI tool you can monitor your application’s memory usage, also analyze process threads and get a thread dump. Also profile the performance of your application by analyzing CPU and memory usage.

That's all for this topic Heap Memory Allocation in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. PermGen Space Removal in Java 8
  2. Just In Time Compiler (JIT) in Java
  3. What Are JVM, JRE And JDK in Java
  4. How to Run a Shell Script From Java Program
  5. How to Pass Command Line Arguments in Eclipse

You may also like-

  1. Reflection in Java - Getting Field Information
  2. Java Object Cloning - clone() Method
  3. Java Stream API Examples
  4. Lambda Expressions in Java 8
  5. Lock Striping in Java Concurrency
  6. AtomicInteger in Java With Examples
  7. Java ReentrantReadWriteLock With Examples
  8. How HashMap Works Internally in Java

Saturday, January 29, 2022

Merge Sort Program in Java

In this post we’ll see how to write Merge sort program in Java. Merge sort is much more efficient than the simple sort algorithms like bubble sort and insertion sort. One drawback is that it requires an additional array along with the original array that is sorted.

How merge sort works

Merge sort works on the concept of merging two sorted arrays to create another array which is also sorted.

Now the question is how do you get sorted arrays which are merged? Since merge sort is also termed as divide and conquer algorithm so the idea is to divide the input array into two halves then each of these halves are further divided into halves and so on until you get sub-arrays with only one element which is considered a sorted array.

At that point you start merging these sub-arrays, from two single element sub-arrays you create a sorted merged array of two elements. Then two such sorted sub-arrays of two elements are merged to create a sorted array of four elements and so on until you have a sorted array of all the elements.

Since array is recursively divided into two halves so this division process can be written as a recursive method where base case becomes the point when you have sub-arrays with only one element each.

Let’s try to understand with an example where we have an input array as given below.

int[] intArr = {21, 11, 33, 70, 5, 25, 65, 55};

The process of recursive calls to divide the array into halves can be explained using the following image.

merge sort in java

At this point merge process starts which merges two sorted arrays to create another sorted array, this merging process can be explained using the following image.

merge sort program java

Merge Sort Java program

In the merge sort program there is a method mergeSortRecursive which is called recursively to divide the array.

Merge method merges the two sub-arrays to create a sorted array.

public class MergeSort {
  public static void main(String[] args) {
    int[] intArr = {47, 85, 620, 3456, -7, 10, 4500, 106, -345, 1000, 67, 80, 5500, 34, 78, 782, 4, 0, 99, 190};
    MergeSort ms = new MergeSort();
    ms.mergeSortRecursive(intArr, 0, intArr.length-1);
    System.out.println("Sorted array after merge sort- ");
    for(int num : intArr){
      System.out.print(num + " ");
    }
  }
    
  private void mergeSortRecursive(int[] intArr, int lower, int upper){
    //base case
    if (lower == upper){
      return;
    }else{
      // get mid point for division of array
      int middle = (lower + upper)/2;
      
      mergeSortRecursive(intArr, lower, middle);        
      mergeSortRecursive(intArr, middle+1, upper);
      
      merge(intArr, lower, middle, upper);
    }
  }
    
  private void merge(int[] intArr, int lower, int middle, int upper){
      /** Create two temp arrays pertaining to two halves that 
       are being merged and add elements to them  */
      int subArrayOneLength = middle - lower + 1;
      int subArrayTwoLength = upper - middle;
      int[] temp1 = new int[subArrayOneLength];
      int[] temp2 = new int[subArrayTwoLength];
      for(int i = 0; i < subArrayOneLength; i++){
        temp1[i] = intArr[lower + i];
      }
      for(int j = 0; j < subArrayTwoLength; j++){
        temp2[j] = intArr[middle + 1 + j];
      }           
      int i =0;        
      int j = 0;
      // merging process, merge two temp arrays 
      while((i < subArrayOneLength) && (j < subArrayTwoLength)){
        if(temp1[i] < temp2[j]){
          intArr[lower] = temp1[i++];
        }else{
          intArr[lower] = temp2[j++];
        }
        lower++;
      }
      // If there are more elements
      while(i < subArrayOneLength){
        intArr[lower++] = temp1[i++];
      }
      while(j < subArrayTwoLength){
        intArr[lower++] = temp2[j++];
      }
  }
}

Output

Sorted array after merge sort- 
-345 -7 0 4 10 34 47 67 78 80 85 99 106 190 620 782 1000 3456 4500 5500 

Performance of merge sort

In merge sort there is subdivision of arrays and for each sub-division there is merging. Number of levels (subdivisions of array) can be calculated as– (logN + 1)

For example log of 8 base 2 is 3, so log8 + 1 = 4

Which is same as the number of halves for the array having 8 elements– 8 4 2 1.

At each level N elements are merged which makes the time complexity of merge sort as N*(logN + 1). we can discard the 1 so the time complexity of merge sort is O(N*logN).

Merge sort is not an in place sort algorithm as extra space is required. Auxiliary space required is equal to the number of elements in the original array so the space complexity of merge sort is O(N).

That's all for this topic Merge Sort Program in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Programs Page


Related Topics

  1. Shell Sort Program in Java
  2. Radix Sort Program in Java
  3. How to Display Pyramid Patterns in Java - Part1
  4. Arrange Non-Negative Integers to Form Largest Number - Java Program
  5. Find All Permutations of a Given String Java Program

You may also like-

  1. Running Dos/Windows Commands From Java Program
  2. Find Duplicate Elements in an Array Java Program
  3. How to Display Time in AM-PM Format in Java
  4. How to Sort an ArrayList in Descending Order in Java
  5. CopyOnWriteArrayList in Java With Examples
  6. Java Reflection API Tutorial
  7. String in Java Tutorial
  8. NameNode, DataNode And Secondary NameNode in HDFS

Friday, January 28, 2022

equals() And hashCode() Methods in Java

In this post we'll see what are equals() and hashCode() methods in Java and why do we need to override these methods.


equals() and hashCode() methods in Java

In Java equals() and hashCode() methods are present in the java.lang.Object class. These two methods are used for making inferences about an object's identity or in simpler language to reach to a decision whether the two compared objects are equal or not.

The default implementation of equals() method in the Object class is a simple reference equality check.

public boolean equals(Object obj){
 return (this == obj);
}

The default implementation of hashCode() in the Object class just returns integer value of the memory address of the object.

Usage of hashCode() and equals() methods in Java

  • hashCode()- This method is used to get a unique integer value for a given object. We can see it's use with hash based collections like HashTable or HashMap where hashCode() is used to find the correct bucket location where the particular (key, value) pair is stored.

    Refer How HashMap internally works in Java to know more about it.

  • equals()- equals() method is used to determine the equality of two objects.

When do we need to override hashCode() and equals() methods

If you are using a custom object as key in a hash based collection it becomes very important to override hashCode() and equals() methods in Java.
Also in case of ORM like Hibernate when a detached instance is re-attached to a session, we need to make sure that the object is same.

In such cases we can't rely on the default implementation provided by the Object class and need to provide custom implementation of hashCode() and equals() method.

But I never had the need to override hashCode() and equals() methods

Most of the time we use HashMap when it comes to hash based collections. With in HashMap, we mostly use primitive wrapper class like Integer or String class, as key. These classes are immutable and provide their own proper implementation of hashCode() and equals() methods, thus these classes, on their own are, good hash keys.

Things become tricky when we are using any custom object as key, in that case it becomes very important to override these 2 methods to make sure that we really get the object we want.

Let's say you are using some custom class object as a key in HashMap and that class did not override equals() and hashCode(), what will happen in that case?

In that case we would not be able to reliably retrieve the associated value using that key, unless we used the exact same class object as key in the get() method as we stored using the put() method.

Now, you may wonder why using the same instance is it possible? Because in the case we are not overriding and providing the implementation of equals() and hashCode(), Object class' equals() and hashCode() methods will be used and recall that in Object class equals() method implementation is simple reference equality check thus having the same instance may satisfy the Object class' equals() method.

Let's see an example where custom object is used but hashCode and equals are not overridden and custom implementation is not given.

class Customer {
  private int customerID;
  private String firstName;
  private String lastName;
  public Customer(int customerID, String firstName, String lastName) {
    super();
    this.customerID = customerID;
    this.firstName = firstName;
    this.lastName = lastName;
  }  
}

public class HashCodeDemo {
  public static void main(String[] args) {
    Map<Customer, String> m = new HashMap<Customer, String>();
    Customer cust = new Customer(1, "Roger", "Cox");
    m.put(cust,"Roger Cox");
    // retrieving using another instance
    System.out.println(m.get(new Customer(1, "Roger", "Cox")));
    // retrieving using same instance
    System.out.println(m.get(cust));               
  }
}

Output

null
Roger Cox

It can be seen how; when the instance is changed the value is not retrieved even when the same values for the customer object are given as key. But when we use the same instance then it is able to fetch. But that's not very convenient and in a big application we are not expected to keep the same instance and use it whenever we want to retrieve the value. So enter the equals() and hashCode() methods.

Same example with hashCode and equals implemented

class Customer {
  private int customerID;
  private String firstName;
  private String lastName;

  public Customer(int customerID, String firstName, String lastName) {
    super();
    this.customerID = customerID;
    this.firstName = firstName;
    this.lastName = lastName;
  }
      
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + customerID;
    result = prime * result
            + ((firstName == null) ? 0 :   firstName.hashCode());
    result = prime * result
            + ((lastName == null) ? 0 : lastName.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;
    Customer other = (Customer) obj;
    if (customerID != other.customerID)
      return false;
    if (firstName == null) {
      if (other.firstName != null)
        return false;
    } else if (!firstName.equals(other.firstName))
        return false;
    if (lastName == null) {
      if (other.lastName != null)
        return false;
    } else if (!lastName.equals(other.lastName))
        return false;
    return true;
  }
}
public class HashCodeDemo {
  public static void main(String[] args) {
    Map<Customer, String> m = new HashMap<Customer, String>();
    Customer cust = new Customer(1, "Roger", "Cox");
    m.put(cust,"Roger Cox");
    // retrieving using another instance
    System.out.println(m.get(new Customer(1, "Roger", "Cox")));
    // retrieving using same instance
    System.out.println(m.get(cust));               
  }
}

Output

Roger Cox
Roger Cox

Now it will use the custom implementation of equals to see if two instances of Customer class are logically equal. So in both cases, using the same instance or different instance, we are able to get the value.

To see a discussion on why 31 is recommended to be used as a multiplier in hashCode refer-
http://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier

And if you are worried that you have to write so much to implement hashCode and equals, don't worry! IDEs provide an option to do that for you. In eclipse IDE that option to generate hashcode and equals method has this path source - Generate hashCode() and equals().

Rules for implementing equals() and hashCode() in Java

There are certain restrictions on the behavior of equals() and hashCode() methods in Java, which can be seen in the Javadocs for Object class- http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals(java.lang.Object)

For equals() method the JavaDocs say-

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

And the general contract of hashCode is-

  • 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.

To explain this contract a little more-

The first point says that the hashCode() method must consistently return the same integer. In case a mutable object is used as key we have to make sure that its state does not change. If a key's hashCode changes while used in a Collection get and put will give some unpredictable results.

According to the second point if 2 objects Obj1 and Obj2 are equal according to their equals() method then they must have the same hash code too. Though the vice-versa is not true that is if 2 objects have the same hash code then they do not have to be equal too.

According to the third point if 2 objects are not equal according to their equals() method they still can have the same hash code. That is known as hash collision.

That's all for this topic equals() And hashCode() Methods in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. How to Loop Through a Map in Java
  2. How ArrayList Works Internally in Java
  3. Difference Between Comparable and Comparator in Java
  4. Difference Between equals() Method And equality Operator == in Java
  5. Java Collections Interview Questions And Answers

You may also like-

  1. Fail-Fast Vs Fail-Safe Iterator in Java
  2. Marker Interface in Java
  3. Why no Multiple Inheritance in Java
  4. Lambda Expressions in Java 8
  5. Transaction Management in Java-JDBC
  6. finally Block in Java Exception Handling
  7. Check Given Strings Anagram or Not Java Program
  8. Dependency Injection in Spring Framework

Thursday, January 27, 2022

Java Stream - Collectors.joining() With Examples

If you want to join stream elements into a String you can do that using joining() method of the java.util.stream.Collectors class. It is a handy utility method to concatenate elements of an array, collection into a String.

Syntax of Collectors.joining() method

There are three overloaded Collectors.joining() method

  • Collector<CharSequence,?,String> joining()- Returns a Collector that concatenates the input elements into a String, in encounter order.
  • Collector<CharSequence,?,String> joining(CharSequence delimiter)- Returns a Collector that concatenates the input elements, separated by the specified delimiter, in encounter order.
  • Collector<CharSequence,?,String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)- Returns a Collector that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in encounter order.

Collectors.joining() Java examples

1. We have an array of Strings and we want to join all the array elements to get a String. That can be quickly done using Collectors.joining() method by using the array as a Stream source and then calling the collect method. We'll also use the other two variants of joining() method in this example.

import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CollectorsJoining {
  public static void main(String[] args) {
    String[] strArr = { "Example", "to", "demonstrate", "Collectors", "joining"};
      String str1 = Stream.of(strArr).collect(Collectors.joining());
      System.out.println("Concatenated String- " + str1);
      
      // Second Variant- Passing Space as delimiter
      String str2 = Stream.of(strArr).collect(Collectors.joining(" "));
      System.out.println("Concatenated String with delimiter- " + str2);
      // Passing "-" as delimiter
      str2 = Stream.of(strArr).collect(Collectors.joining("-"));
      System.out.println("Concatenated String with delimiter- " + str2);
      
      // Third Variant- Passing delimiter, suffix and prefix
      String str3 = Stream.of(strArr).collect(Collectors.joining("|", "[", "]"));
      System.out.println("Concatenated String with delimiter and suffix, prefix- " + str3);
  }
}

Output

Concatenated String- ExampletodemonstrateCollectorsjoining
Concatenated String with delimiter- Example to demonstrate Collectors joining
Concatenated String with delimiter- Example-to-demonstrate-Collectors-joining
Concatenated String with delimiter and suffix, prefix- [Example|to|demonstrate|Collectors|joining]

2. Using Collectors.joining method to concatenate elements of an ArrayList.

public class CollectorsJoining {
  public static void main(String[] args) {
    List<String> alphabetList = Arrays.asList("A", "B", "C", "D","E", "F");
      String str1 = alphabetList.stream().collect(Collectors.joining());
      System.out.println("Concatenated String- " + str1);
      
      // Second Variant- Passing Space as delimiter
      String str2 = alphabetList.stream().collect(Collectors.joining(" "));
      System.out.println("Concatenated String with delimiter- " + str2);
      // Passing "-" as delimiter
      str2 = alphabetList.stream().collect(Collectors.joining("-"));
      System.out.println("Concatenated String with delimiter- " + str2);
      
      // Third Variant- Passing delimiter, suffix and prefix
      String str3 = alphabetList.stream().collect(Collectors.joining("|", "[", "]"));
      System.out.println("Concatenated String with delimiter and suffix, prefix- " + str3);
  }
}

Output

Concatenated String- ABCDEF
Concatenated String with delimiter- A B C D E F
Concatenated String with delimiter- A-B-C-D-E-F
Concatenated String with delimiter and suffix, prefix- [A|B|C|D|E|F]

That's all for this topic Java Stream - Collectors.joining() With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream - Collectors.partitioningBy() With Examples
  2. Java Stream - findAny() With Examples
  3. Java Stream - noneMatch() With Examples
  4. Java Stream API Interview Questions And Answers

You may also like-

  1. Externalizable Interface in Java
  2. Lock Striping in Java Concurrency
  3. Switch Expressions in Java 12
  4. Java String Interview Questions And Answers
  5. Find Maximum And Minimum Numbers in a Given Matrix Java Program
  6. BeanPostProcessor in Spring Framework
  7. How to Install Node.js and NPM in Windows
  8. How to Write a Map Only Job in Hadoop MapReduce

Wednesday, January 26, 2022

TreeMap in Java With Examples

TreeMap in Java is also one of the implementation of the Map interface like HashMap and LinkedHashMap. How TreeMap differs from these other implementations is that the elements in TreeMap are sorted on keys.

The elements in TreeMap are ordered according to the natural ordering of its keys, which is the default sort ordering or a comparator can be provided at map creation time to provide custom ordering (We'll see an example a little later).


How TreeMap is implemented in Java

TreeMap class in Java implements the NavigableMap interface and extends the AbstractMap class.

TreeMap is a Red-Black tree based NavigableMap implementation. This implementation provides guaranteed log(n) time cost for the containsKey, get, put and remove operations.

Some of the important points about TreeMap in Java which are discussed in this post are as follows-

  1. TreeMap in Java is a Red-Black tree based NavigableMap implementation.
  2. TreeMap stores its elements in sorted order and sorting is done on keys. By default elements are sorted using their natural ordering.
  3. If you want to sort elments in TreeMap in any other order then you will have to provide a Comparator.
  4. TreeMap doesn’t allow null as key though other Map implementation like HashMap and LinkedHashMap do allow one key as null.
  5. Key in the TreeMap should be unique otherwise the previous stored value for the same key will be overwritten. Duplicate values are allowed though.
  6. TreeMap in Java is not synchronized so it is not thread safe. If TreeMap is accessed concurrently by multiple threads and at least one of the threads modifies the map structurally, then the TreeMap must be synchronized externally.
  7. The iterators returned by the iterator method of the collections returned by all of the TreeMap's "collection view methods" are fail-fast. If the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.

Java TreeMap Constructors

There are four constructors in the TreeMap class in Java.

  • TreeMap()- Constructs a new, empty tree map, using the natural ordering of its keys.
  • TreeMap(Comparator<? super K> comparator)- Constructs a new, empty tree map, ordered according to the given comparator.
  • TreeMap(Map<? extends K,? extends V> m)- Constructs a new tree map containing the same mappings as the given map, ordered according to the natural ordering of its keys.
  • TreeMap(SortedMap<K,? extends V> m)- Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map.

Java TreeMap creation and element insertion example

public class TreeMapDemo {
  public static void main(String[] args) {
    Map<String, String> cityTemperatureMap = new TreeMap<String, String>();
    // Storing elements
    cityTemperatureMap.put("Delhi", "24");
    cityTemperatureMap.put("Mumbai", "32");
    cityTemperatureMap.put("Chennai", "35");
    cityTemperatureMap.put("Bangalore", "22" );
    cityTemperatureMap.put("Kolkata", "28");
    cityTemperatureMap.put("Chennai", "36");

    // iterating the map
    for(Map.Entry<String, String> me : cityTemperatureMap.entrySet()){
      System.out.println(me.getKey() + " " + me.getValue());
    }
  }
}

Output

Bangalore 22
Chennai 36
Delhi 24
Kolkata 28
Mumbai 32

It can be seen that the elements in TreeMap are sorted according to the natural ordering of its keys which is ascending for String.
Also note that though Chennai is added twice but it is stored only once as trying to store the same key twice will result in overwriting of the old value with the new value (as the calculated hash will be the same for the keys). Thus the last one is displayed while iterating the values.

TreeMap doesn't allow null

Though HashMap and LinkedHashMap allow one null as key, TreeMap in Java doesn't allow null as key. Any attempt to add null in a TreeMap will result in a NullPointerException.

public class TreeMapDemo {
  public static void main(String[] args) {
    Map<String, String> cityTemperatureMap = new TreeMap<String, String>();
    // Storing elements
    cityTemperatureMap.put("Delhi", "24");
    cityTemperatureMap.put("Mumbai", "32");
    cityTemperatureMap.put("Chennai", "35");
    cityTemperatureMap.put("Bangalore", "22" );
    cityTemperatureMap.put("Kolkata", "28");
    // Null key
    cityTemperatureMap.put(null, "36");
    
    // iterating the map
    for(Map.Entry<String, String> me : cityTemperatureMap.entrySet()){
      System.out.println(me.getKey() + " " + me.getValue());
    }
  }
}

Output

Exception in thread "main" java.lang.NullPointerException
 at java.util.TreeMap.put(Unknown Source)
 at org.netjs.prog.TreeMapDemo.main(TreeMapDemo.java:17)

Sorting elements in different order in TreeMap

As already mentioned by default elements are stored in TreeMap using natural ordering. If you want to sort TreeMap is descending order (reverse order) then you need to provide your own Comparator at map creation time. Let's see an example where we sort the TreeMap of Strings in descending order of its keys.

public class TreeMapDemo {
  public static void main(String[] args) {
    Map<String, String> cityTemperatureMap = new TreeMap<String, String>(new TreeComparator());
    // Storing elements
    
    cityTemperatureMap.put("Delhi", "24");
    cityTemperatureMap.put("Mumbai", "32");
    cityTemperatureMap.put("Chennai", "35");
    cityTemperatureMap.put("Bangalore", "22" );
    cityTemperatureMap.put("Kolkata", "28");
    
    // iterating the map
    for(Map.Entry<String, String> me : cityTemperatureMap.entrySet()){
        System.out.println(me.getKey() + " " + me.getValue());
    }
  }
}

//Comparator class
class TreeComparator implements Comparator<String>{
  @Override
  public int compare(String str1, String str2) {
    return str2.compareTo(str1);
  }    
}

Output

Mumbai 32
Kolkata 28
Delhi 24
Chennai 35
Bangalore 22

Here note that a Custom Comparator is implemented with the logic to sort objects in reverse order. That Comparator is passed in the constructor of the TreeMap at map creation time .

TreeMap is not synchronized

TreeMap in Java is not thread safe. In case we need to Synchronize it, it should be synchronized externally. That can be done using the Collections.synchronizedSortedMap method.

SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));

TreeMap class' iterator is fail-fast

The iterators returned by TreeMap in Java are fail-fast, if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.

TreeMap iteration example

In the example we’ll get the Set view of the mapped entries using the entrySet() method. While iterating that Set we’ll try to remove an element from the TreeMap using the Map's remove() method (not the iterator's remove method) which means a structural modification and results in ConcurrentModificationException being thrown.

public class TreeMapItr {
  public static void main(String[] args) {
    Map<String, String> langMap = new TreeMap<String, String>();
    // Storing (key, value) pair to HashMap
    langMap.put("ENG", "English");
    langMap.put("NLD", "Dutch");
    langMap.put("ZHO", "Chinese");
    langMap.put("BEN", "Bengali");
    langMap.put("ZUL", "Zulu");
    langMap.put("FRE", "French");
    // Collection view of the TreeMap
    Set<Map.Entry<String, String>> langSet = langMap.entrySet();
    Iterator<Map.Entry<String, String>> itr = langSet.iterator();
    while (itr.hasNext()) {
      Map.Entry<String, String> entry = itr.next();
      System.out.println("Key is " + entry.getKey() + " Value is " + entry.getValue());    
      // removing value using TreeMap's remove method
      if(entry.getKey().equals("NLD")){
        langMap.remove(entry.getKey());
      }
    }
  }
}

Output

Key is BEN Value is Bengali
Key is ENG Value is English
Key is FRE Value is French
Key is NLD Value is Dutch
Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1207)
 at java.util.TreeMap$EntryIterator.next(TreeMap.java:1243)
 at java.util.TreeMap$EntryIterator.next(TreeMap.java:1238)
 at org.netjs.examples.impl.TreeMapItr.main(TreeMapItr.java:23)

If iterator's remove method is used to remove an element from TreeMap while it is iterated then the ConcurrentModificationException is not thrown.

public class TreeMapItr {
  public static void main(String[] args) {
    Map<String, String> langMap = new TreeMap<String, String>();
    // Storing (key, value) pair to HashMap
    langMap.put("ENG", "English");
    langMap.put("NLD", "Dutch");
    langMap.put("ZHO", "Chinese");
    langMap.put("BEN", "Bengali");
    langMap.put("ZUL", "Zulu");
    langMap.put("FRE", "French");
    // Collection view of the TreeMap
    Set<Map.Entry<String, String>> langSet = langMap.entrySet();
    Iterator<Map.Entry<String, String>> itr = langSet.iterator();
    while (itr.hasNext()) {
      Map.Entry<String, String> entry = itr.next();
      // removing value using iterator's remove method
      if(entry.getKey().equals("NLD")){
        itr.remove();
      }
    }
    for(Map.Entry<String, String> lang : langMap.entrySet()) {
      System.out.println("Key- " + lang.getKey() + 
                  " Value- " + lang.getValue());
    }
  }
}

Output

Key- BEN Value- Bengali
Key- ENG Value- English
Key- FRE Value- French
Key- ZHO Value- Chinese
Key- ZUL Value- Zulu

As you can see element from TreeMap is removed when iterator's remove() metod is used and ConcurrentModificationException is not thrown.

Reference: https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/TreeMap.html

That's all for this topic TreeMap in Java With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. How HashMap Works Internally in Java
  2. How to Sort Elements in Different Order in TreeSet
  3. How to Loop Through a Map in Java
  4. HashMap Vs LinkedHashMap Vs TreeMap in Java
  5. Java Collections Interview Questions And Answers

You may also like-

  1. Abstraction in Java
  2. Type Casting in Java With Conversion Examples
  3. EnumSet in Java
  4. How to Remove Duplicate Elements From an ArrayList in Java
  5. Race Condition in Java Multi-Threading
  6. Java ThreadLocal Class With Examples
  7. Functional Interface Annotation in Java
  8. Spring Setter Based Dependency Injection

Tuesday, January 25, 2022

Java Stream - Collectors.partitioningBy() With Examples

If you want to partition stream elements into two groups as per the given condition you can do that using partitioningBy() method of the java.util.stream.Collectors class.

Collectors.partitioningBy() method in Java

Collectors.partitioningBy() method partitions the input elements according to the passed Predicate argument and organizes them into a Map<Boolean, List<T>>. As you can notice the key in the Map is of type Boolean because the two keys used are “false” and “true” to map the input elements that doesn't pass the condition or pass it respectively.

There are two overloaded partitioningBy() method with Syntax as given below-

  • Collector<T,?,Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)- Partitions the stream elements according to the passed Predicate.
  • Collector<T,?,Map<Boolean,D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)- In this variant there is a Second argument of type Collector which reduces the values in each partition and organizes them into a Map<Boolean, D> whose values are the result of the downstream reduction.

Collectors.partitioningBy() Java examples

1. In this Collectors.partitioningBy() example we'll use it to partition a list of integers to return a map of even and odd numbers.

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectorsPartitioning {

  public static void main(String[] args) {
    List<Integer> listOfNumbers = Arrays.asList(5,9,14,19,47,56,72,89,90,18);
      Map<Boolean, List<Integer>> numbers = listOfNumbers.stream()
                                 .collect(Collectors.partitioningBy(n -> n%2 == 0));
      // When key false - returns list with odd numbers
      System.out.println("Odd Numbers- " + numbers.get(false));
      // When key true - returns list with even numbers
      System.out.println("Even Numbers- " + numbers.get(true));
  }
}

Output

Odd Numbers- [5, 9, 19, 47, 89]
Even Numbers- [14, 56, 72, 90, 18]

2. Partition a List of Employee into those who earn more than 8000 and those who don’t using Collectors.partitioningBy() method.

Employee Class

public class Employee {
  private String empId;
  private int age;
  private String name;
  private char gender;
  private int salary;
  Employee(String empId, int age, String name, char gender, int salary){
    this.empId = empId;
    this.age = age;
    this.name = name;
    this.gender = gender;
    this.salary = salary;
  }
  public String getEmpId() {
    return empId;
  }

  public int getAge() {
    return age;
  }

  public String getName() {
    return name;
  }

  public char getGender() {
    return gender;
  }

  public int getSalary() {
    return salary;
  }
  @Override
  public String toString() {
      return "Emp Id: " +  getEmpId() + " Name: " + getName() + " Age: " + getAge();
  }
}
public class CollectorsPartitioning {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 8000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 6000), 
        new Employee("E006", 28, "Amy", 'F', 14000));
      Map<Boolean, List<Employee>> numbers = empList.stream()
                                 .collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
      // When key false - employee with salary <= 8000
      System.out.println("Employees with Salary less than 8000- " + numbers.get(false));
      // When key true - employee with salary > 8000
      System.out.println("Employees with Salary greater than 8000- " + numbers.get(true));
  }
}

Output

Employees with Salary less than 8000- [Emp Id: E001 Name: Ram Age: 40, Emp Id: E002 Name: Shelly Age: 35, Emp Id: E005 Name: Anuj Age: 32]
Employees with Salary greater than 8000- [Emp Id: E003 Name: Mark Age: 24, Emp Id: E004 Name: Ritu Age: 37, Emp Id: E006 Name: Amy Age: 28]

3. In the second example we got the Employees partitioned into groups as per the condition salary greater than 8000 or not. Suppose you want the count of employees in each partitioned group, in that scenario you can use the Collectors.partitioningBy() method with two arguments and pass the second argument which is a counting Collector in this case.

public class CollectorsPartitioning {

  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 8000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Ritu", 'F', 11000),
                new Employee("E005", 32, "Anuj", 'M', 6000), 
        new Employee("E006", 28, "Amy", 'F', 14000));
      Map<Boolean, Long> numbers = empList.stream()
                                 .collect(Collectors.partitioningBy(e -> e.getSalary() > 8000, Collectors.counting()));
      // When key false - employee with salary <= 8000
      System.out.println("Count of employees with Salary less than 8000- " + numbers.get(false));
      // When key true - employee with salary > 8000
      System.out.println("Count of employees with Salary greater than 8000- " + numbers.get(true));
  }
}

Output

Count of employees with Salary less than 8000- 3
Count of employees with Salary greater than 8000- 3

That's all for this topic Java Stream - Collectors.partitioningBy() With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream - Collectors.groupingBy() With Examples
  2. Java Stream - Collectors.joining() With Examples
  3. Java Stream - noneMatch() With Examples
  4. Java Stream - skip() With Examples
  5. Java Stream - sorted() With Examples

You may also like-

  1. Transient Keyword in Java With Examples
  2. Executor And ExecutorService in Java With Examples
  3. Java Nested Class And Inner Class
  4. Java Exception Handling Interview Questions And Answers
  5. Deque Implementation in Java Using Doubly Linked List
  6. Lazy Initialization in Spring Using lazy-init And @Lazy Annotation
  7. Angular Two-Way Data Binding With Examples
  8. Using Combiner in Hadoop MapReduce to Improve Performance