Showing posts with label Java 8. Show all posts
Showing posts with label Java 8. Show all posts

Java 8 - Stream and Filter

 
Stream API:
A stream gives a set of elements of specific type in a sequential order and it gets/computes elements on demand. But never stores the elements one the stream is been processed. Same thing we see in the below example.

Filter:
Applying filter on stream while computing and geting only the elements on specified conditions satisfied.

Java 8 - Stream and Filter

Before Java 8 if we need to filter some values on List, then only the option is to iterate on each element by External Iterator (for each loop). But in Java 8 we can use filter on top of stream to get those filtered elements from the given List. Lets see simple example as how to stream and then using filter on it to collect the final elements which we need from the list.


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

public class StreamFilter {

 public static void main(String[] args) {
  
  Stream<String> language = Stream.of("java", ".net", "python", "nodejs","angular");
  
  //List will contain all the elements from stream
  List<String> listAllLanguages = language.collect(Collectors.toList());
  System.out.println("**** All Elements ****");
  listAllLanguages.forEach(System.out::println);
  



  /* Here we can see stream getting empty and throwing IllegalStateException
  * if we didn't recreate stream again. Since all the elements are streamed already
  * and stream will be empty as of now. 
  */ 
  language = Stream.of("java", ".net", "python", "nodejs","angular");
  // Using filter as all elements except ".net"
  List<String> listFilterLanguage = language.filter(lang -> !".net".equals(lang)).collect(Collectors.toList());
  System.out.println("\n**** Using filter as all elements except \".net\" ****");
  listFilterLanguage.forEach(System.out::println);
 }
}


OUTPUT:


**** All Elements ****
java
.net
python
nodejs
angular

**** Using filter as all elements except ".net" ****
java
python
nodejs
angular

Java 8 - ForEach loop

Introduced in Java 8, the forEach loop provides programmers a new concise and interesting way for iterating over a list of elements. We will see how to use forEach with collections, what kind of argument it takes and how this loop differs from the enhanced for-loop. In java 8 with forEach, we can iterate over a collection and perform a given action on each element – by just passing a class that implements the Consumer interface

Java 8 ForEach


Consumer Interface

Consumer interface is a functional interface (an interface with a single abstract method). It accepts an input and returns no result

Both forEach and For Loop provide the same functionality. Looping through elements in a collection and only the main difference between the two of them is that they are different iterators – the enhanced for-loop is an external iterator whereas the new forEach method is an internal one.

Internal Iterator - iterator manage the iteration in the background and leaves the programmer to just code what is meant to be done with the elements of the collection, rather than managing the iteration and making sure that all the elements are processed one-by-one.

External Iterator - If need more control on the iterator and need to preform some checks and operation on each element then External Iterator can be used.

Lambda Expression - lambda expression is an anonymous representation of a function descriptor of a functional interface.

Method Reference - where a method already exists to perform an operation on the class, this syntax can be used instead of the normal lambda expression

All these types are listed in the below sample program and we can get to know about each of these implementation in Java 8



import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Java8For {

 public static void main(String[] args) {
  
  Stream<String> language = Stream.of("java", ".net", "python", "nodejs","angular");
  List<String> listLanguages = language.collect(Collectors.toList());

  
  // Consumer Interface
  System.out.println("\n**** Consumer Interface ****");
        Consumer<String> languages = lang -> {
            System.out.println(lang);
        };
        listLanguages.forEach(languages);
        
        
        
        // Internal iterator & Lambda Expression
  System.out.println("\n**** Internal iterator & Lambda Expression ****");
  listLanguages.forEach(results -> System.out.println(results));

        
        
        //External Iterator 
  System.out.println("\n**** External Iterator  ****");
        for (String string : listLanguages) {
   System.out.println(string);
  }
        
        
        
        //Method Reference
  System.out.println("\n**** Method Reference ****");
  listLanguages.forEach(System.out::println);
 }
}


OUTPUT:


**** Consumer Interface ****
java
.net
python
nodejs
angular

**** Internal iterator & Lambda Expression ****
java
.net
python
nodejs
angular

**** External Iterator  ****
java
.net
python
nodejs
angular

**** Method Reference ****
java
.net
python
nodejs
angular

Java 8 - StringJoiner with examples

StringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix. Internally StringJoiner uses the String.join()

Java 8 - StringJoiner


Lets see simple examples with various uses of StringJoiner class.
  • Joning string by delimiter
  • Other way to join string with delimiter
  • List to string
  • Adding delimiter, prefix and suffix
  • Using setEmptyValue() method
  • Using setEmptyValue() method - Same after adding some value
  • Merging 2 string joiner 

public class StringJoiners {

 public static void main(String[] args) {
  
  //Joning string by delimiter
  StringJoiner sj = new StringJoiner(",");
  sj.add("Java");
  sj.add("Discover");
  sj.add("2018");
  System.out.println("Joning string by delimiter :: "+ sj.toString());
  
  
  
  //Other way to join string with delimiter
  String str = String.join(",", "JAVA", "DISCOVER", "2018");
  System.out.println("\nOther way : "+str);
  
  
  //List to string
  List<String> list = Arrays.asList("Java", "String", "Joiner");
  String str1 = String.join(", ", list);
  System.out.println("\nList to string : "+str1);
  
  
  //Adding delimiter, prefix and suffix
  StringJoiner sj1 = new StringJoiner(".","Hello "," :)");
  sj1.add("Java");
  sj1.add("Discover");
  sj1.add("2018");
  System.out.println("\nAdding delimiter, prefix and suffix :: "+ sj1.toString());
  
  
  
  /*
   * Using setEmptyValue() method - set default value of the string
   * if string is empty
   */
  StringJoiner sj2 = new StringJoiner("*");   
  sj2.setEmptyValue("DEFUALT EMPTY STRING");
  System.out.println("\nUsing setEmptyValue() method-1 :: "+ sj2.toString() + " ---- LENGTH : "+sj2.toString().length());
  

  // Same after adding some value
  sj2.add("ORACLE");
  sj2.add("LAMBDA");
  System.out.println("\nUsing setEmptyValue() method-2 :: "+ sj2.toString() + " ---- LENGTH : "+sj2.toString().length());
  
  
  
  //Merging 2 string joiner 
  StringJoiner sj3 = sj1.merge(sj2);
  System.out.println("\nMerging 2 string joiner :: "+sj3.toString());
 }
}

OUTPUT:


Joning string by delimiter :: Java,Discover,2018

Other way : JAVA,DISCOVER,2018

List to string : Java, String, Joiner

Adding delimiter, prefix and suffix :: Hello Java.Discover.2018 :)

Using setEmptyValue() method-1 :: DEFUALT EMPTY STRING ---- LENGTH : 20

Using setEmptyValue() method-2 :: ORACLE*LAMBDA ---- LENGTH : 13

Merging 2 string joiner :: Hello Java.Discover.2018.ORACLE*LAMBDA :)

Java 8 - StringJoiner

In Java-8 we got new class called StringJoiner to join multiple strings. Nothing new in StringJoiner class when we compare with already existing classes like StringBuffer and StringBuilder. Only the difference here we can set prefix and suffix along with delimiter, or we can set only delimiter by using different constructor. Lets see simple example as how to use StringJoiner from Java-8.
Java 8 - StringJoiner

public class StringJoinerExample {

 public static void main(String[] args) {
  
  StringJoiner sJoin = new StringJoiner(",");
  
  sJoin.add("java");
  sJoin.add("python");
  sJoin.add("C#");
  sJoin.add("scala");
    
  System.out.println(sJoin);
 }
}

OUTPUT:

java,python,C#,scala




public class StringJoinerExample {

 public static void main(String[] args) {
  
  StringJoiner sJoin = new StringJoiner("," ,"{" ,"}");
  
  sJoin.add("java");
  sJoin.add("python");
  sJoin.add("C#");
  sJoin.add("scala");
    
  System.out.println(sJoin);  
 }
}

OUTPUT:
{java,python,C#,scala}

Default Method in Interface - Java 8

 
Java 8 comes lots of new features and in one important feature is Default method in Interface. Till Java 7 we can have only abstract methods in interface and we can't have any method definition in interfaces. In Java 8 we can have default method nothing but method with definition, following example will show you how to write those methods.

Next we can ask, why we need Default method in interface?

Lets assume we need to introduce new method in interface and then it breaks all classes implementing those interfaces by compile time error. For example if interface used in hundreds of classes which can break millions of line code by asking to implement new method introduced in Interface.
Even in JDK we can see foreach default method introduced to entire collection classes by giving definition in Iterable interface. Below is the Oracle API docs snapshot for Iterable interface.

Interface Default method in Java 8

Suppose if we don't have default method feature then JDK need to come-up with millions of code change in all collection classes by giving those method implementation.

Next we may ask whats the difference between Java 8 interface and Abstract method?

Interfaces cannot have state associated with them but Abstract classes can have state associated with them.

Next how to solve Multiple Inheritance Ambiguity Problems with default methods?

Since we can implement multiple interface, also we can have same default method (with same name) in 2 different interface. When we implement those interfaces in concrete class compile will give a warning that

"Duplicate default methods named <method_name> with the parameters () and () are inherited from the types <Interface1> and <Interface2>"

To solve this we can Override default method from any 1 interface. Below example will give detail about how to use default method in interface and to solve Ambiguity Problems with 2 interface have same default method.

FirstInterface.java

public interface FirstInterface {

 public void method();
 
 default public String methodName(){
  return "FirstInterface";
 }
}


SecondInterface.java

public interface SecondInterface {
 
 public void method();
 
 public default String methodName(){
  return "SecondInterface";
 }
}


TestClass.java

public class TestClass implements FirstInterface, SecondInterface {

 @Override
 public void method() {
  System.out.println("Hello Java Discover :) ");
 }
 
 /*
  * To solve Ambiguity Problems we can override 
  * default method as below.
  */
 @Override
 public String methodName(){
  return FirstInterface.super.methodName();
 }

 
 public static void main(String[] args) {
  new TestClass().testInterface();
 }
 
 private void testInterface(){
  method();
  System.out.println(FirstInterface.super.methodName());
  System.out.println(SecondInterface.super.methodName());
 }
 
}


OUTPUT:

Hello Java Discover :) 
FirstInterface
SecondInterface

If we see above example 2 interfaces having default method with same name. When we implement those interfaces in our concrete class (TestClass.java) we will get compile time error as

"Duplicate default methods named methodName with the parameters () and () are inherited from the types SecondInterface and FirstInterface"

To overcome this we need to override default method in our class as above. Next we can call default methods by using Interface names as we called in testInterface() method.
  • FirstInterface.super.methodName()
  • SecondInterface.super.methodName()