diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a3417b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/out/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..188022c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..ea08649 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CalculatorImpl.java b/CalculatorImpl.java deleted file mode 100644 index 74ee688..0000000 --- a/CalculatorImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.javatechie.lambda.demo; - -interface Calculator { - - // void switchOn(); - - /* void sum(int input); */ - - int substract(int i1, int i2); -} - -public class CalculatorImpl { - - public static void main(String[] args) { - - /* - * Calculator calculator = () -> System.out.println("Switch On"); - * - * calculator.switchOn(); - */ - - /* - * Calculator calculator = (input) -> System.out.println("Sum : " + input); - * calculator.sum(394); - */ - - Calculator calculator = (i1, i2) -> { - if (i2 < i1) { - throw new RuntimeException("message"); - } else { - return i2 - i1; - } - }; - - System.out.println(calculator.substract(8, 20)); - } - - // () -> {body}; -} diff --git a/EvenAndOddPrinter.java b/EvenAndOddPrinter.java deleted file mode 100644 index 6f10674..0000000 --- a/EvenAndOddPrinter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.javatechie; - -import java.util.concurrent.CompletableFuture; -import java.util.function.IntPredicate; -import java.util.stream.IntStream; - -public class EvenAndOddPrinter { - - private static Object object = new Object(); - - private static IntPredicate evenCondition = e -> e % 2 == 0; - private static IntPredicate oddCondition = e -> e % 2 != 0; - - - public static void main(String[] args) throws InterruptedException { - CompletableFuture.runAsync(() -> EvenAndOddPrinter.printResults(oddCondition)); - CompletableFuture.runAsync(() -> EvenAndOddPrinter.printResults(evenCondition)); - Thread.sleep(1000); - } - - public static void printResults(IntPredicate condition) { - IntStream.rangeClosed(1, 10) - .filter(condition) - .forEach(EvenAndOddPrinter::execute); - } - - - public static void execute(int i) { - synchronized (object) { - try { - System.out.println("Thread Name " + Thread.currentThread().getName() + " : " + i); - object.notify(); - object.wait(); - } catch (InterruptedException ex) { - //error log - } - } - } -} diff --git a/EvenAndOddPrinterBy2Threads.java b/EvenAndOddPrinterBy2Threads.java deleted file mode 100644 index b7ed6bd..0000000 --- a/EvenAndOddPrinterBy2Threads.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.javatechie; - -public class EvenAndOddPrinterBy2Threads implements Runnable { - - static int count = 1; - Object object; - - public EvenAndOddPrinterBy2Threads(Object object) { - this.object = object; - } - - @Override - public void run() { - - while (count <= 100) { - if (count % 2 == 0 && Thread.currentThread().getName().equals("even")) { - synchronized (object) { - System.out.println("Thread Name : " + Thread.currentThread().getName() + " value :" + count); - count++; - try { - object.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - if (count % 2 != 0 && Thread.currentThread().getName().equals("odd")) { - synchronized (object) { - System.out.println("Thread Name : " + Thread.currentThread().getName() + " value :" + count); - count++; - object.notify(); - } - } - - } - - } - - public static void main(String[] args) { - Object lock=new Object(); - Runnable r1=new EvenAndOddPrinterBy2Threads(lock); - Runnable r2=new EvenAndOddPrinterBy2Threads(lock); - new Thread(r1, "even").start(); - new Thread(r2, "odd").start(); - } -} diff --git a/EvenAndOddPrinterByES.java b/EvenAndOddPrinterByES.java deleted file mode 100644 index 54d351f..0000000 --- a/EvenAndOddPrinterByES.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.javatechie; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.IntStream; - -public class EvenAndOddPrinterByES { - - public static void main(String[] args) { - - ExecutorService executorService = Executors.newFixedThreadPool(2); - - IntStream.rangeClosed(1, 10) - .forEach(num -> { - CompletableFuture oddCompletableFuture = CompletableFuture.completedFuture(num) - .thenApplyAsync(x -> { - if (x % 2 != 0) { - System.out.println("Thread Name " + Thread.currentThread().getName() + " : " + x); - } - return num; - }, executorService); - oddCompletableFuture.join(); - - CompletableFuture evenCompletableFuture = CompletableFuture.completedFuture(num) - .thenApplyAsync(x -> { - if (x % 2 == 0) { - System.out.println("Thread Name " + Thread.currentThread().getName() + " : " + x); - } - return num; - }, executorService); - evenCompletableFuture.join(); - }); - executorService.shutdown(); - } -} diff --git a/MyFunctionalInterface.java b/MyFunctionalInterface.java deleted file mode 100644 index 65920e5..0000000 --- a/MyFunctionalInterface.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.javatechie.lambda.demo; - -public interface MyFunctionalInterface { - - void m1(); - - default void m2() { - System.out.println("Default method-1"); - } - - default void m3() { - System.out.println("Default method-2"); - } - - static void m4() { - System.out.println("static method-1"); - } -} diff --git a/NthHighestSalaryDemo.java b/NthHighestSalaryDemo.java deleted file mode 100644 index 63e7c26..0000000 --- a/NthHighestSalaryDemo.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.javatechie; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -public class NthHighestSalaryDemo { - - public static void main(String[] args) { - - Map map1 = new HashMap<>(); - map1.put("anil", 1000); - map1.put("bhavna", 1300); - map1.put("micael", 1500); - map1.put("tom", 1600);//output - map1.put("ankit", 1200); - map1.put("daniel", 1700); - map1.put("james", 1400); - - Map.Entry results = getNthHighestSalary(4, map1); - System.out.println(results); - - Map map2 = new HashMap<>(); - map2.put("anil", 1000); - map2.put("ankit", 1200); - map2.put("bhavna", 1200); - map2.put("james", 1200); - map2.put("micael", 1000); - map2.put("tom", 1300); - map2.put("daniel", 1300); - - //System.out.println(getNthHighestSalary(2, map2)); - - - System.out.println(getDynamicNthHighestSalary(3, map1)); - - } - - public static Map.Entry getNthHighestSalary(int num, Map map) { - return map.entrySet().stream() - .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) - .collect(Collectors.toList()) - .get(num - 1); - } - - public static Map.Entry> getDynamicNthHighestSalary(int num, Map map) { - return map.entrySet() - .stream() - .collect(Collectors.groupingBy(Map.Entry::getValue, - Collectors.mapping(Map.Entry::getKey, Collectors.toList()) - )) - .entrySet() - .stream() - .sorted(Collections.reverseOrder(Map.Entry.comparingByKey())) - .collect(Collectors.toList()) - .get(num - 1); - } -} diff --git a/Optional/Customer.java b/Optional/Customer.java deleted file mode 100644 index e38c673..0000000 --- a/Optional/Customer.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.javatechie; - -import java.util.List; -import java.util.Optional; - -public class Customer { - - private int id; - private String name; - private String email; - private List phoneNumbers; - - public Customer() { - } - - public Customer(int id, String name, String email, List phoneNumbers) { - this.id = id; - this.name = name; - this.email = email; - this.phoneNumbers = phoneNumbers; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setEmail(String email) { - this.email = email; - } - - public List getPhoneNumbers() { - return phoneNumbers; - } - - public void setPhoneNumbers(List phoneNumbers) { - this.phoneNumbers = phoneNumbers; - } -} diff --git a/Optional/EkartDataBase.java b/Optional/EkartDataBase.java deleted file mode 100644 index eb7f756..0000000 --- a/Optional/EkartDataBase.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.javatechie; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class EkartDataBase { - - - public static List getAll() { - return Stream.of( - new Customer(101, "john", "john@gmail.com", Arrays.asList("397937955", "21654725")), - new Customer(102, "smith", "smith@gmail.com", Arrays.asList("89563865", "2487238947")), - new Customer(103, "peter", "peter@gmail.com", Arrays.asList("38946328654", "3286487236")), - new Customer(104, "kely", "kely@gmail.com", Arrays.asList("389246829364", "948609467")) - ).collect(Collectors.toList()); - } - - -} diff --git a/Optional/OptionalDemo.java b/Optional/OptionalDemo.java deleted file mode 100644 index ccabd0e..0000000 --- a/Optional/OptionalDemo.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.javatechie; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -public class OptionalDemo { - - - - public static Customer getCustomerByEmailId(String email) throws Exception { - List customers = EkartDataBase.getAll(); - return customers.stream() - .filter(customer -> customer.getEmail().equals(email)) - .findAny().orElseThrow(()->new Exception("no customer present with this email id")); - - } - - public static void main(String[] args) throws Exception { - - Customer customer=new Customer(101, "john", "test@gmail.com", Arrays.asList("397937955", "21654725")); - - //empty - //of - //ofNullable - - Optional emptyOptional = Optional.empty(); - System.out.println(emptyOptional); - - //Optional emailOptional = Optional.of(customer.getEmail()); - //System.out.println(emailOptional); - - Optional emailOptional2 = Optional.ofNullable(customer.getEmail()); - /* if(emailOptional2.isPresent()){ - System.out.println(emailOptional2.get()); - }*/ - // System.out.println(emailOptional2.orElse("default@email.com")); - - // System.out.println(emailOptional2.orElseThrow(()->new IllegalArgumentException("email not present"))); - - - System.out.println(emailOptional2.map(String::toUpperCase).orElseGet(()->"default email...")); - - getCustomerByEmailId("pqr"); - } -} diff --git a/demo/ConsumerDemo.java b/demo/ConsumerDemo.java deleted file mode 100644 index cf184cb..0000000 --- a/demo/ConsumerDemo.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.javatechie.pre.functional.demo; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -public class ConsumerDemo { - - public static void main(String[] args) { - /* - * Consumer consumer = t -> System.out.println("Printing : " + t); - * - * consumer.accept(10); - */ - - List list1 = Arrays.asList(1, 2, 3, 4, 5); - - list1.stream().forEach(t -> System.out.println("print : " + t)); - - } -} diff --git a/demo/ForEachDemo.java b/demo/ForEachDemo.java deleted file mode 100644 index 98ba59c..0000000 --- a/demo/ForEachDemo.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.javatechie.stream.demo; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -public class ForEachDemo { - - // filter----> conditional check - - public static void main(String[] args) { - - List list = new ArrayList<>(); - list.add("Murrit"); - list.add("john"); - list.add("piter"); - list.add("marek"); - list.add("mac"); - - for (String s : list) { - if (s.startsWith("m")) { - System.out.println(s); - } - } - - list.stream().filter(t -> t.startsWith("m")).forEach(t -> System.out.println(t)); - - Map map = new HashMap<>(); - - map.put(1, "a"); - map.put(2, "b"); - map.put(3, "c"); - map.put(4, "d"); - - /* - * map.forEach((key,value)->System.out.println(key+": "+value));*/ - - map.entrySet().stream().filter(k->k.getKey()%2==0).forEach(obj->System.out.println(obj)); - - - /* - * Consumer consumer=(t)->System.out.println(t); for(String s1:list) { - * consumer.accept(s1); } - */ - - } -} diff --git a/demo/PredicateDemo.java b/demo/PredicateDemo.java deleted file mode 100644 index d300ebe..0000000 --- a/demo/PredicateDemo.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.javatechie.pre.functional.demo; - -import java.util.Arrays; -import java.util.List; - -public class PredicateDemo { - - public static void main(String[] args) { - - List list1 = Arrays.asList(1, 2, 3, 4, 5); - - list1.stream().filter(t -> t % 2 == 0).forEach(t -> System.out.println("print Even: " + t)); - } -} diff --git a/demo/SupplierDemo.java b/demo/SupplierDemo.java deleted file mode 100644 index e936203..0000000 --- a/demo/SupplierDemo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.javatechie.pre.functional.demo; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Supplier; - -public class SupplierDemo { - - public static void main(String[] args) { - - - List list1 = Arrays.asList(); - - System.out.println(list1.stream().findAny().orElseGet(() -> "Hi viewers")); - } -} diff --git a/example/Book.java b/example/Book.java deleted file mode 100644 index 1f95d9f..0000000 --- a/example/Book.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.javatechie.lambda.example; - -public class Book { - private int id; - private String name; - private int pages; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getPages() { - return pages; - } - - public void setPages(int pages) { - this.pages = pages; - } - - @Override - public String toString() { - return "Book [id=" + id + ", name=" + name + ", pages=" + pages + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + pages; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Book other = (Book) obj; - if (id != other.id) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (pages != other.pages) - return false; - return true; - } - - public Book() { - super(); - // TODO Auto-generated constructor stub - } - - public Book(int id, String name, int pages) { - super(); - this.id = id; - this.name = name; - this.pages = pages; - } - -} diff --git a/example/BookDAO.java b/example/BookDAO.java deleted file mode 100644 index 00f7528..0000000 --- a/example/BookDAO.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.javatechie.lambda.example; - -import java.util.ArrayList; -import java.util.List; - -public class BookDAO { - - public List getBooks() { - List books = new ArrayList<>(); - books.add(new Book(101, "Core Java", 400)); - books.add(new Book(363, "Hibernate", 180)); - books.add(new Book(275, "Spring", 200)); - books.add(new Book(893, "WebService", 300)); - return books; - } -} diff --git a/example/BookService.java b/example/BookService.java deleted file mode 100644 index 9ec247f..0000000 --- a/example/BookService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.javatechie.lambda.example; - -import java.util.Collections; -import java.util.List; - -public class BookService { - - /* - * ( o1, o2) -> o2.getName().compareTo(o1.getName()); - * - */ - public List getBooksinSort() { - List books = new BookDAO().getBooks(); - Collections.sort(books, (o1, o2) -> o1.getName().compareTo(o2.getName())); - return books; - } - - public static void main(String[] args) { - System.out.println(new BookService().getBooksinSort()); - } -} - -/* - * class MyComparator implements Comparator { - * - * @Override public int compare(Book o1, Book o2) { return - * o2.getName().compareTo(o1.getName()); } - */ diff --git a/example/DataBase.java b/example/DataBase.java deleted file mode 100644 index 5724360..0000000 --- a/example/DataBase.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.javatechie.stream.api.example; - -import java.util.ArrayList; -import java.util.List; -//DAO layer -public class DataBase { - - public static List getEmployees() { - List list = new ArrayList<>(); - list.add(new Employee(176, "Roshan", "IT", 600000)); - list.add(new Employee(388, "Bikash", "CIVIL", 900000)); - list.add(new Employee(470, "Bimal", "DEFENCE", 500000)); - list.add(new Employee(624, "Sourav", "CORE", 400000)); - list.add(new Employee(176, "Prakash", "SOCIAL", 1200000)); - return list; - } - -} diff --git a/example/Employee.java b/example/Employee.java deleted file mode 100644 index 7e02bcc..0000000 --- a/example/Employee.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.javatechie.stream.api.example; - -public class Employee { - - private int id; - private String name; - private String dept; - private long salary; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDept() { - return dept; - } - - public void setDept(String dept) { - this.dept = dept; - } - - public long getSalary() { - return salary; - } - - public void setSalary(long salary) { - this.salary = salary; - } - - public Employee(int id, String name, String dept, long salary) { - super(); - this.id = id; - this.name = name; - this.dept = dept; - this.salary = salary; - } - - public Employee() { - super(); - // TODO Auto-generated constructor stub - } - - @Override - public String toString() { - return "Employee [id=" + id + ", name=" + name + ", dept=" + dept + ", salary=" + salary + "]"; - } - -} diff --git a/example/TaxService.java b/example/TaxService.java deleted file mode 100644 index 82492ca..0000000 --- a/example/TaxService.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.javatechie.stream.api.example; - -import java.util.List; -import java.util.stream.Collectors; - -public class TaxService { - - public static List evaluateTaxUsers(String input) { - return (input.equalsIgnoreCase("tax")) - ? DataBase.getEmployees().stream().filter(emp -> emp.getSalary() > 500000).collect(Collectors.toList()) - : DataBase.getEmployees().stream().filter(emp -> emp.getSalary() <= 500000) - .collect(Collectors.toList()); - - } - - public static void main(String[] args) { - System.out.println(evaluateTaxUsers("tax")); - } -} diff --git a/exception_handling/CheckedExceptionHandlerConsumer.java b/exception_handling/CheckedExceptionHandlerConsumer.java deleted file mode 100644 index 2ee06b0..0000000 --- a/exception_handling/CheckedExceptionHandlerConsumer.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.javatechie.exception_handling; - -@FunctionalInterface -public interface CheckedExceptionHandlerConsumer { - - public void accept(Target target) throws ExObj; -} diff --git a/exception_handling/ExceptionHandlingExample.java b/exception_handling/ExceptionHandlingExample.java deleted file mode 100644 index 8a36544..0000000 --- a/exception_handling/ExceptionHandlingExample.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.javatechie.exception_handling; - -import java.lang.annotation.Target; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -public class ExceptionHandlingExample { - - public static void main(String[] args) { - - List list = Arrays.asList("44", "373", "xyz"); - List list1 = Arrays.asList(1,0); - // list1.forEach(handleGenericException(s-> System.out.println(10/s),ArithmeticException.class)); - - // list.forEach(handleGenericException(s -> System.out.println(Integer.parseInt(s)),NumberFormatException.class)); - - // List intList = list.stream().map(Integer::parseInt).collect(Collectors.toList()); - // System.out.println(intList); - - //handle exception for checkedException - List list2 = Arrays.asList(10,20); - - list2.forEach(handleCheckedExceptionConsumer(i->{ - Thread.sleep(i); - System.out.println(i); - })); - } - //approach -2 - - public static void printList(String s) { - try { - System.out.println(Integer.parseInt(s)); - } catch (Exception ex) { - System.out.println("exception : " + ex.getMessage()); - } - } - - static Consumer handleExceptionIfAny(Consumer payload) { - return obj -> { - try { - payload.accept(obj); - } catch (Exception ex) { - System.out.println("exception : " + ex.getMessage()); - } - }; - } - - static Consumer handleGenericException(Consumer targetConsumer, - Class exObjClass) { - return obj -> { - try { - targetConsumer.accept(obj); - } catch (Exception ex) { - try { - ExObj exObj = exObjClass.cast(ex); - System.out.println("exception : " + exObj.getMessage()); - } catch (ClassCastException ecx) { - throw ex; - } - } - }; - } - - static Consumer handleCheckedExceptionConsumer(CheckedExceptionHandlerConsumer handlerConsumer){ - return obj -> { - try { - handlerConsumer.accept(obj); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - }; - } -} diff --git a/java8.iml b/java8.iml new file mode 100644 index 0000000..bf7805c --- /dev/null +++ b/java8.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/java8/Employee.java b/java8/Employee.java deleted file mode 100644 index ebf0723..0000000 --- a/java8/Employee.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.java8; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -class Employee{ - - private int id; - private String name; - private String dept; - private List projects; - private double salary; - private String gender; - -} - diff --git a/java8/EmployeeDataBase.java b/java8/EmployeeDataBase.java deleted file mode 100644 index a934918..0000000 --- a/java8/EmployeeDataBase.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.java8; - -import java.util.Arrays; -import java.util.List; - -public class EmployeeDataBase { - - public static List getAllEmployees() { - Project p1 = new Project("P001", "Alpha", "ABC Corp", "Alice"); - Project p2 = new Project("P002", "Beta", "XYZ Ltd", "Bob"); - Project p3 = new Project("P003", "Gamma", "ABC Corp", "Alice"); - Project p4 = new Project("P004", "Delta", "TechWorld", "Charlie"); - Project p5 = new Project("P005", "Epsilon", "MoneyMatters", "Daniel"); - Project p6 = new Project("P006", "Zeta", "SmartTech", "Eva"); - Project p7 = new Project("P007", "Eta", "BrandBoost", "George"); - Project p8 = new Project("P008", "Theta", "InnoSoft", "Hannah"); - Project p9 = new Project("P009", "Iota", "FastTrack", "Ian"); - Project p10 = new Project("P010", "Kappa", "DigitalWave", "Jessica"); - - // Employee instances - Employee e1 = new Employee(1, "John Doe", "Development", Arrays.asList(p1, p2), 80000, "Male"); - Employee e2 = new Employee(2, "Jane Smith", "Development", Arrays.asList(p3), 80000, "Female"); - Employee e3 = new Employee(3, "Robert Brown", "Sales", Arrays.asList(p4), 60000, "Male"); - Employee e4 = new Employee(4, "Lisa White", "HR", Arrays.asList(p1), 55000, "Female"); - Employee e5 = new Employee(5, "Michael Green", "Finance", Arrays.asList(p5), 90000, "Male"); - Employee e6 = new Employee(6, "Sophia Brown", "Development", Arrays.asList(p6), 85000, "Female"); - Employee e7 = new Employee(7, "James Wilson", "Marketing", Arrays.asList(p7), 72000, "Male"); - Employee e8 = new Employee(8, "Olivia Harris", "Development", Arrays.asList(p8), 88000, "Female"); - Employee e9 = new Employee(9, "William Lee", "Sales", Arrays.asList(p9), 78000, "Male"); - Employee e10 = new Employee(10, "Emily Clark", "Development", Arrays.asList(p10), 95000, "Female"); - - // Print employee details (just for testing) - return Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); - } -} diff --git a/java8/Java8MethodCheatSheet.java b/java8/Java8MethodCheatSheet.java deleted file mode 100644 index c0267d7..0000000 --- a/java8/Java8MethodCheatSheet.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.java8; - -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class Java8MethodCheatSheet { - - public static void main(String[] args) { - - - List employees = EmployeeDataBase.getAllEmployees(); - - // forEach - //employees.forEach(e-> System.out.println(e.getName()+" : "+e.getSalary())); - - //employees.stream().forEach(System.out::println); - - //filter - //.collect - - Map developmentEmployees = employees.stream() - .filter(e -> e.getDept().equals("Development") && e.getSalary() > 80000) - .collect(Collectors.toMap(Employee::getId, Employee::getName)); - - // System.out.println(developmentEmployees); - - //map - //distinct - List depts = employees.stream() - .map(Employee::getDept) - .distinct() - .collect(Collectors.toList()); - //System.out.println(depts); - - List> projectNames = employees.stream() - .map(e -> e.getProjects() - .stream().map(p -> p.getName())).collect(Collectors.toList()); - - //flatMap - - List projects = employees.stream() - .flatMap(e -> e.getProjects().stream()) - .map(p -> p.getName()).distinct() - .collect(Collectors.toList()); - - //System.out.println(projects); - - - //sorted - //asc - List ascSortedEmployees = employees.stream() - .sorted(Comparator.comparing(Employee::getSalary)) - .collect(Collectors.toList()); - -// ascSortedEmployees.get(0); - - //ascSortedEmployees.forEach(System.out::println); - - //desc - List descSortedEmployees = employees.stream() - .sorted(Collections.reverseOrder(Comparator.comparing(Employee::getSalary))) - .collect(Collectors.toList()); - -// descSortedEmployees.get(0); - - //descSortedEmployees.forEach(System.out::println); - - //min & max - Optional highestPaidEmployees = employees.stream() - .max(Comparator.comparingDouble(Employee::getSalary)); - - // System.out.println("Highest paid employee : "+highestPaidEmployees); - - Optional lowestPaidEmployees = employees.stream() - .min(Comparator.comparingDouble(Employee::getSalary)); - - //System.out.println("Lowest paid employee : "+lowestPaidEmployees); - - //groupingBy - - Map> employeeGroup = employees.stream() - .collect(Collectors.groupingBy(Employee::getGender)); - - //System.out.println(employeeGroup); - - //Gender -> [names] - Map> employeeGroupNames = employees.stream() - .collect(Collectors.groupingBy(Employee::getGender, - Collectors.mapping(Employee::getName, Collectors.toList()) - )); - - //System.out.println(employeeGroupNames); - - //Gender -> [count] - Map employeeGroupCountMap = employees.stream() - .collect(Collectors.groupingBy(Employee::getGender, Collectors.counting())); - System.out.println(employeeGroupCountMap); - - //findFirst - - Employee findFirstElement = employees.stream() - .filter(e -> e.getDept().equals("Development")) - .findFirst() - .orElseThrow(()->new IllegalArgumentException("Employee not found ")); - -// System.out.println(findFirstElement.get());//NPE -// -// if(findFirstElement.isPresent()){ -// System.out.println(findFirstElement.get()); -// } -// -// findFirstElement.ifPresent(e-> System.out.println(e.getName())); - - //System.out.println(findFirstElement); - - //findAny - - Employee findAnyElement = employees.stream() - .filter(e -> e.getDept().equals("Development")) - .findAny() - .orElseThrow(()->new IllegalArgumentException("Employee not found ")); - - // System.out.println(findAnyElement); - - //anyMatch(Predicate) , allMatch(Predicate) , noneMatch(Predicate) - - boolean developmentEmpAnyMatch = employees.stream() - .anyMatch(e -> e.getDept().equals("Development")); - //System.out.println("is there any employee match from development dept "+developmentEmpAnyMatch); - - - boolean developmentEmpAllMatch = employees.stream() - .allMatch(e -> e.getSalary()>50000);//55000 - //System.out.println(developmentEmpAllMatch); //false - - - boolean isNoneMatch = employees.stream() - .noneMatch(e -> e.getDept().equals("abc")); - //System.out.println(isNoneMatch); - - //limit(long) - - List topPaidEmployees = employees.stream() - .sorted(Comparator.comparing(Employee::getSalary).reversed()) - .limit(4) - .collect(Collectors.toList()); - - topPaidEmployees.forEach(e-> System.out.println(e.getName())); - - //skip(long) - List skipEmployees = employees.stream().skip(10) - .collect(Collectors.toList()); - - -// -// forEach(Consumer) -// filter(Predicate) -// collect(Collector) -// map(Function) -// distinct() -// flatMap(Function) -// sorted(Comparator both ASC and DESC) -// min() & max() -// GroupBy -// findFirst() -// findAny() -// anyMatch(Predicate) -// allMatch(Predicate) -// noneMatch(Predicate) -// limit(long maxSize) -// skip(long n) - - - } -} diff --git a/java8/Project.java b/java8/Project.java deleted file mode 100644 index 651d42c..0000000 --- a/java8/Project.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.java8; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -class Project{ - - private String projectCode; - private String name ; - private String client; - private String buLeadName; -} \ No newline at end of file diff --git a/mapVsflatMap/Customer.java b/mapVsflatMap/Customer.java deleted file mode 100644 index 744fd04..0000000 --- a/mapVsflatMap/Customer.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.javatechie; - -import java.util.List; - -public class Customer { - - private int id; - private String name; - private String email; - private List phoneNumbers; - - public Customer() { - } - - public Customer(int id, String name, String email, List phoneNumbers) { - this.id = id; - this.name = name; - this.email = email; - this.phoneNumbers = phoneNumbers; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public List getPhoneNumbers() { - return phoneNumbers; - } - - public void setPhoneNumbers(List phoneNumbers) { - this.phoneNumbers = phoneNumbers; - } -} diff --git a/mapVsflatMap/EkartDataBase.java b/mapVsflatMap/EkartDataBase.java deleted file mode 100644 index eb7f756..0000000 --- a/mapVsflatMap/EkartDataBase.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.javatechie; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class EkartDataBase { - - - public static List getAll() { - return Stream.of( - new Customer(101, "john", "john@gmail.com", Arrays.asList("397937955", "21654725")), - new Customer(102, "smith", "smith@gmail.com", Arrays.asList("89563865", "2487238947")), - new Customer(103, "peter", "peter@gmail.com", Arrays.asList("38946328654", "3286487236")), - new Customer(104, "kely", "kely@gmail.com", Arrays.asList("389246829364", "948609467")) - ).collect(Collectors.toList()); - } - - -} diff --git a/mapVsflatMap/MapVsFlatMap.java b/mapVsflatMap/MapVsFlatMap.java deleted file mode 100644 index 4017cdb..0000000 --- a/mapVsflatMap/MapVsFlatMap.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.javatechie; - -import java.util.List; -import java.util.stream.Collectors; - -public class MapVsFlatMap { - public static void main(String[] args) { - - List customers = EkartDataBase.getAll(); - - //List convert List -> Data Transformation - //mapping : customer -> customer.getEmail() - //customer -> customer.getEmail() one to one mapping - List emails = customers.stream() - .map(customer -> customer.getEmail()) - .collect(Collectors.toList()); - System.out.println(emails); - -//customer -> customer.getPhoneNumbers() ->> one to many mapping - //customer -> customer.getPhoneNumbers() ->> one to many mapping - List> phoneNumbers = customers. - stream().map(customer -> customer.getPhoneNumbers()) - .collect(Collectors.toList()); - System.out.println(phoneNumbers); - - //List convert List -> Data Transformation - //mapping : customer -> phone Numbers - //customer -> customer.getPhoneNumbers() ->> one to many mapping - List phones = customers.stream() - .flatMap(customer -> customer.getPhoneNumbers().stream()) - .collect(Collectors.toList()); - System.out.println(phones); - } -} diff --git a/mapVsflatMap/readme.md b/mapVsflatMap/readme.md deleted file mode 100644 index 9567b8a..0000000 --- a/mapVsflatMap/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -Differences between Java 8 Map() Vs flatMap() : - -map() | flatMap() | ---- | --- | -It processes stream of values. | It processes stream of stream of values. -It does only mapping. | It performs mapping as well as flattening. -It’s mapper function produces single value for each input value. | It’s mapper function produces multiple values for each input value. -It is a One-To-One mapping. | It is a One-To-Many mapping. -Data Transformation : From Stream to Stream | Data Transformation : From Stream to Stream -Use this method when the mapper function is producing a single value for each input value. | Use this method when the mapper function is producing multiple values for each input value. - diff --git a/map_reduce/Employee.java b/map_reduce/Employee.java deleted file mode 100644 index b70f65d..0000000 --- a/map_reduce/Employee.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.javatechie.map_reduce; - -public class Employee { - private int id; - private String name; - private String grade; - private double salary; - - public Employee() { - } - - public Employee(int id, String name, String grade, double salary) { - this.id = id; - this.name = name; - this.grade = grade; - this.salary = salary; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getGrade() { - return grade; - } - - public void setGrade(String grade) { - this.grade = grade; - } - - public double getSalary() { - return salary; - } - - public void setSalary(double salary) { - this.salary = salary; - } - - -} diff --git a/map_reduce/EmployeeDatabase.java b/map_reduce/EmployeeDatabase.java deleted file mode 100644 index b1e0f34..0000000 --- a/map_reduce/EmployeeDatabase.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.javatechie.map_reduce; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class EmployeeDatabase { - - - public static List getEmployees(){ - return Stream.of(new Employee(101,"john","A",60000), - new Employee(109,"peter","B",30000), - new Employee(102,"mak","A",80000), - new Employee(103,"kim","A",90000), - new Employee(104,"json","C",15000)) - .collect(Collectors.toList()); - } -} diff --git a/map_reduce/MapReduceExample.java b/map_reduce/MapReduceExample.java deleted file mode 100644 index 3e8b4fd..0000000 --- a/map_reduce/MapReduceExample.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.javatechie.map_reduce; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -public class MapReduceExample { - - public static void main(String[] args) { - - List numbers = Arrays.asList(3, 7, 8, 1, 5, 9); - - List words = Arrays.asList("corejava", "spring", "hibernate"); - - int sum = 0; - for (int no : numbers) { - sum = sum + no; - } - System.out.println(sum); - - int sum1 = numbers.stream().mapToInt(i -> i).sum(); - System.out.println(sum1); - - Integer reduceSum = numbers.stream().reduce(0, (a, b) -> a + b); - System.out.println(reduceSum); - - Optional reduceSumWithMethodReference = numbers.stream().reduce(Integer::sum); - System.out.println(reduceSumWithMethodReference.get()); - - Integer mulResult = numbers.stream().reduce(1, (a, b) -> a * b); - System.out.println(mulResult); - - Integer maxvalue = numbers.stream().reduce(0, (a, b) -> a > b ? a : b); - System.out.println(maxvalue); - - Integer maxvalueWithMethodReference = numbers.stream().reduce(Integer::max).get(); - System.out.println(maxvalueWithMethodReference); - - - String longestString = words.stream() - .reduce((word1, word2) -> word1.length() > word2.length() ? word1 : word2) - .get(); - System.out.println(longestString); - - //get employee whose grade A - //get salary - double avgSalary = EmployeeDatabase.getEmployees().stream() - .filter(employee -> employee.getGrade().equalsIgnoreCase("A")) - .map(employee -> employee.getSalary()) - .mapToDouble(i -> i) - .average().getAsDouble(); - - System.out.println(avgSalary); - - double sumSalary = EmployeeDatabase.getEmployees().stream() - .filter(employee -> employee.getGrade().equalsIgnoreCase("A")) - .map(employee -> employee.getSalary()) - .mapToDouble(i -> i) - .sum(); - System.out.println(sumSalary); - } - - -} diff --git a/parralel_stream/ParallelStreamExample.java b/parralel_stream/ParallelStreamExample.java deleted file mode 100644 index 8b6e913..0000000 --- a/parralel_stream/ParallelStreamExample.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.javatechie.parralel_stream; - -import com.javatechie.map_reduce.Employee; -import com.javatechie.map_reduce.EmployeeDatabase; - -import java.util.List; -import java.util.stream.IntStream; - -public class ParallelStreamExample { - - - public static void main(String[] args) { - long start=0; - long end=0; - - start=System.currentTimeMillis(); - IntStream.range(1,100).forEach(System.out::println); - end=System.currentTimeMillis(); - System.out.println("Plain stream took time : "+(end-start)); - - System.out.println("============================================"); - - start=System.currentTimeMillis(); - IntStream.range(1,100).parallel().forEach(System.out::println); - end=System.currentTimeMillis(); - System.out.println("Parallel stream took time : "+(end-start)); - - - - IntStream.range(1,10).forEach(x->{ - System.out.println("Thread : "+Thread.currentThread().getName()+" : "+x); - }); - - IntStream.range(1,10).parallel().forEach(x->{ - System.out.println("Thread : "+Thread.currentThread().getName()+" : "+x); - }); - - List employees = EmployeeDatabase.getEmployees(); - - //normal - start=System.currentTimeMillis(); - double salaryWithStream = employees.stream() - .map(Employee::getSalary).mapToDouble(i -> i).average().getAsDouble(); - end=System.currentTimeMillis(); - - System.out.println("Normal stream execution time : "+(end-start)+" : Avg salary : "+salaryWithStream); - - start=System.currentTimeMillis(); - double salaryWithParallelStream = employees.parallelStream() - .map(Employee::getSalary).mapToDouble(i -> i).average().getAsDouble(); - - end=System.currentTimeMillis(); - - System.out.println("Parallel stream execution time : "+(end-start)+" : Avg salary : "+salaryWithParallelStream); - } -} diff --git a/qa/Java8CommonProgrammingQA.java b/qa/Java8CommonProgrammingQA.java deleted file mode 100644 index 72b51f7..0000000 --- a/qa/Java8CommonProgrammingQA.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.interview.qa; - -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class Java8CommonProgrammingQA { - - public static void main(String[] args) { - List studentList = Stream.of( - new Student(1, "Rohit", 30, "Male", "Mechanical Engineering", "Mumbai", 122, Arrays.asList("+912632632782", "+1673434729929")), - new Student(2, "Pulkit", 56, "Male", "Computer Engineering", "Delhi", 67, Arrays.asList("+912632632762", "+1673434723929")), - new Student(3, "Ankit", 25, "Female", "Mechanical Engineering", "Kerala", 164, Arrays.asList("+912632633882", "+1673434709929")), - new Student(4, "Satish Ray", 30, "Male", "Mechanical Engineering", "Kerala", 26, Arrays.asList("+9126325832782", "+1671434729929")), - new Student(5, "Roshan", 23, "Male", "Biotech Engineering", "Mumbai", 12, Arrays.asList("+012632632782")), - new Student(6, "Chetan", 24, "Male", "Mechanical Engineering", "Karnataka", 90, Arrays.asList("+9126254632782", "+16736784729929")), - new Student(7, "Arun", 26, "Male", "Electronics Engineering", "Karnataka", 324, Arrays.asList("+912632632782", "+1671234729929")), - new Student(8, "Nam", 31, "Male", "Computer Engineering", "Karnataka", 433, Arrays.asList("+9126326355782", "+1673434729929")), - new Student(9, "Sonu", 27, "Female", "Computer Engineering", "Karnataka", 7, Arrays.asList("+9126398932782", "+16563434729929", "+5673434729929")), - new Student(10, "Shubham", 26, "Male", "Instrumentation Engineering", "Mumbai", 98, Arrays.asList("+912632646482", "+16734323229929"))) - .collect(Collectors.toList()); - - // 1. Find the list of students whose rank is in between 50 and 100 - - List students = studentList.stream().filter(student -> student.getRank() > 50 && student.getRank() < 100) - .collect(Collectors.toList()); - // System.out.println(students); - - //2. Find the Students who stays in Karnataka and sort them by their names - - List studentsByCity = studentList.stream().filter(student -> student.getCity().equals("Karnataka")) - .sorted(Comparator.comparing(Student::getFirstName,Comparator.reverseOrder())).collect(Collectors.toList()); - // System.out.println(studentsByCity); - - // 3. Find all departments names - - List deptNames = studentList.stream() - .map(Student::getDept) - .distinct() - .collect(Collectors.toList()); - - Set deptNamesInSet = studentList - .stream().map(Student::getDept) - .collect(Collectors.toSet()); - - //System.out.println(deptNames); - //System.out.println(deptNamesInSet); - - //4. Find all the contact numbers - List contacts = studentList.stream() - .flatMap(student -> student.getContacts().stream()) - .distinct() - .collect(Collectors.toList()); - System.out.println(contacts); - //one2one-> map - //one2many-> flatmap - - //5. Group The Student By Department Names - - - Map> studentMap = studentList.stream() - .collect(Collectors.groupingBy(Student::getDept)); - // System.out.println(studentMap); - - - //6. Find the department who is having maximum number of students - Map.Entry results = studentList.stream() - .collect(Collectors.groupingBy(Student::getDept, Collectors.counting())) - .entrySet().stream().max(Map.Entry.comparingByValue()).get(); - - System.out.println(results); - - //7. Find the average age of male and female students - - Map avgStudents = studentList.stream() - .collect(Collectors - .groupingBy(Student::getGender, - Collectors.averagingInt(Student::getAge))); - - //System.out.println(avgStudents); - - //8. Find the highest rank in each department - - Map> stdMap = studentList.stream() - .collect(Collectors.groupingBy(Student::getDept, - Collectors.minBy(Comparator.comparing(Student::getRank)))); - // System.out.println(stdMap); - - //9 .Find the student who has second rank - - Student student = studentList.stream() - .sorted(Comparator.comparing(Student::getRank)) - .skip(2) - .findFirst().get(); - System.out.println(student); - - } - - -} diff --git a/qa/Student.java b/qa/Student.java deleted file mode 100644 index e79dd7f..0000000 --- a/qa/Student.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.interview.qa; - -import java.util.List; -import java.util.Objects; - -public class Student { - - private int id; - private String firstName; - private int age; - private String gender; - private String dept; - private String city; - private int rank; - private List contacts; - - public Student(int id, String firstName, int age, String gender, String dept, String city, int rank, List contacts) { - this.id = id; - this.firstName = firstName; - this.age = age; - this.gender = gender; - this.dept = dept; - this.city = city; - this.rank = rank; - this.contacts = contacts; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public String getGender() { - return gender; - } - - public void setGender(String gender) { - this.gender = gender; - } - - public String getDept() { - return dept; - } - - public void setDept(String dept) { - this.dept = dept; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public int getRank() { - return rank; - } - - public void setRank(int rank) { - this.rank = rank; - } - - public List getContacts() { - return contacts; - } - - public void setContacts(List contacts) { - this.contacts = contacts; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Student student = (Student) o; - return id == student.id && age == student.age && rank == student.rank && Objects.equals(firstName, student.firstName) && Objects.equals(gender, student.gender) && Objects.equals(dept, student.dept) && Objects.equals(city, student.city) && Objects.equals(contacts, student.contacts); - } - - @Override - public int hashCode() { - return Objects.hash(id, firstName, age, gender, dept, city, rank, contacts); - } - - @Override - public String toString() { - return "Student{" + - "id=" + id + - ", firstName='" + firstName + '\'' + - ", age=" + age + - ", gender='" + gender + '\'' + - ", dept='" + dept + '\'' + - ", city='" + city + '\'' + - ", rank=" + rank + - ", contacts=" + contacts + - '}'; - } -} \ No newline at end of file diff --git a/sort/SortListDemo.java b/sort/SortListDemo.java deleted file mode 100644 index ac489c8..0000000 --- a/sort/SortListDemo.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.javatechie.stream.sort; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import com.javatechie.stream.api.example.DataBase; -import com.javatechie.stream.api.example.Employee; - -public class SortListDemo { - - public static void main(String[] args) { - - List list = new ArrayList<>(); - list.add(8); - list.add(3); - list.add(12); - list.add(4); - - List employees = DataBase.getEmployees(); - - /*Collections.sort(employees, new Comparator() { - - @Override - public int compare(Employee o1, Employee o2) { - return (int) (o1.getSalary() - o2.getSalary());// ascending - } - });*/ - - - Collections.sort(employees, ( o1, o2) ->(int) (o1.getSalary() - o2.getSalary())); - - //System.out.println(employees); - - - //employees.stream().sorted(( o1, o2) ->(int) (o2.getSalary() - o1.getSalary())).forEach(System.out::println); - - //employees.stream().sorted(Comparator.comparing(emp->emp.getSalary())).forEach(System.out::println); - - employees.stream().sorted(Comparator.comparing(Employee::getDept)).forEach(System.out::println); - - /* - * Collections.sort(list);//ASSENDING Collections.reverse(list); - * System.out.println(list); - * - * list.stream().sorted(Comparator.reverseOrder()).forEach(s->System.out.println - * (s));//descending - */ - - } -} diff --git a/sort/SortMapDemo.java b/sort/SortMapDemo.java deleted file mode 100644 index 6cec015..0000000 --- a/sort/SortMapDemo.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.javatechie.stream.sort; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -import com.javatechie.stream.api.example.Employee; - -public class SortMapDemo { - - public static void main(String[] args) { - - Map map = new HashMap<>(); - map.put("eight", 8); - map.put("four", 4); - map.put("ten", 10); - map.put("two", 2); - - Map employeeMap = new TreeMap<>((o1, o2) -> (int) (o2.getSalary() - o1.getSalary())); - employeeMap.put(new Employee(176, "Roshan", "IT", 600000), 60); - employeeMap.put(new Employee(388, "Bikash", "CIVIL", 900000), 90); - employeeMap.put(new Employee(470, "Bimal", "DEFENCE", 500000), 50); - employeeMap.put(new Employee(624, "Sourav", "CORE", 400000), 40); - employeeMap.put(new Employee(284, "Prakash", "SOCIAL", 1200000), 120); - - System.out.println(employeeMap); - - List> entries = new ArrayList<>(map.entrySet()); - Collections.sort(entries, (o1, o2) -> o1.getKey().compareTo(o2.getKey())); - - /* - * for (Entry entry : entries) { - * System.out.println(entry.getKey() + " " + entry.getValue()); } - */ - - // map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(System.out::println); - System.out.println("****************************"); - // map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(System.out::println); - - employeeMap.entrySet().stream() - .sorted(Map.Entry.comparingByKey(Comparator.comparing(Employee::getDept).reversed())) - .forEach(System.out::println); - - } -} diff --git a/src/models/Book.java b/src/models/Book.java new file mode 100644 index 0000000..abb1a1e --- /dev/null +++ b/src/models/Book.java @@ -0,0 +1,44 @@ +package com.javatechie.lambda.demo.src.models; + +import java.util.Objects; + +public class Book { + private int id; + private String name; + private int pages; + + public Book() {} + + public Book(int id, String name, int pages) { + this.id = id; + this.name = name; + this.pages = pages; + } + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public int getPages() { return pages; } + public void setPages(int pages) { this.pages = pages; } + + @Override + public String toString() { + return "Book [id=" + id + ", name=" + name + ", pages=" + pages + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return id == book.id && pages == book.pages && Objects.equals(name, book.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, pages); + } +} diff --git a/src/models/Customer.java b/src/models/Customer.java new file mode 100644 index 0000000..31ff815 --- /dev/null +++ b/src/models/Customer.java @@ -0,0 +1,36 @@ +package com.javatechie.lambda.demo.src.models; + +import java.util.List; + +public class Customer { + private int id; + private String name; + private String email; + private List phoneNumbers; + + public Customer() {} + + public Customer(int id, String name, String email, List phoneNumbers) { + this.id = id; + this.name = name; + this.email = email; + this.phoneNumbers = phoneNumbers; + } + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + + public List getPhoneNumbers() { return phoneNumbers; } + public void setPhoneNumbers(List phoneNumbers) { this.phoneNumbers = phoneNumbers; } + + @Override + public String toString() { + return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", phoneNumbers=" + phoneNumbers + "]"; + } +} diff --git a/src/models/DataStore.java b/src/models/DataStore.java new file mode 100644 index 0000000..1280f65 --- /dev/null +++ b/src/models/DataStore.java @@ -0,0 +1,66 @@ +package com.javatechie.lambda.demo.src.models; + +import java.util.Arrays; +import java.util.List; + +public class DataStore { + + public static List getAllEmployees() { + Project p1 = new Project("P001", "Alpha", "ABC Corp", "Alice"); + Project p2 = new Project("P002", "Beta", "XYZ Ltd", "Bob"); + Project p3 = new Project("P003", "Gamma", "ABC Corp", "Alice"); + Project p4 = new Project("P004", "Delta", "TechWorld", "Charlie"); + Project p5 = new Project("P005", "Epsilon", "MoneyMatters", "Daniel"); + Project p6 = new Project("P006", "Zeta", "SmartTech", "Eva"); + Project p7 = new Project("P007", "Eta", "BrandBoost", "George"); + Project p8 = new Project("P008", "Theta", "InnoSoft", "Hannah"); + Project p9 = new Project("P009", "Iota", "FastTrack", "Ian"); + Project p10 = new Project("P010", "Kappa", "DigitalWave", "Jessica"); + + Employee e1 = new Employee(1, "John Doe", "Development", Arrays.asList(p1, p2), 80000, "Male"); + Employee e2 = new Employee(2, "Jane Smith", "Development", Arrays.asList(p3), 80000, "Female"); + Employee e3 = new Employee(3, "Robert Brown", "Sales", Arrays.asList(p4), 60000, "Male"); + Employee e4 = new Employee(4, "Lisa White", "HR", Arrays.asList(p1), 55000, "Female"); + Employee e5 = new Employee(5, "Michael Green", "Finance", Arrays.asList(p5), 90000, "Male"); + Employee e6 = new Employee(6, "Sophia Brown", "Development", Arrays.asList(p6), 85000, "Female"); + Employee e7 = new Employee(7, "James Wilson", "Marketing", Arrays.asList(p7), 72000, "Male"); + Employee e8 = new Employee(8, "Olivia Harris", "Development", Arrays.asList(p8), 88000, "Female"); + Employee e9 = new Employee(9, "William Lee", "Sales", Arrays.asList(p9), 78000, "Male"); + Employee e10 = new Employee(10, "Emily Clark", "Development", Arrays.asList(p10), 95000, "Female"); + + return Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); + } + + public static List getAllStudents() { + return Arrays.asList( + new Student(1, "Rohit", 30, "Male", "Mechanical Engineering", "Mumbai", 122, Arrays.asList("+912632632782", "+1673434729929")), + new Student(2, "Pulkit", 56, "Male", "Computer Engineering", "Delhi", 67, Arrays.asList("+912632632762", "+1673434723929")), + new Student(3, "Ankit", 25, "Female", "Mechanical Engineering", "Kerala", 164, Arrays.asList("+912632633882", "+1673434709929")), + new Student(4, "Satish Ray", 30, "Male", "Mechanical Engineering", "Kerala", 26, Arrays.asList("+9126325832782", "+1671434729929")), + new Student(5, "Roshan", 23, "Male", "Biotech Engineering", "Mumbai", 12, Arrays.asList("+012632632782")), + new Student(6, "Chetan", 24, "Male", "Mechanical Engineering", "Karnataka", 90, Arrays.asList("+9126254632782", "+16736784729929")), + new Student(7, "Arun", 26, "Male", "Electronics Engineering", "Karnataka", 324, Arrays.asList("+912632632782", "+1671234729929")), + new Student(8, "Nam", 31, "Male", "Computer Engineering", "Karnataka", 433, Arrays.asList("+9126326355782", "+1673434729929")), + new Student(9, "Sonu", 27, "Female", "Computer Engineering", "Karnataka", 7, Arrays.asList("+9126398932782", "+16563434729929", "+5673434729929")), + new Student(10, "Shubham", 26, "Male", "Instrumentation Engineering", "Mumbai", 98, Arrays.asList("+912632646482", "+16734323229929")) + ); + } + + public static List getAllCustomers() { + return Arrays.asList( + new Customer(101, "john", "john@gmail.com", Arrays.asList("397937955", "21654725")), + new Customer(102, "smith", "smith@gmail.com", Arrays.asList("89563856", "24856389")), + new Customer(103, "peter", "peter@gmail.com", Arrays.asList("38946326", "2389463")), + new Customer(104, "kely", "kely@gmail.com", Arrays.asList("98763524", "3794628")) + ); + } + + public static List getAllBooks() { + return Arrays.asList( + new Book(101, "Core Java", 400), + new Book(363, "Hibernate", 180), + new Book(275, "Spring Boot", 240), + new Book(893, "WebService", 300) + ); + } +} diff --git a/src/models/Employee.java b/src/models/Employee.java new file mode 100644 index 0000000..db5538d --- /dev/null +++ b/src/models/Employee.java @@ -0,0 +1,53 @@ +package com.javatechie.lambda.demo.src.models; + +import java.util.List; + +public class Employee { + private int id; + private String name; + private String dept; + private List projects; + private double salary; + private String gender; + + public Employee() {} + + public Employee(int id, String name, String dept, List projects, double salary, String gender) { + this.id = id; + this.name = name; + this.dept = dept; + this.projects = projects; + this.salary = salary; + this.gender = gender; + } + + public Employee(int id, String name, String dept, double salary) { + this.id = id; + this.name = name; + this.dept = dept; + this.salary = salary; + } + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getDept() { return dept; } + public void setDept(String dept) { this.dept = dept; } + + public List getProjects() { return projects; } + public void setProjects(List projects) { this.projects = projects; } + + public double getSalary() { return salary; } + public void setSalary(double salary) { this.salary = salary; } + + public String getGender() { return gender; } + public void setGender(String gender) { this.gender = gender; } + + @Override + public String toString() { + return "Employee [id=" + id + ", name=" + name + ", dept=" + dept + ", salary=" + salary + ", gender=" + gender + "]"; + } +} diff --git a/src/models/Project.java b/src/models/Project.java new file mode 100644 index 0000000..9676828 --- /dev/null +++ b/src/models/Project.java @@ -0,0 +1,34 @@ +package com.javatechie.lambda.demo.src.models; + +public class Project { + private String code; + private String name; + private String client; + private String manager; + + public Project() {} + + public Project(String code, String name, String client, String manager) { + this.code = code; + this.name = name; + this.client = client; + this.manager = manager; + } + + public String getCode() { return code; } + public void setCode(String code) { this.code = code; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getClient() { return client; } + public void setClient(String client) { this.client = client; } + + public String getManager() { return manager; } + public void setManager(String manager) { this.manager = manager; } + + @Override + public String toString() { + return "Project [code=" + code + ", name=" + name + ", client=" + client + ", manager=" + manager + "]"; + } +} diff --git a/src/models/Student.java b/src/models/Student.java new file mode 100644 index 0000000..55b75bd --- /dev/null +++ b/src/models/Student.java @@ -0,0 +1,56 @@ +package com.javatechie.lambda.demo.src.models; + +import java.util.List; + +public class Student { + private int id; + private String firstName; + private int age; + private String gender; + private String dept; + private String city; + private int rank; + private List contacts; + + public Student() {} + + public Student(int id, String firstName, int age, String gender, String dept, String city, int rank, List contacts) { + this.id = id; + this.firstName = firstName; + this.age = age; + this.gender = gender; + this.dept = dept; + this.city = city; + this.rank = rank; + this.contacts = contacts; + } + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public String getFirstName() { return firstName; } + public void setFirstName(String firstName) { this.firstName = firstName; } + + public int getAge() { return age; } + public void setAge(int age) { this.age = age; } + + public String getGender() { return gender; } + public void setGender(String gender) { this.gender = gender; } + + public String getDept() { return dept; } + public void setDept(String dept) { this.dept = dept; } + + public String getCity() { return city; } + public void setCity(String city) { this.city = city; } + + public int getRank() { return rank; } + public void setRank(int rank) { this.rank = rank; } + + public List getContacts() { return contacts; } + public void setContacts(List contacts) { this.contacts = contacts; } + + @Override + public String toString() { + return "Student [id=" + id + ", firstName=" + firstName + ", age=" + age + ", gender=" + gender + ", dept=" + dept + ", city=" + city + ", rank=" + rank + "]"; + } +} diff --git a/src/module1_lambda/CONCEPT.md b/src/module1_lambda/CONCEPT.md new file mode 100644 index 0000000..48bc398 --- /dev/null +++ b/src/module1_lambda/CONCEPT.md @@ -0,0 +1,147 @@ +# Module 1: Lambda Expressions and Functional Interfaces + +## 1. Introduction to Lambda Expressions +A lambda expression in Java is a concise way to represent an anonymous function (a function without a name). It allows you to pass behavior as an argument to a method, treating code as data. This is the cornerstone of functional programming in Java. + +Syntax of Lambda Expressions: +```java +(parameters) -> expression +// or +(parameters) -> { statements; } +``` +Example: +```java +// Traditional Anonymous Class +Runnable r1 = new Runnable() { + @Override + public void run() { + System.out.println("Running..."); + } +}; + +// Lambda Expression equivalent +Runnable r2 = () -> System.out.println("Running..."); +``` + +--- + +## 2. Functional Interfaces and `@FunctionalInterface` +A **Functional Interface** is an interface that contains exactly **one abstract method** (also known as the Single Abstract Method or SAM). +* It can have any number of default and static methods. +* The `@FunctionalInterface` annotation is informative but highly recommended. It instructs the compiler to generate an error if the interface does not satisfy the SAM contract. +* Methods inherited from `java.lang.Object` (like `toString()`, `equals(Object)`, etc.) do not count toward the single abstract method limit. + +Example: +```java +@FunctionalInterface +public interface SimpleCalculator { + int operate(int a, int b); // Single Abstract Method + + default void printInfo() { + System.out.println("Calculator operational"); + } + + static boolean isNonZero(int a) { + return a != 0; + } +} +``` + +--- + +## 3. Built-in Functional Interfaces +Java 8 introduced the `java.util.function` package containing pre-defined functional interfaces. Here are the core categories: + +| Interface | Method Signature | Input | Output | Primary Use Case | +| :--- | :--- | :--- | :--- | :--- | +| **`Predicate`** | `boolean test(T t)` | Single Object `T` | `boolean` | Filtering, validation, checks | +| **`Consumer`** | `void accept(T t)` | Single Object `T` | `void` | Side effects, printing, logging | +| **`Supplier`** | `T get()` | None | Object `T` | Factory patterns, lazy generation | +| **`Function`** | `R apply(T t)` | Single Object `T` | Object `R` | Transformation, mapping | +| **`BiPredicate`** | `boolean test(T t, U u)` | Two Objects `T, U` | `boolean` | Comparison, two-input validation | +| **`BiConsumer`** | `void accept(T t, U u)` | Two Objects `T, U` | `void` | Multi-parameter actions, Map iteration | +| **`BiFunction`**| `R apply(T t, U u)` | Two Objects `T, U` | Object `R` | Math operations, combining two items | + +### Primitive Variations +To prevent automatic box/unbox overhead, Java 8 provides specialized interfaces for primitives: +* Predicates: `IntPredicate`, `LongPredicate`, `DoublePredicate` +* Consumers: `IntConsumer`, `LongConsumer`, `DoubleConsumer` +* Functions: `IntFunction`, `ToIntFunction`, `IntToLongFunction`, etc. + +--- + +## 4. Target Typing +Lambda expressions do not contain information about which functional interface they implement. Instead, their type is inferred from the context they are used in. This is called **Target Typing**. + +The compiler checks: +1. The target type context (assignment, method arguments, return statements). +2. If the target type is indeed a functional interface. +3. If the lambda parameters and return type match the signature of the Single Abstract Method. + +Example: +```java +// Same lambda expression resolved to different functional interfaces +Callable c = () -> "Hello"; +Supplier s = () -> "Hello"; +``` + +--- + +## 5. Scoping Rules and Variable Capture +Lambda expressions look like anonymous inner classes, but they have distinct differences in how they handle scope: + +### Variable Capture / Effectively Final +A lambda expression can access local variables from its enclosing block. However, the captured variable must be **final** or **effectively final** (meaning its value is never modified after initialization). +* This restriction exists because lambda instances may run asynchronously or in another thread, and permitting changes would introduce thread-safety issues. +* Instance and static variables are NOT subject to this restriction. + +### `this` Reference +* **Anonymous Class**: `this` refers to the anonymous class instance itself. +* **Lambda Expression**: `this` refers to the enclosing class instance where the lambda is defined. Lambdas do not introduce a new scope level. + +```java +public class ScopeDemo { + private String name = "Enclosing Class"; + + public void execute() { + Runnable rAnon = new Runnable() { + private String name = "Anonymous Class"; + @Override + public void run() { + System.out.println(this.name); // Prints: "Anonymous Class" + } + }; + + Runnable rLambda = () -> { + // System.out.println(this.name); + // In lambda, 'this' refers to ScopeDemo. This prints: "Enclosing Class" + System.out.println(this.name); + }; + + rAnon.run(); + rLambda.run(); + } +} +``` + +--- + +## 6. Senior-Level Interview Tips (5+ YOE) + +### Q1: How do lambdas compile under the hood? (Anonymous Classes vs invokedynamic) +Unlike anonymous inner classes, which generate a separate `.class` file (e.g., `MyClass$1.class`) and are instantiated via `new` (involving standard class loading and allocation costs), lambda expressions are compiled using the `invokedynamic` (indy) instruction (introduced in Java 7). +* At compile time, the compiler places the lambda body into a synthetic private static (or instance) method in the same class. +* At runtime, the first time the `invokedynamic` call site is hit, it bootstraps a recipe to link the lambda to a functional interface call site using `LambdaMetafactory.metafactory`. +* Subsequent calls bypass translation and run as fast as direct method calls, avoiding the overhead of creating dedicated classes and loading them. + +### Q2: Can Lambda expressions cause memory leaks? +Yes. If a lambda captures an instance variable or calls an instance method, it implicitly captures the `this` reference of the enclosing class. If that lambda is stored or passed to a long-lived object (e.g., a static cache or global registry), the enclosing object cannot be garbage collected, leading to a memory leak. To avoid this, capture local variables or use static helper methods whenever possible. + +### Q3: Why is there no `TripleFunction` or custom functions in the JDK? +The JDK designers limited functional interfaces to 1 or 2 parameters for simplicity and readability. If a function requires three or more parameters, it usually indicates that the parameters should be grouped into a domain object or configuration record. If truly necessary, you can define your own `@FunctionalInterface` with three arguments: +```java +@FunctionalInterface +public interface TriFunction { + R apply(T t, U u, V v); +} +``` diff --git a/src/module1_lambda/FunctionalInterfacesPractice.java b/src/module1_lambda/FunctionalInterfacesPractice.java new file mode 100644 index 0000000..b767e30 --- /dev/null +++ b/src/module1_lambda/FunctionalInterfacesPractice.java @@ -0,0 +1,108 @@ +package com.javatechie.lambda.demo.src.module1_lambda; + + + +import com.javatechie.lambda.demo.src.models.DataStore; +import com.javatechie.lambda.demo.src.models.Employee; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; + +/** + * Practice template for built-in functional interfaces: + * Predicate, Consumer, Supplier, Function, BiPredicate, BiConsumer, BiFunction. + */ +public class FunctionalInterfacesPractice { + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + + System.out.println("=== 1. Predicate Practice ==="); + // TODO: Create a Predicate that checks if an employee's salary is greater than 80,000 + Predicate highSalaryPredicate = emp -> false; + + // TODO: Create a Predicate that checks if an employee belongs to the "Development" department + Predicate devDeptPredicate = emp -> false; + + // TODO: Combine predicates (high salary AND development department) using .and() + Predicate highSalaryDevPredicate = emp -> false; + + System.out.println("Employees with high salary (>80k):"); + filterAndPrint(employees, highSalaryPredicate); + + System.out.println("\nEmployees in Development department with high salary:"); + filterAndPrint(employees, highSalaryDevPredicate); + + + System.out.println("\n=== 2. Consumer Practice ==="); + // TODO: Create a Consumer that prints employee name and salary in the format "Name: [name], Salary: [salary]" + Consumer printFormatConsumer = emp -> {}; + + // TODO: Create a Consumer that prints employee's assigned projects names + Consumer printProjectsConsumer = emp -> {}; + + // TODO: Chain consumers using .andThen() to print format first, then projects + Consumer chainedConsumer = emp -> {}; + + System.out.println("Executing chained consumer for first 3 employees:"); + employees.stream().limit(3).forEach(chainedConsumer); + + + System.out.println("\n=== 3. Supplier Practice ==="); + // TODO: Create a Supplier that returns a default Employee (id: 0, name: "Default Guest", department: "Guest", salary: 0) + Supplier defaultEmployeeSupplier = () -> null; + + Employee guestEmployee = defaultEmployeeSupplier.get(); + System.out.println("Default Guest Employee: " + guestEmployee); + + + System.out.println("\n=== 4. Function Practice ==="); + // TODO: Create a Function that extracts the name of an employee + Function nameExtractor = emp -> null; + + // TODO: Create a Function that converts a String to uppercase + Function toUpperCaseFn = str -> null; + + // TODO: Compose these functions using .andThen() to get the uppercase name of an Employee + Function upperCaseNameExtractor = emp -> null; + + System.out.println("Uppercase names of all employees:"); + for (Employee emp : employees) { + System.out.println(upperCaseNameExtractor.apply(emp)); + } + + + System.out.println("\n=== 5. Bi-Functional Interfaces Practice ==="); + // TODO: Create a BiPredicate to check if an employee's salary is greater than a given threshold + BiPredicate isSalaryAboveThreshold = (emp, threshold) -> false; + + // TODO: Create a BiConsumer to print employee name and a custom message prefix + BiConsumer printWithMessage = (emp, prefix) -> {}; + + // TODO: Create a BiFunction to calculate a new salary after applying a percentage raise (e.g. Employee, percentage -> double) + BiFunction calculateRaisedSalary = (emp, percent) -> 0.0; + + Employee sampleEmp = employees.get(0); + System.out.println("Is " + sampleEmp.getName() + " salary above 75k? " + isSalaryAboveThreshold.test(sampleEmp, 75000.0)); + printWithMessage.accept(sampleEmp, "Processing Record: "); + System.out.println("Salary after 10% raise: " + calculateRaisedSalary.apply(sampleEmp, 10.0)); + } + + /** + * Helper method to filter list of employees using a Predicate and print them. + */ + private static void filterAndPrint(List list, Predicate predicate) { + for (Employee emp : list) { + if (predicate.test(emp)) { + System.out.println(emp); + } + } + } +} diff --git a/src/module1_lambda/LambdaBasics.java b/src/module1_lambda/LambdaBasics.java new file mode 100644 index 0000000..0258eca --- /dev/null +++ b/src/module1_lambda/LambdaBasics.java @@ -0,0 +1,73 @@ +package com.javatechie.lambda.demo.src.module1_lambda; + +/** + * Practice template for custom functional interfaces using Lambda expressions. + */ +public class LambdaBasics { + + // 1. Custom Math Operation Interface + @FunctionalInterface + public interface MathOperation { + int operate(int a, int b); + } + + // 2. Custom String Formatter Interface + @FunctionalInterface + public interface StringFormatter { + String format(String input); + } + + // Helper method that uses MathOperation + public static int executeOperation(int a, int b, MathOperation op) { + return op.operate(a, b); + } + + // Helper method that uses StringFormatter + public static String formatString(String input, StringFormatter formatter) { + return formatter.format(input); + } + + public static void main(String[] args) { + System.out.println("=== 1. Custom Math Operations ==="); + + // TODO: Implement MathOperation for Addition using Lambda + MathOperation addition = (a, b) -> 0; // Replace with lambda expression + + // TODO: Implement MathOperation for Multiplication using Lambda + MathOperation multiplication = (a, b) -> 0; // Replace with lambda expression + + // TODO: Implement MathOperation for Division using Lambda (ensure you handle division by zero or assume b != 0 for now) + MathOperation division = (a, b) -> 0; // Replace with lambda expression + + System.out.println("Addition (10 + 5): " + executeOperation(10, 5, addition)); + System.out.println("Multiplication (10 * 5): " + executeOperation(10, 5, multiplication)); + System.out.println("Division (10 / 5): " + executeOperation(10, 5, division)); + + + System.out.println("\n=== 2. Custom String Formatters ==="); + + // TODO: Implement StringFormatter to convert input to uppercase + StringFormatter toUpperCase = input -> ""; // Replace with lambda expression + + // TODO: Implement StringFormatter to reverse the input string + StringFormatter reverse = input -> ""; // Replace with lambda expression + + // TODO: Implement StringFormatter to wrap input with brackets, e.g. "[input]" + StringFormatter bracketWrap = input -> ""; // Replace with lambda expression + + String rawText = "LambdaPractice"; + System.out.println("Uppercase: " + formatString(rawText, toUpperCase)); + System.out.println("Reversed: " + formatString(rawText, reverse)); + System.out.println("Bracket Wrapped: " + formatString(rawText, bracketWrap)); + + + System.out.println("\n=== 3. Scoping Rules and Variable Capture ==="); + + int multiplier = 5; + // TODO: Create a MathOperation using Lambda that multiplies 'a' and 'b' and then multiplies the result by the local variable 'multiplier' + // Check if you can modify the variable 'multiplier' after creating/executing the lambda. + MathOperation scaledMultiply = (a, b) -> 0; // Replace with lambda expression + + System.out.println("Scaled Multiplication (2 * 3 * 5): " + executeOperation(2, 3, scaledMultiply)); + } +} diff --git a/src/module2_streams/CONCEPT.md b/src/module2_streams/CONCEPT.md new file mode 100644 index 0000000..4d67fc6 --- /dev/null +++ b/src/module2_streams/CONCEPT.md @@ -0,0 +1,136 @@ +# Java 8 Streams API: Core Concepts, Architecture, & Advanced Mechanics + +The Stream API is one of the most significant features introduced in Java 8. It provides a declarative, functional programming approach to processing sequences of elements (e.g., collections, arrays, I/O resources) with support for parallel execution. + +--- + +## 1. The Stream Lifecycle +A Java Stream is not a data structure; it does not store elements. Instead, it conveys elements from a source through a pipeline of computational steps. The lifecycle of a stream consists of three distinct phases: + +```mermaid +graph LR + Source[1. Source] --> |Stream Elements| Intermediate[2. Intermediate Operations] + Intermediate --> |Configured Pipeline| Terminal[3. Terminal Operation] + Terminal --> |Triggers Execution| Result[Result / Side-Effects] +``` + +### Phase A: Source +A stream is initiated from a data source. Common sources include: +- **Collections**: `list.stream()`, `set.stream()` +- **Arrays**: `Arrays.stream(array)` +- **Static Factories**: `Stream.of(val1, val2)`, `Stream.iterate()`, `Stream.generate()` +- **File I/O**: `Files.lines(Paths.get(path))` +- **Primitive Streams**: `IntStream.range(1, 10)`, `LongStream`, `DoubleStream` + +### Phase B: Intermediate Operations +Intermediate operations transform a stream into another stream. They are **lazy**; they do not perform any processing until a terminal operation is invoked. +- **Stateless Operations**: Elements are processed independently without retaining state from previously seen elements. + - Examples: `filter()`, `map()`, `flatMap()`, `peek()` +- **Stateful Operations**: Must look at the entire dataset or maintain state across multiple elements before producing results. + - Examples: `distinct()`, `sorted()`, `limit()`, `skip()` + +### Phase C: Terminal Operation +A terminal operation triggers the execution of the entire pipeline, consumes the stream elements, and produces a result (e.g., a collection, a primitive value, an optional) or performs a side-effect (e.g., `forEach`). Once a terminal operation completes, the stream is **closed** and cannot be reused. +- Examples: `collect()`, `forEach()`, `reduce()`, `count()`, `findFirst()`, `anyMatch()`, `allMatch()`, `noneMatch()`, `min()`, `max()`. + +--- + +## 2. Stream Pipelines & Lazy Evaluation + +### Lazy Evaluation (Deferred Execution) +Intermediate operations are not executed when they are defined. Instead, they build a query plan or execution pipeline (internally represented as linked pipeline stages subclassing `AbstractPipeline`). + +Consider this code: +```java +List names = Arrays.asList("Alice", "Bob", "Charlie", "David"); +Stream stream = names.stream() + .filter(name -> { + System.out.println("Filter: " + name); + return name.length() > 3; + }) + .map(name -> { + System.out.println("Map: " + name); + return name.toUpperCase(); + }); + +System.out.println("Pipeline constructed. Triggering terminal operation..."); +List result = stream.collect(Collectors.toList()); +``` + +**Output:** +``` +Pipeline constructed. Triggering terminal operation... +Filter: Alice +Map: Alice +Filter: Bob +Filter: Charlie +Map: Charlie +Filter: David +Map: David +``` + +### Order of Execution & Vertical Integration +Unlike SQL or traditional collection transformations that process an entire collection stage-by-stage (horizontal evaluation), Java Streams process elements **vertically** (one element at a time through the entire pipeline), unless stateful operations prevent it. +In the output above: +1. `"Alice"` goes through `filter` (passes) -> goes through `map`. +2. `"Bob"` goes through `filter` (fails) -> execution for `"Bob"` stops immediately. It does not go to `map`. + +This vertical execution allows for **short-circuiting** optimizations. For instance, if you append `.findFirst()` or `.limit(1)` to the stream above, processing will stop as soon as the first matching element (`"Alice"`) completes the pipeline, completely ignoring the remaining elements (`"Charlie"`, `"David"`). + +--- + +## 3. Map vs. FlatMap + +| Feature | `map()` | `flatMap()` | +| :--- | :--- | :--- | +| **Operation Type** | One-to-One transformation. | One-to-Many transformation. | +| **Function Signature** | `Function` | `Function>` | +| **Output** | Returns a stream of transformed values (`Stream`). | Flattens multiple streams into a single stream (`Stream`). | +| **Use Case** | Extracting a property, converting types (e.g., `Employee -> String`). | Flattening nested collections (e.g., `Employee -> List -> Stream`). | + +### Visualizing Map vs. FlatMap + +``` +Map: +[ [1, 2], [3, 4] ] ===> map(x -> x) ===> [ [1, 2], [3, 4] ] (Nested Stream>) + +FlatMap: +[ [1, 2], [3, 4] ] ===> flatMap(List::stream) ===> [ 1, 2, 3, 4 ] (Flattened Stream) +``` + +--- + +## 4. Advanced 5+ YOE Interview Questions & Answers + +### Q1: What is the difference in memory and CPU footprint between stateless (e.g., `filter`, `map`) and stateful (e.g., `sorted`, `distinct`) operations? +**Answer:** +* **Stateless Operations (`filter`, `map`)**: Have an $O(1)$ memory overhead per element because they process elements one by one without needing to know what came before or what comes next. They are highly performant and extremely friendly to parallelization. +* **Stateful Operations (`sorted`, `distinct`, `limit`, `skip`)**: Have up to $O(N)$ memory and time overhead. + - For `sorted()`, the stream execution must block, buffering all elements from the upstream into an internal array structure to perform the sort before emitting any elements downstream. This can cause high memory consumption and latency spike. + - For `distinct()`, the stream must maintain an internal `HashSet` of seen elements to check duplicates. If the stream is infinite or extremely large, this will lead to an `OutOfMemoryError` or high memory pressure. + +### Q2: What happens if you attempt to reuse a stream? How does the JVM enforce this? +**Answer:** +A stream is a one-use-only object. If you attempt to invoke an intermediate or terminal operation on a stream that has already started execution or has completed its terminal operation, the JVM throws an `IllegalStateException` with the message `"stream has already been operated upon or closed"`. +This is enforced internally by a boolean state flag `linkedOrConsumed` inside the `AbstractPipeline` abstract class. When any pipeline stage or terminal action is initiated, this flag is set to `true`. Any subsequent method call on the same stream reference checks this flag and throws `IllegalStateException` if it is already `true`. + +### Q3: Explain how intermediate and terminal operations are chained under the hood. How does Java compile the pipeline? +**Answer:** +Under the hood, intermediate operations construct a linked list of pipeline stages. Each stage is represented by an implementation of the `PipelineHelper` and `Sink` interface. +- A `Sink` is a consumer that receives elements from upstream, processes them, and pushes them downstream using three primary methods: + - `begin(long size)`: Signals that elements are coming, allowing stateful operations to allocate memory. + - `accept(T value)`: Processes an individual element. + - `end()`: Signals that all elements have been processed, triggering stateful operations (like sorting or reduction) to output their buffered data. +- When a terminal operation is called, the stream framework travels upstream to compile a single chained execution loop of `Sink` instances. This chain is executed as a single pass over the elements, avoiding overhead and enabling JIT compilation to optimize the entire traversal. + +### Q4: Does `flatMap` have any memory impact or performance concerns compared to iterative nested loops? +**Answer:** +Yes. `flatMap` requires returning a `Stream` for each element of the outer stream. This means that if you have a stream of 100,000 employees and you flatmap their projects, the JVM will instantiate 100,000 temporary `Stream` objects. +This object allocation causes garbage collection (GC) pressure. For high-performance, low-latency code, nesting traditional loops or using custom collectors might be faster than `flatMap` due to the overhead of short-lived `Stream` allocations. + +### Q5: How do short-circuiting operations (e.g., `anyMatch`, `findFirst`, `limit`) behave in parallel streams? +**Answer:** +In a sequential stream, short-circuiting is straightforward: as soon as a condition is satisfied, the loop terminates. +In a parallel stream (which utilizes the ForkJoinPool), short-circuiting is much more complex: +- If one thread finds a match in `anyMatch`, it sets a shared cancelation flag to notify other threads to stop their work early. However, due to thread scheduling, other threads may continue computing for some time before checking the flag. +- For order-sensitive operations like `findFirst()` or `limit()`, the parallel stream must respect the encounter order of the source. Even if a thread processing a partition later in the stream finishes first, the framework must wait or buffer results to ensure the actual first element in the original sequence is returned. Thus, using `findFirst` in parallel streams can hurt performance significantly compared to `findAny()`, which has no order constraint. diff --git a/src/module2_streams/SortingPractice.java b/src/module2_streams/SortingPractice.java new file mode 100644 index 0000000..ae5a686 --- /dev/null +++ b/src/module2_streams/SortingPractice.java @@ -0,0 +1,94 @@ +package com.javatechie.lambda.demo.src.module2_streams; + + +import com.javatechie.lambda.demo.src.models.DataStore; +import com.javatechie.lambda.demo.src.models.Employee; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Exercise class for sorting collections using Stream API, lambdas, and Comparator. + * Practices sorting collections and maps with double comparisons, multi-key sorting, and reverse ordering. + * + * Note: Do not write implementation code. The methods must return placeholder values (like null or empty collections) + * so that they compile. Write comments explaining what the user needs to implement. + */ +public class SortingPractice { + + /** + * Exercise 1: Basic sorting of a list of objects by a double attribute (Ascending) + * Task: Sort the list of employees by their salary in ascending order. + * Return a list of sorted Employee objects. + */ + public static List sortEmployeesBySalaryAsc(List employees) { + // TODO: Implement this method using Stream API + // Hint: sorted(Comparator.comparingDouble(Employee::getSalary)) + return Collections.emptyList(); + } + + /** + * Exercise 2: Basic sorting of a list of objects by a double attribute (Descending / Reverse) + * Task: Sort the list of employees by their salary in descending order. + * Return a list of sorted Employee objects. + */ + public static List sortEmployeesBySalaryDesc(List employees) { + // TODO: Implement this method using Stream API + // Hint: sorted(Comparator.comparingDouble(Employee::getSalary).reversed()) + return Collections.emptyList(); + } + + /** + * Exercise 3: Multi-key sorting + * Task: Sort employees first by their Department (dept) alphabetically (ascending), + * and then by their Salary in descending order if they are in the same department. + * Return a list of sorted Employee objects. + */ + public static List sortEmployeesByDeptThenSalaryDesc(List employees) { + // TODO: Implement this method using Stream API + // Hint: Use Comparator.comparing(Employee::getDept).thenComparing(..., Comparator.reverseOrder()) + // or chaining comparingDouble and reversed(). + return Collections.emptyList(); + } + + /** + * Exercise 4: Sorting a Map by Key + * Task: Given a Map of book title to rating (Integer), sort this Map by its Keys (alphabetically) + * and return a list of Map Entries sorted by Key. + */ + public static List> sortBookRatingsByKey(Map ratings) { + // TODO: Implement this method using Stream API + // Hint: ratings.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(...) + return Collections.emptyList(); + } + + /** + * Exercise 5: Sorting a Map by Value (Descending) + * Task: Given a Map of book title to rating (Integer), sort this Map by its Values in descending order. + * Return a list of Map Entries sorted by value descending. + */ + public static List> sortBookRatingsByValueDesc(Map ratings) { + // TODO: Implement this method using Stream API + // Hint: sorted(Map.Entry.comparingByValue().reversed()) + return Collections.emptyList(); + } + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + + Map bookRatings = Map.of( + "Core Java", 5, + "Hibernate", 3, + "Spring Boot", 4, + "WebService", 2 + ); + + System.out.println("Sorting exercises loaded!"); + System.out.println("Employees sorted by salary ASC: " + sortEmployeesBySalaryAsc(employees)); + System.out.println("Employees sorted by salary DESC: " + sortEmployeesBySalaryDesc(employees)); + System.out.println("Employees sorted by Dept then Salary DESC: " + sortEmployeesByDeptThenSalaryDesc(employees)); + System.out.println("Book ratings sorted by Key: " + sortBookRatingsByKey(bookRatings)); + System.out.println("Book ratings sorted by Value DESC: " + sortBookRatingsByValueDesc(bookRatings)); + } +} diff --git a/src/module2_streams/StreamBasics.java b/src/module2_streams/StreamBasics.java new file mode 100644 index 0000000..b81c273 --- /dev/null +++ b/src/module2_streams/StreamBasics.java @@ -0,0 +1,108 @@ +package com.javatechie.lambda.demo.src.module2_streams; + +import com.javatechie.lambda.demo.src.models.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Exercise class for basic Stream operations: map, filter, flatMap, collect, distinct, limit, skip. + * Use the DataStore models to practice these operations. + * + * Note: Do not write implementation code. The methods must return placeholder values (like null or empty collections) + * so that they compile. Write comments explaining what the user needs to implement. + */ +public class StreamBasics { + + /** + * Exercise 1: Filter and Map + * Task: Filter employees who belong to the "Development" department and have a salary greater than 80000. + * Map their names and collect them into a List of Strings. + */ + public static List getHighPaidDevNames(List employees) { + // TODO: Implement this method using Stream API + // Hint: filter -> map -> collect + return Collections.emptyList(); + } + + /** + * Exercise 2: FlatMap and Distinct (Objects) + * Task: Retrieve a list of all unique Projects associated with all employees. + * Since each Employee has a List of Projects, you need to use flatMap to flatten the stream, + * then use distinct to filter duplicates, and collect them to a List. + */ + public static List getUniqueProjects(List employees) { + // TODO: Implement this method using Stream API + // Hint: flatMap(e -> e.getProjects().stream()) -> distinct() -> collect + return Collections.emptyList(); + } + + /** + * Exercise 3: FlatMap (Strings) + * Task: Retrieve a list of all phone numbers from all customers. + * Flatten the list of phone numbers for each customer into a single list of Strings. + */ + public static List getAllCustomerPhoneNumbers(List customers) { + // TODO: Implement this method using Stream API + return Collections.emptyList(); + } + + /** + * Exercise 4: Limit and Sort + * Task: Get the names of the top N students based on their rank (smaller rank is better, e.g., rank 1 is top). + * Sort the stream by student rank, limit to N, map to their first names, and collect to a List. + */ + public static List getTopNStudentNames(List students, int n) { + // TODO: Implement this method using Stream API + // Hint: sorted(Comparator.comparingInt(Student::getRank)) -> limit(n) -> map(Student::getFirstName) -> collect + return Collections.emptyList(); + } + + /** + * Exercise 5: Skip and Limit pagination simulation + * Task: Skip the first 'skipCount' students and then limit the result to 'pageSize' students. + * Collect them to a List of Students. + */ + public static List getStudentsPaged(List students, int skipCount, int pageSize) { + // TODO: Implement this method using Stream API + return Collections.emptyList(); + } + + /** + * Exercise 6: Distinct (Strings) + * Task: Get the set of all unique cities where students reside. + * Collect the result into a Set. + */ + public static Set getDistinctStudentCities(List students) { + // TODO: Implement this method using Stream API + return Collections.emptySet(); + } + + /** + * Exercise 7: Count Unique Contacts + * Task: Filter students who belong to "Mechanical Engineering" department. + * Extract their contacts (which is a list of Strings). + * Count the total number of unique contacts across all mechanical engineering students. + */ + public static long countUniqueMechanicalStudentContacts(List students) { + // TODO: Implement this method using Stream API + return 0L; + } + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + List customers = DataStore.getAllCustomers(); + List students = DataStore.getAllStudents(); + + System.out.println("Exercises loaded. Try implementing the solutions!"); + System.out.println("High Paid Devs Names: " + getHighPaidDevNames(employees)); + System.out.println("Unique Projects count: " + getUniqueProjects(employees).size()); + System.out.println("All Customer Phones: " + getAllCustomerPhoneNumbers(customers)); + System.out.println("Top 3 Students: " + getTopNStudentNames(students, 3)); + System.out.println("Paged Students (skip 2, limit 3): " + getStudentsPaged(students, 2, 3)); + System.out.println("Distinct Student Cities: " + getDistinctStudentCities(students)); + System.out.println("Unique Mechanical Contacts Count: " + countUniqueMechanicalStudentContacts(students)); + } +} diff --git a/src/module3_advanced_streams/AdvancedStreams.java b/src/module3_advanced_streams/AdvancedStreams.java new file mode 100644 index 0000000..28a3854 --- /dev/null +++ b/src/module3_advanced_streams/AdvancedStreams.java @@ -0,0 +1,75 @@ +package com.javatechie.lambda.demo.src.module3_advanced_streams; + +import com.javatechie.lambda.demo.src.models.DataStore; +import com.javatechie.lambda.demo.src.models.Employee; +import com.javatechie.lambda.demo.src.models.Student; + +import java.util.*; + +/** + * Advanced Stream API Exercises targeting 5+ Years of Experience. + * Implement the methods below using Java 8 streams, collectors, groupingBy, partitioningBy, and custom collectors. + * DO NOT use external libraries. + */ +public class AdvancedStreams { + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + List students = DataStore.getAllStudents(); + + System.out.println("1. Employees grouped by Dept then Gender: " + groupEmployeesByDeptThenGender(employees)); + System.out.println("2. Highest paid employee in each Dept: " + getHighestPaidEmployeeInEachDept(employees)); + System.out.println("3. Students partitioned by top rank (< 100): " + partitionStudentsByRank(students)); + System.out.println("4. Custom representation of names: " + mapNamesToCustomStringRepresentation(employees)); + System.out.println("5. Custom collector join strings: " + joinStringsWithCustomCollector(Arrays.asList("Java", "Python", "Go", "Rust"))); + } + + /** + * Exercise 1: Group employees by Department, and then nested-group them by Gender. + * Expected Output Map structure: Map>> + */ + public static Map>> groupEmployeesByDeptThenGender(List employees) { + // TODO: Implement groupingBy department and nested groupingBy gender + return Collections.emptyMap(); + } + + /** + * Exercise 2: Find the highest-paid employee in each department. + * Avoid returning Optional inside the map values. Return the Employee object directly. + * Hint: Use Collectors.collectingAndThen along with Collectors.maxBy. + */ + public static Map getHighestPaidEmployeeInEachDept(List employees) { + // TODO: Find the highest-paid employee in each department using downstream collectors + return Collections.emptyMap(); + } + + /** + * Exercise 3: Partition students into two groups: + * - Top Rankers: rank < 100 (key: true) + * - Others: rank >= 100 (key: false) + */ + public static Map> partitionStudentsByRank(List students) { + // TODO: Partition students by rank (< 100) + return Collections.emptyMap(); + } + + /** + * Exercise 4: Convert the names of all employees into a custom formatted string: + * "[Name1 | Name2 | Name3]" + * Hint: Use Collectors.joining with delimiter, prefix, and suffix. + */ + public static String mapNamesToCustomStringRepresentation(List employees) { + // TODO: Collect employee names and join them in the specified format + return ""; + } + + /** + * Exercise 5: Implement a custom Collector using Collector.of() that joins a list of strings + * with a hyphen "-" delimiter, skipping empty/null strings. + * Do not use the built-in Collectors.joining() directly. + */ + public static String joinStringsWithCustomCollector(List strings) { + // TODO: Define a custom collector using Collector.of to join non-empty strings with a hyphen "-" + return ""; + } +} diff --git a/src/module3_advanced_streams/CONCEPT.md b/src/module3_advanced_streams/CONCEPT.md new file mode 100644 index 0000000..dbbc52d --- /dev/null +++ b/src/module3_advanced_streams/CONCEPT.md @@ -0,0 +1,108 @@ +# Module 3: Advanced Streams + +## 1. Grouping and Partitioning + +Grouping and partitioning allow you to classify elements of a stream into categories, resulting in a `Map`. + +### `Collectors.groupingBy` +The `groupingBy` collector is used to group elements by a classifier function. There are three overloaded variants of `groupingBy`: + +1. **One-parameter**: `groupingBy(classifier)` + - Groups elements by the classifier. The map values are `List`. + - Example: `groupingBy(Employee::getDept)` -> `Map>` +2. **Two-parameter**: `groupingBy(classifier, downstreamCollector)` + - Groups elements by the classifier and processes the values of each group using a downstream collector. + - Example: `groupingBy(Employee::getDept, Collectors.counting())` -> `Map` +3. **Three-parameter**: `groupingBy(classifier, mapFactory, downstreamCollector)` + - Allows you to specify a custom `Map` implementation (e.g., `TreeMap::new` for sorted keys). + - Example: `groupingBy(Employee::getDept, TreeMap::new, Collectors.toList())` -> `TreeMap>` + +### `Collectors.partitioningBy` +Partitioning is a special case of grouping where the classifier is a `Predicate`. The resulting map always contains two keys: `true` and `false`, with values representing elements that match or do not match the predicate. + +1. **One-parameter**: `partitioningBy(predicate)` -> `Map>` +2. **Two-parameter**: `partitioningBy(predicate, downstreamCollector)` -> `Map` + +--- + +## 2. Downstream Collectors + +Downstream collectors are crucial for nesting and aggregating grouped data. Some of the most powerful downstream collectors include: + +- **`mapping(mapper, downstream)`**: Transforms elements before passing them to the downstream collector. + ```java + Map> deptEmployeeNames = employees.stream() + .collect(groupingBy(Employee::getDept, mapping(Employee::getName, toSet()))); + ``` +- **`flatMapping(mapper, downstream)`**: Flattens nested collections before collecting them (Java 9+). +- **`filtering(predicate, downstream)`**: Filters elements within each group before collection (Java 9+). +- **`reducing(identity, mapper, op)`**: Performs a reduction on the elements of each group. +- **`maxBy(comparator)` / `minBy(comparator)`**: Finds the maximum or minimum element in each group. Returns a `Map>`. +- **`collectingAndThen(downstream, finisher)`**: Adapts a collector to perform an additional finishing transformation. + ```java + // Find highest-paid employee in each department (without Optional wrapper) + Map topPaidInDept = employees.stream() + .collect(groupingBy( + Employee::getDept, + collectingAndThen(maxBy(Comparator.comparingDouble(Employee::getSalary)), Optional::get) + )); + ``` + +--- + +## 3. Custom Collectors + +When built-in collectors are insufficient, you can implement your own by implementing the `java.util.stream.Collector` interface, which defines five functions: + +1. **`Supplier supplier()`**: Creates and returns a new mutable result container (e.g., `ArrayList::new`). +2. **`BiConsumer accumulator()`**: Folds a new element into the mutable result container. +3. **`BinaryOperator combiner()`**: Combines two partial results into one (used in parallel stream processing). +4. **`Function finisher()`**: Transforms the intermediate container into the final result type. If `Characteristics.IDENTITY_FINISH` is set, this function can just be `Function.identity()`. +5. **`Set characteristics()`**: Returns a set of characteristics specifying the collector's capabilities (e.g., `CONCURRENT`, `UNORDERED`, `IDENTITY_FINISH`). + +### Example: Custom Collector for String Joins +```java +Collector commaJoiner = Collector.of( + StringBuilder::new, // supplier + (sb, s) -> { if (sb.length() > 0) sb.append(","); sb.append(s); }, // accumulator + (sb1, sb2) -> { // combiner + if (sb1.length() > 0 && sb2.length() > 0) sb1.append(","); + return sb1.append(sb2); + }, + StringBuilder::toString // finisher +); +``` + +--- + +## 4. Reducing: Stream.reduce vs Collectors.reducing + +- **`Stream.reduce()`** is a terminal operation on a Stream. It is designed for immutable reductions (e.g., summing integers, combining strings). +- **`Collectors.reducing()`** is designed to be used as a downstream collector inside `groupingBy` or `partitioningBy`. +- **Mutable vs Immutable Reduction**: Do not use `reduce` for mutable operations like adding elements to an `ArrayList`. That is what `Stream.collect` (mutable reduction) is optimized for. Using `reduce` for mutable structures in parallel streams can lead to thread-safety issues or terrible performance because of copying. + +--- + +## 5. Advanced Interview Patterns (5+ YOE) + +### Q1: How do you achieve Dense Ranking in Java Streams? +Dense ranking means ranking items by a value where items with equal values receive the same rank, and the next rank is consecutive (e.g., salaries 100k (1), 100k (1), 90k (2)). +To implement this cleanly, you can: +1. Sort the unique values in descending order. +2. Store their rank in a map or look up the index in the sorted unique list. +3. Or group by value, sort the entry keys, and assign rank based on position. + +### Q2: What is Stream Performance Optimization & Characteristics? +Streams utilize metadata flag characteristics defined in `Spliterator` (e.g., `ORDERED`, `DISTINCT`, `SORTED`, `SIZED`). +- If a stream knows it is `DISTINCT`, calling `distinct()` becomes a no-op. +- If a stream is `SORTED` and you call `sorted()`, it skips sorting. +- If a stream is `SIZED`, operations like `toArray` can allocate the exact size, avoiding resizing. + +**Performance Tips**: +- Avoid **boxing/unboxing overhead**: Use `IntStream`, `LongStream`, and `DoubleStream` instead of `Stream`, `Stream`, and `Stream`. +- **Filter early**: Place filter operations as close to the source as possible to reduce the number of elements processed downstream. +- **Limit stateful operations**: Operations like `distinct()`, `sorted()`, `limit()`, and `skip()` are stateful and require buffering all elements before producing output, which can hurt performance, especially in parallel streams. +- **Parallel stream overhead**: Only use parallel streams when: + 1. The data size is large (e.g., N > 10,000). + 2. The computation per element is heavy (high Q-value). + 3. The source splits easily (e.g., `ArrayList` is excellent, `LinkedList` or `BufferedReader.lines` splits poorly). diff --git a/src/module3_advanced_streams/NthHighestSalaryPractice.java b/src/module3_advanced_streams/NthHighestSalaryPractice.java new file mode 100644 index 0000000..48553b2 --- /dev/null +++ b/src/module3_advanced_streams/NthHighestSalaryPractice.java @@ -0,0 +1,59 @@ +package com.javatechie.lambda.demo.src.module3_advanced_streams; + + + +import com.javatechie.lambda.demo.src.models.DataStore; +import com.javatechie.lambda.demo.src.models.Employee; + +import java.util.*; +import java.util.stream.*; + +/** + * Exercises to find the N-th highest salary (dynamic value) using Streams. + * This covers grouping, sorting, and dense ranking scenarios. + */ +public class NthHighestSalaryPractice { + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + + System.out.println("2nd Highest Unique Salary: " + getNthHighestUniqueSalary(employees, 2)); + System.out.println("Employees with 2nd Highest Salary: " + getEmployeesWithNthHighestSalary(employees, 2)); + System.out.println("2nd Highest Salary by Dept: " + getNthHighestSalaryByDepartment(employees, 2)); + } + + /** + * Exercise 1: Find the N-th highest unique salary from the list of employees. + * For example, given salaries: [95k, 90k, 88k, 88k, 85k] + * - 1st highest: 95k + * - 2nd highest: 90k + * - 3rd highest: 88k (duplicates should be treated as one rank, i.e. dense ranking) + * - 4th highest: 85k + * + * If N is invalid or out of bounds, return Optional.empty(). + */ + public static Optional getNthHighestUniqueSalary(List employees, int n) { + // TODO: Implement dense ranking to find the N-th highest unique salary + return Optional.empty(); + } + + /** + * Exercise 2: Find all employees who earn the N-th highest salary. + * Note: Multiple employees can have the same salary, so return a List of those Employees. + * If no such salary exists, return an empty list. + */ + public static List getEmployeesWithNthHighestSalary(List employees, int n) { + // TODO: Find the N-th highest salary, and then filter employees matching that salary + return Collections.emptyList(); + } + + /** + * Exercise 3: Find the N-th highest salary in each department. + * Return a Map where the key is the department name and the value is the N-th highest salary in that department. + * If a department has fewer than N unique salaries, omit it from the result map or set the value to 0.0. + */ + public static Map getNthHighestSalaryByDepartment(List employees, int n) { + // TODO: Group by department and find the N-th highest salary in each department + return Collections.emptyMap(); + } +} diff --git a/src/module4_optional/CONCEPT.md b/src/module4_optional/CONCEPT.md new file mode 100644 index 0000000..40f6bbd --- /dev/null +++ b/src/module4_optional/CONCEPT.md @@ -0,0 +1,142 @@ +# Module 4: Java 8 Optional + +## 1. Design Goal of `Optional` +`java.util.Optional` was introduced in Java 8 as a container object which may or may not contain a non-null value. + +### Core Design Philosophy +The primary goal of `Optional` is to **provide a clear, type-level representation of an optional return value** to prevent `NullPointerException` (NPE) and encourage developers to explicitly handle the absence of a value rather than relying on `null` references. + +> [!IMPORTANT] +> **What Optional is NOT:** +> * **Not a replacement for every null reference:** It is not designed to replace all nulls in Java. +> * **Not for parameters:** Passing `Optional` as a method parameter adds boilerplate to the caller (who must wrap values) and violates clean coding principles. Use method overloading or nullable parameters instead. +> * **Not for class fields / serialization:** `Optional` does not implement `java.io.Serializable`. Storing it in class fields will break standard Java serialization and increase the memory footprint of objects. +> * **Not for collection elements:** Never use `List>` or `Map>`. Instead, represent the absence of a value in collections by omitting the key/value or using an empty collection/null check at the boundary. + +--- + +## 2. Creating Optionals +There are three static factory methods to instantiate an `Optional`: + +* **`Optional.empty()`**: Returns an empty `Optional` instance. +* **`Optional.of(value)`**: Returns an `Optional` containing the specified non-null value. If the value is `null`, it immediately throws a `NullPointerException`. +* **`Optional.ofNullable(value)`**: Returns an `Optional` containing the value if non-null, otherwise returns an empty `Optional`. + +```java +String name = getNullableName(); + +// Safe creation: +Optional optName = Optional.ofNullable(name); + +// Unsafe creation (will throw NPE if name is null): +Optional optNameUnsafe = Optional.of(name); +``` + +--- + +## 3. Extracting and Handling Values + +### `orElse` vs `orElseGet` (Lazy vs Eager) +These two methods provide a default value when the `Optional` is empty, but they evaluate their parameters differently. + +| Method | Signature | Parameter Evaluation | Recommended Use Case | +| :--- | :--- | :--- | :--- | +| **`orElse(T other)`** | `public T orElse(T other)` | **Eager**: The parameter is evaluated *regardless* of whether the Optional contains a value or not. | Constant, pre-instantiated default values or primitives. | +| **`orElseGet(Supplier other)`** | `public T orElseGet(Supplier other)` | **Lazy**: The `Supplier` is executed *only* if the Optional is empty. | Database calls, heavy object instantiations, web service calls. | + +#### Example of Evaluation Difference: +```java +// Suppose we have a method that logs and creates a default item: +public static String getDefault() { + System.out.println("getDefault() invoked!"); + return "Default Value"; +} + +Optional optional = Optional.of("Present Value"); + +// 1. orElse: getDefault() IS executed even though the Optional has a value! +String val1 = optional.orElse(getDefault()); +// Prints: "getDefault() invoked!" + +// 2. orElseGet: getDefault() is NOT executed because the Optional has a value. +String val2 = optional.orElseGet(() -> getDefault()); +// Prints nothing. +``` + +### `orElseThrow` +Used when the absence of a value is an exceptional condition. In Java 8, it takes a supplier generating the exception: +```java +User user = optUser.orElseThrow(() -> new UserNotFoundException("User not found")); +``` + +--- + +## 4. Transforming and Filtering Optionals + +### `map` vs `flatMap` +Both methods are used to transform values inside an `Optional`, but they handle functions returning other `Optional` instances differently. + +#### `map` +If the mapper function returns a raw value `U`, `map` automatically wraps the result into an `Optional`. +* **Signature**: `public Optional map(Function mapper)` +```java +Optional user = getUser(); +Optional username = user.map(User::getName); // Wraps String into Optional +``` + +#### `flatMap` +If the mapper function itself returns an `Optional`, using `map` would result in a nested `Optional>`. `flatMap` flattens the result into a single `Optional`. +* **Signature**: `public Optional flatMap(Function> mapper)` +```java +public class User { + public Optional
getAddress() { ... } +} + +Optional user = getUser(); +// Use flatMap to avoid Optional> +Optional
address = user.flatMap(User::getAddress); +``` + +### `filter` +Allows you to conditionally keep or discard the value inside the `Optional`. If the value matches the predicate, it is retained; otherwise, an empty `Optional` is returned. +```java +Optional activeUser = optUser.filter(User::isActive); +``` + +--- + +## 5. Senior-Level Interview Tips (5+ YOE) + +### Q1: What is the overhead of using `Optional`? When should we avoid it for performance-critical code? +`Optional` is a reference type. Wrapping a value in an `Optional` creates an extra object allocation on the heap, adding memory footprint and garbage collection pressure. +* **Avoid in tight loops or high-frequency methods**: If a method is called millions of times per second, returning `Optional` can significantly degrade performance. Use `null` or sentinel values instead, and document the behavior. +* **Avoid wrapping primitives**: Use specialized primitive optional classes (`OptionalInt`, `OptionalLong`, `OptionalDouble`) instead of `Optional`, etc., to avoid the dual overhead of wrapper class allocation and boxing. + +### Q2: Why shouldn't `Optional` be used in Domain Model Fields? +1. **Serialization**: `Optional` does not implement `Serializable`. If your domain models are sent over RMI, saved via some legacy caching engines, or serialized directly, it will throw `NotSerializableException`. +2. **JPA / Hibernate Mapping**: Hibernate does not naturally map `Optional` fields to database columns out-of-the-box without custom AttributeConverters, making code unnecessarily complex. +* **Alternative pattern**: Define the class field as nullable, and expose the getter returning an `Optional`: +```java +public class Employee { + private String middleName; // Nullable database field + + public Optional getMiddleName() { + return Optional.ofNullable(middleName); // Safe Optional API exposed to clients + } +} +``` + +### Q3: What is the `Optional` API anti-pattern "Coding style: Imperative in Functional wrapper"? +A common anti-pattern is using `Optional` like a structured null check using `isPresent()` and `get()`: +```java +// Anti-pattern (essentially identical to if-null-check): +Optional user = findUser(); +if (user.isPresent()) { + System.out.println(user.get().getName()); +} + +// Recommended Functional Approach: +findUser().map(User::getName) + .ifPresent(System.out::println); +``` +Avoid calling `get()` without first calling `isPresent()`. In Java 8, `orElseThrow(Supplier)` is preferred over `get()` to make the potential failure explicit and prevent standard NoSuchElementException without a useful message. diff --git a/src/module4_optional/OptionalPractice.java b/src/module4_optional/OptionalPractice.java new file mode 100644 index 0000000..6a19519 --- /dev/null +++ b/src/module4_optional/OptionalPractice.java @@ -0,0 +1,172 @@ +package com.javatechie.lambda.demo.src.module4_optional; + + +import com.javatechie.lambda.demo.src.models.DataStore; +import com.javatechie.lambda.demo.src.models.Employee; +import com.javatechie.lambda.demo.src.models.Project; + +import java.util.*; + +/** + * Practice exercises for Java 8 Optional class. + * Learn to write code avoiding NullPointerExceptions and refactor legacy null checks. + */ +public class OptionalPractice { + + // Helper classes for nested Optional exercises (Computer -> Soundcard -> USB) + public static class Computer { + private final Soundcard soundcard; + public Computer(Soundcard soundcard) { this.soundcard = soundcard; } + public Optional getSoundcard() { return Optional.ofNullable(soundcard); } + } + + public static class Soundcard { + private final USB usb; + public Soundcard(USB usb) { this.usb = usb; } + public Optional getUsb() { return Optional.ofNullable(usb); } + } + + public static class USB { + private final String version; + public USB(String version) { this.version = version; } + public String getVersion() { return version; } + } + + // Helper method that simulates an expensive db lookup or creation + private static String getExpensiveDefault() { + System.out.println(">>> getExpensiveDefault() was executed!"); + return "System Default Project"; + } + + /** + * Task 1: Safe Mapping & flatMapping with Optional + * Get the USB version of the Computer. If the computer, soundcard, or USB is missing, return "UNKNOWN". + * Hint: Use flatMap when the method returns an Optional, and map when it returns a raw object. + */ + public static String getUsbVersion(Optional computer) { + // TODO: Implement using flatMap and map on the Optional, defaulting to "UNKNOWN" + return "UNKNOWN"; + } + + /** + * Task 2: findAny/findFirst with Optional + * Find an Employee from the list by their ID. + * Return an Optional containing the Employee, or Optional.empty() if not found. + */ + public static Optional findEmployeeById(List employees, int id) { + // TODO: Implement using Stream API and findFirst or findAny + return Optional.empty(); + } + + /** + * Task 3: orElseThrow Exception + * Find an Employee by ID. If found, return their name. If not, throw an IllegalArgumentException with message "Employee not found". + * Hint: Use Optional.orElseThrow(...) + */ + public static String getEmployeeNameOrThrow(List employees, int id) { + // TODO: Find the employee and map to name, or throw IllegalArgumentException + return ""; + } + + /** + * Task 4: orElse vs orElseGet (Evaluation differences) + * Demonstrates execution of default values. + * Use orElse and orElseGet with 'getExpensiveDefault()' on a PRESENT optional and an EMPTY optional. + */ + public static void runOrElseDemo() { + Optional presentOptional = Optional.of("Alpha Project"); + Optional emptyOptional = Optional.empty(); + + System.out.println("--- Testing orElse with PRESENT Optional ---"); + // TODO: Retrieve value or use orElse(getExpensiveDefault()) + String val1 = ""; + System.out.println("Result: " + val1); + + System.out.println("\n--- Testing orElseGet with PRESENT Optional ---"); + // TODO: Retrieve value or use orElseGet(() -> getExpensiveDefault()) + String val2 = ""; + System.out.println("Result: " + val2); + + System.out.println("\n--- Testing orElse with EMPTY Optional ---"); + // TODO: Retrieve value or use orElse(getExpensiveDefault()) + String val3 = ""; + System.out.println("Result: " + val3); + + System.out.println("\n--- Testing orElseGet with EMPTY Optional ---"); + // TODO: Retrieve value or use orElseGet(() -> getExpensiveDefault()) + String val4 = ""; + System.out.println("Result: " + val4); + } + + /** + * Task 5: Refactoring Legacy Null Checks + * Here is a typical legacy nested null check method. Refactor it using Optional. + */ + public static String getProjectClientLegacy(Employee employee) { + if (employee != null) { + List projects = employee.getProjects(); + if (projects != null && !projects.isEmpty()) { + Project firstProject = projects.get(0); + if (firstProject != null) { + String client = firstProject.getClient(); + if (client != null) { + return client.toUpperCase(); + } + } + } + } + return "UNKNOWN CLIENT"; + } + + /** + * Task 5 (Refactored): Refactor the above method to use Optional pipelines. + * Do NOT use any 'if' checks. + */ + public static String getProjectClientOptional(Employee employee) { + // TODO: Refactor using Optional.ofNullable, map, filter, etc. + return "UNKNOWN CLIENT"; + } + + public static void main(String[] args) { + List employees = DataStore.getAllEmployees(); + + System.out.println("=== Task 1: USB Version Lookup ==="); + USB usb = new USB("3.1"); + Soundcard soundcard = new Soundcard(usb); + Computer computer = new Computer(soundcard); + + System.out.println("USB Version (Valid Computer): " + getUsbVersion(Optional.of(computer))); + System.out.println("USB Version (No USB): " + getUsbVersion(Optional.of(new Computer(new Soundcard(null))))); + System.out.println("USB Version (No Soundcard): " + getUsbVersion(Optional.of(new Computer(null)))); + System.out.println("USB Version (Empty): " + getUsbVersion(Optional.empty())); + + + System.out.println("\n=== Task 2 & 3: Finding Elements & Throwing Exceptions ==="); + Optional empOpt = findEmployeeById(employees, 5); + empOpt.ifPresent(e -> System.out.println("Found Employee 5: " + e.getName())); + + try { + System.out.println("Employee 2 Name: " + getEmployeeNameOrThrow(employees, 2)); + System.out.println("Employee 99 Name: " + getEmployeeNameOrThrow(employees, 99)); + } catch (Exception e) { + System.out.println("Caught Expected Exception: " + e.getMessage()); + } + + + System.out.println("\n=== Task 4: orElse vs orElseGet Demo ==="); + runOrElseDemo(); + + + System.out.println("\n=== Task 5: Refactoring Null Checks ==="); + Employee legacyEmp = employees.get(0); // Has projects with client + Employee noProjEmp = new Employee(11, "No Project Dev", "Development", null, 60000, "Male"); + + System.out.println("Legacy (Normal): " + getProjectClientLegacy(legacyEmp)); + System.out.println("Legacy (Null Projects): " + getProjectClientLegacy(noProjEmp)); + System.out.println("Legacy (Null Employee): " + getProjectClientLegacy(null)); + + System.out.println("Optional (Normal): " + getProjectClientOptional(legacyEmp)); + System.out.println("Optional (Null Projects): " + getProjectClientOptional(noProjEmp)); + System.out.println("Optional (Null Employee): " + getProjectClientOptional(null)); + } +} diff --git a/src/module5_concurrency/CONCEPT.md b/src/module5_concurrency/CONCEPT.md new file mode 100644 index 0000000..7ed7927 --- /dev/null +++ b/src/module5_concurrency/CONCEPT.md @@ -0,0 +1,97 @@ +# Module 5: Concurrency - CompletableFuture and Parallel Streams + +This module covers Java 8's powerful asynchronous programming capabilities using `CompletableFuture`, along with parallel streams and thread synchronization techniques. + +--- + +## 1. Asynchronous Programming with CompletableFuture + +Prior to Java 8, asynchronous programming relied on `java.util.concurrent.Future`. While `Future` represented the result of an asynchronous computation, it had severe limitations: +- **Blocking `get()`:** You could not obtain the result without blocking the calling thread until the result was available. +- **No Callback Support:** You could not attach callbacks to run automatically when the task completed. +- **No Chaining/Pipelines:** You could not easily chain multiple asynchronous tasks together (e.g., output of Task A becomes input of Task B). +- **No Manual Completion:** You could not manually complete a future. + +`CompletableFuture` implements both `Future` and `CompletionStage`. It allows you to build **non-blocking asynchronous pipelines**, registering callbacks that execute as stages complete. + +### Core Creation Methods +* **`CompletableFuture.supplyAsync(Supplier supplier)`**: Runs a task asynchronously that returns a result. By default, it runs in the `ForkJoinPool.commonPool()`. +* **`CompletableFuture.runAsync(Runnable runnable)`**: Runs a task asynchronously that does *not* return a result. + +### Non-blocking Callbacks and Chaining +* **`thenApply(Function fn)`**: Transforms the result of the previous stage. Equivalent to map. It runs synchronously on the thread that completed the previous stage, or on the main thread if the previous stage was already complete. +* **`thenAccept(Consumer action)`**: Consumes the result of the previous stage without returning a new result (returns `CompletableFuture`). +* **`thenRun(Runnable action)`**: Executes a task after the previous stage completes, ignoring its result. + +### Chaining vs. Combining Futures +* **`thenCompose(Function> fn)`**: Used to chain two dependent futures. If Task B depends on the result of Task A and itself returns a `CompletableFuture`, `thenCompose` flattens the nested futures (`CompletableFuture>` becomes `CompletableFuture`). This is equivalent to flatMap. +* **`thenCombine(CompletionStage other, BiFunction fn)`**: Used to run two independent futures in parallel and combine their results when both complete. + +--- + +## 2. Thread Pools and Executors + +### Common ForkJoinPool vs. Custom ExecutorService +By default, the `*Async` methods without an explicit `Executor` parameter run their tasks in `ForkJoinPool.commonPool()`. +* **ForkJoinPool.commonPool()**: + * Designed for CPU-bound computations. + * The size of the pool defaults to `Runtime.getRuntime().availableProcessors() - 1`. + * **Anti-pattern:** Running blocking I/O tasks (database queries, external API calls) in the common pool. If the common pool threads get blocked, it starves other parts of the application (like parallel streams) that rely on the same common pool. +* **Custom ExecutorService**: + * Always supply a custom executor (e.g., `ThreadPoolExecutor` or cached/fixed thread pool) for I/O-intensive tasks to keep application-wide CPU tasks unblocked. + * Usage: `CompletableFuture.supplyAsync(supplier, customExecutor)`. + +--- + +## 3. Parallel Streams and ForkJoinPool + +Parallel streams simplify parallel processing by dividing a stream into substreams and execution across multiple threads using a `ForkJoinPool`. + +### How Parallel Streams Work +1. **Splitting:** The stream is split into parts using a `Spliterator`. +2. **Execution:** Substreams are processed by worker threads using the *work-stealing algorithm* inside `ForkJoinPool.commonPool()`. +3. **Combining:** The results of the substreams are combined (e.g., using `reduce` or `collect`). + +### Pitfalls of Parallel Streams +* **Sharing the Common Pool:** As mentioned, all parallel streams in a JVM share the same `ForkJoinPool.commonPool()`. If one stream blocks on I/O, it can degrade the performance of all other parallel streams. +* **Ordering Costs:** Operations like `limit()`, `skip()`, and `findFirst()` can be very expensive on parallel streams because they must preserve document/encounter order. +* **Stateful/Side-effecting Lambdas:** Modifying shared state inside parallel stream operations leads to race conditions. +* **Custom ForkJoinPool Trick:** Although not officially part of the standard Stream API design, you can run a parallel stream inside a custom `ForkJoinPool` to isolate it from the common pool: + ```java + ForkJoinPool customPool = new ForkJoinPool(4); + customPool.submit(() -> list.parallelStream().map(x -> ...).collect(...)).get(); + ``` + +--- + +## 4. Coordination & Synchronization (Classic Wait/Notify) + +For coordinate execution between threads (like an Even/Odd printing handshake), classic synchronization primitives are used: +* **`synchronized` block:** Obtains a monitor lock on a shared object. +* **`wait()`**: Tells the current thread to give up the monitor and go to sleep until another thread invokes `notify()` or `notifyAll()` on the same monitor. Must be called from a synchronized context. +* **`notify()` / `notifyAll()`**: Wakes up one or all threads waiting on this object's monitor. Must be called from a synchronized context. +* **Condition Loops:** Always invoke `wait()` inside a loop testing the condition being waited for: + ```java + synchronized (lock) { + while (!condition) { + lock.wait(); + } + // perform action + lock.notifyAll(); + } + ``` + +--- + +## 5. Advanced Interview Cheat Sheet (5+ YoE) + +1. **What is the difference between `thenApply` and `thenApplyAsync`?** + * `thenApply` runs on the thread that completes the previous stage (or on the calling thread if already completed). + * `thenApplyAsync` always submits the callback task to an executor (the default `ForkJoinPool.commonPool()` or a custom one if supplied) to run on a separate thread. +2. **How do you handle exceptions in CompletableFuture?** + * `exceptionally(Function fn)`: Recovers from an exception by providing a fallback value. + * `handle(BiFunction fn)`: Allows you to transform both the successful result or the exception. + * `whenComplete(BiConsumer action)`: Executes a callback upon completion without transforming the result. +3. **Java 8 Timeout Handling Limitations:** + * Java 8 does *not* support `.orTimeout()` or `.completeOnTimeout()`. These were introduced in Java 9. + * In Java 8, timeout handling must be done by calling `future.get(timeout, unit)` which blocks, or by using a scheduled executor to manually complete the future exceptionally after a duration. diff --git a/src/module5_concurrency/CompletableFutureDemo.java b/src/module5_concurrency/CompletableFutureDemo.java new file mode 100644 index 0000000..b4016bb --- /dev/null +++ b/src/module5_concurrency/CompletableFutureDemo.java @@ -0,0 +1,115 @@ +package com.javatechie.lambda.demo.src.module5_concurrency; + + +import com.javatechie.lambda.demo.src.models.Employee; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Exercise: CompletableFuture asynchronous pipelines. + * + * Objectives: + * 1. Run asynchronous tasks using custom ExecutorService. + * 2. Combine results from two independent futures using thenCombine. + * 3. Handle task timeout in a Java 8 compliant way (using future.get(timeout, unit)). + * 4. Apply exception recovery logic using exceptionally() or handle(). + */ +public class CompletableFutureDemo { + + public static void main(String[] args) { + ExecutorService executor = Executors.newFixedThreadPool(4); + + try { + System.out.println("--- Task 1: Load Employees Async ---"); + CompletableFuture> empFuture = loadEmployeesAsync(executor); + if (empFuture != null) { + empFuture.thenAccept(employees -> { + if (employees != null) { + System.out.println("Loaded " + employees.size() + " employees asynchronously."); + } + }).join(); // join to wait for printout + } + + System.out.println("\n--- Task 2: Combine and Transform ---"); + CompletableFuture countFuture = combineAndTransform(executor); + if (countFuture != null) { + System.out.println("Combined total contacts count: " + countFuture.join()); + } + + System.out.println("\n--- Task 3: Handle Timeout ---"); + String timeoutResult = handleTimeout(); + System.out.println("Timeout task result: " + timeoutResult); + + System.out.println("\n--- Task 4: Exception Recovery ---"); + CompletableFuture recoveryFuture = exceptionRecovery(); + if (recoveryFuture != null) { + System.out.println("Exception recovery result: " + recoveryFuture.join()); + } + + } finally { + executor.shutdown(); + } + } + + /** + * Task 1: Load employees asynchronously. + * Use CompletableFuture.supplyAsync and pass a custom executor service. + * + * @param executor the custom thread pool executor + * @return CompletableFuture containing list of employees + */ + public static CompletableFuture> loadEmployeesAsync(ExecutorService executor) { + // TODO: Implement loading employees from DataStore.getAllEmployees() asynchronously. + // Return a CompletableFuture containing the employee list. + return CompletableFuture.completedFuture(null); + } + + /** + * Task 2: Fetch and combine two independent datasets. + * 1. Get the list of all employee names asynchronously. + * 2. Get the list of all customer names asynchronously. + * 3. Combine both lists using thenCombine to produce a single count of total contacts (employees + customers). + * + * @param executor the custom thread pool executor + * @return CompletableFuture containing total count of contacts + */ + public static CompletableFuture combineAndTransform(ExecutorService executor) { + // TODO: + // Future A: supplyAsync to get employee names list: DataStore.getAllEmployees() mapped to Employee::getName + // Future B: supplyAsync to get customer names list: DataStore.getAllCustomers() mapped to Customer::getName + // Combine them using thenCombine, summing their sizes. + return CompletableFuture.completedFuture(0); + } + + /** + * Task 3: Simulate a slow operation and handle timeout. + * In Java 8, we handle timeout using future.get(timeout, unit) since .orTimeout() was introduced in Java 9. + * Implement a method that executes a task taking 3 seconds, and attempts to get the result with a 1-second timeout. + * + * @return string output indicating success or timeout recovery message + */ + public static String handleTimeout() { + // TODO: + // 1. Create a CompletableFuture that sleeps for 3 seconds (simulating delay), then returns "Success". + // 2. Try to retrieve the result using future.get(1, TimeUnit.SECONDS). + // 3. Catch TimeoutException (and other exception types), print a message, and return "Fallback / Timeout occurred". + return "Not implemented"; + } + + /** + * Task 4: Exception Recovery. + * Use CompletableFuture.supplyAsync to execute a failing task. + * Recover from the failure using exceptionally() or handle(), returning a fallback message. + * + * @return CompletableFuture containing the final message (either successful completion or recovered value) + */ + public static CompletableFuture exceptionRecovery() { + // TODO: + // 1. supplyAsync a task that throws a RuntimeException("Database error!"). + // 2. Chain exceptionally() to catch the error and return a fallback string: "Recovered: Default Data". + return CompletableFuture.completedFuture(null); + } +} diff --git a/src/module5_concurrency/EvenOddPrinter.java b/src/module5_concurrency/EvenOddPrinter.java new file mode 100644 index 0000000..17d31e2 --- /dev/null +++ b/src/module5_concurrency/EvenOddPrinter.java @@ -0,0 +1,61 @@ +package com.javatechie.lambda.demo.src.module5_concurrency; + +/** + * Exercise: Coordinate Even/Odd printing using two threads with synchronization/handshake logic. + * + * Objectives: + * 1. Implement synchronization between two threads using a shared monitor lock. + * 2. Use wait() and notifyAll() to coordinate the handshake so that numbers are printed + * sequentially (e.g., 1, 2, 3, 4, ..., limit). + * 3. One thread must print odd numbers, and the other thread must print even numbers. + * 4. Ensure there is no busy-spinning (use wait/notify to block threads when it's not their turn). + */ +public class EvenOddPrinter { + + private final int limit; + private int counter = 1; + private final Object lock = new Object(); + + public EvenOddPrinter(int limit) { + this.limit = limit; + } + + /** + * Prints odd numbers up to the limit. + */ + public void printOdd() { + // TODO: Implement the loop and wait/notify logic to print odd numbers. + // Tips: + // - Synchronize on the shared 'lock' object. + // - Loop until counter exceeds limit. + // - While the counter is even, wait on the lock. + // - Print the thread name and current counter. + // - Increment the counter. + // - Notify the other thread. + } + + /** + * Prints even numbers up to the limit. + */ + public void printEven() { + // TODO: Implement the loop and wait/notify logic to print even numbers. + // Tips: + // - Synchronize on the shared 'lock' object. + // - Loop until counter exceeds limit. + // - While the counter is odd, wait on the lock. + // - Print the thread name and current counter. + // - Increment the counter. + // - Notify the other thread. + } + + public static void main(String[] args) { + EvenOddPrinter printer = new EvenOddPrinter(20); + + // TODO: Create Thread t1 for printOdd() and Thread t2 for printEven(). + // Tip: Set meaningful thread names (e.g., "Odd-Thread", "Even-Thread") to verify correctness in console logs. + + // TODO: Start both threads. + + // TODO: Join both threads to wait for completion. + } +} diff --git a/src/module6_exceptions/CONCEPT.md b/src/module6_exceptions/CONCEPT.md new file mode 100644 index 0000000..0b6238e --- /dev/null +++ b/src/module6_exceptions/CONCEPT.md @@ -0,0 +1,81 @@ +# Module 6: Exception Handling in Java 8 + +In Java 8, dealing with checked exceptions inside lambda expressions and Stream pipelines is a common pain point. This module covers the rules, patterns, and elegant wrapper functions to handle exceptions in a clean, functional style. + +--- + +## 1. The Core Problem with Checked Exceptions in Lambdas + +Java 8 functional interfaces (such as `Function`, `Consumer`, `Predicate`, etc.) do **not** declare any throws clauses in their abstract methods. For example, `java.util.function.Function` is defined as: +```java +@FunctionalInterface +public interface Function { + R apply(T t); // Does NOT throw any checked exception +} +``` + +If you try to call a method that throws a checked exception (e.g., `Class.forName()`, `Thread.sleep()`, or database operations) inside a lambda that matches this interface, the compiler will complain: +```java +// Compilation Error: Unhandled exception type ClassNotFoundException +List> classes = classNames.stream() + .map(name -> Class.forName(name)) + .collect(Collectors.toList()); +``` + +--- + +## 2. Solution Patterns + +### Pattern A: Standard Try-Catch Block (The Naive Approach) +The simplest solution is to wrap the body in a try-catch block. +```java +List> classes = classNames.stream() + .map(name -> { + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); +``` +*Downside*: This makes functional pipelines verbose, hard to read, and defeats the purpose of clean, declarative streams. + +### Pattern B: Creating a Wrapper Utility (The Senior Developer Approach) +You can define a generic wrapper method that takes a functional interface throwing checked exceptions and converts it to a standard Java functional interface by catching the checked exception and rethrowing it as an unchecked exception (`RuntimeException`). + +For example, define a functional interface that allows throwing checked exceptions: +```java +@FunctionalInterface +public interface ThrowingFunction { + R apply(T t) throws E; +} +``` + +Then write a generic wrapper static method: +```java +public static Function handleException(ThrowingFunction throwingFunction) { + return val -> { + try { + return throwingFunction.apply(val); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; +} +``` + +This lets you write clean stream pipelines: +```java +List> classes = classNames.stream() + .map(handleException(Class::forName)) + .collect(Collectors.toList()); +``` + +--- + +## 3. 5+ YOE Interview Focus Points + +* **SneakyThrows**: How does Lombok's `@SneakyThrows` work? It uses bytecode manipulation to deceive the compiler into throwing checked exceptions without declaring them in the method signature, bypassing the Java checked exception rules. +* **Custom Functional Interfaces**: When designing custom API libraries, prefer declaring functional interfaces with generic throws bounds (e.g., ``) if the library operations might throw checked exceptions. +* **Performance of Wrapper Lambdas**: Every wrapping adds a small indirection layer. In highly throughput-sensitive batch processing pipelines, handling exceptions via try-catch inside a custom helper method might be slightly more efficient than creating multiple layers of generic function wrappers. diff --git a/src/module6_exceptions/ExceptionHandling.java b/src/module6_exceptions/ExceptionHandling.java new file mode 100644 index 0000000..07c4c86 --- /dev/null +++ b/src/module6_exceptions/ExceptionHandling.java @@ -0,0 +1,67 @@ +package com.javatechie.lambda.demo.src.module6_exceptions; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * MODULE 6: Exception Handling in Java 8 Lambdas + * + * Exercise instructions: + * 1. Implement a wrapping functional interface/wrapper method that lets you execute code throwing checked exceptions + * inside a standard stream pipeline cleanly. + * 2. Complete the tasks below without introducing compiler errors. + */ +public class ExceptionHandling { + + // Define a custom functional interface that allows throwing checked exceptions + @FunctionalInterface + public interface ThrowingConsumer { + void accept(T t) throws E; + } + + @FunctionalInterface + public interface ThrowingFunction { + R apply(T t) throws E; + } + + /** + * Task 1: Complete the wrapper method to convert a ThrowingConsumer into a standard Consumer. + * It should catch any checked exception and rethrow it as a RuntimeException. + */ + public static Consumer handleConsumerException(ThrowingConsumer throwingConsumer) { + // TODO: Implement wrapper + return t -> { + // Your solution here + }; + } + + /** + * Task 2: Complete the wrapper method to convert a ThrowingFunction into a standard Function. + * It should catch any checked exception and rethrow it as a RuntimeException. + */ + public static Function handleFunctionException(ThrowingFunction throwingFunction) { + // TODO: Implement wrapper + return t -> { + // Your solution here + return null; + }; + } + + public static void main(String[] args) { + List list = Arrays.asList("40", "50", "invalid", "60"); + + // Example Task 3: Use handleConsumerException to parse elements and print them. + // Thread.sleep(millis) throws InterruptedException (a checked exception). + // Wrap a sleep delay inside the consumer. + List classNames = Arrays.asList("java.lang.String", "java.lang.Integer", "non.existent.Class"); + + System.out.println("--- Loading Classes using handleFunctionException wrapper ---"); + // TODO: Use the handleFunctionException wrapper inside the map operation to load classes dynamically with Class.forName(name) + // List> loadedClasses = classNames.stream() + // .map( ... ) + // .collect(Collectors.toList()); + } +} diff --git a/stream/BiConsumerExample.java b/stream/BiConsumerExample.java deleted file mode 100644 index 457f192..0000000 --- a/stream/BiConsumerExample.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.javatechie.stream; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; - -public class BiConsumerExample implements BiConsumer { - @Override - public void accept(String i1, Integer i2) { - System.out.println("input 1 "+i1 +": input 2 "+i2); - } - - public static void main(String[] args) { - BiConsumer biConsumer=new BiConsumerExample(); - biConsumer.accept("javatechie",53000); - - BiConsumer biConsumer1=new BiConsumer() { - @Override - public void accept(String i1, Integer i2) { - System.out.println(i1+":"+i2); - } - }; - - biConsumer1.accept("Welcome",143); - - BiConsumer biConsumer2= ( i1, i2) -> System.out.println("key :"+i1+" value :"+i2); - biConsumer2.accept("youTube",786); - - Map map=new HashMap<>(); - map.put("basant",5000); - map.put("santosh",15000); - map.put("javed",12000); - - map.forEach((k,v)-> System.out.println(k+","+v)); - - - } -} diff --git a/stream/BiFunctionExample.java b/stream/BiFunctionExample.java deleted file mode 100644 index 21b17fb..0000000 --- a/stream/BiFunctionExample.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.javatechie.stream; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class BiFunctionExample implements BiFunction, List, List> { - @Override - public List apply(List list1, List list2) { - return Stream.of(list1, list2) - .flatMap(List::stream) - .distinct() - .collect(Collectors.toList()); - } - - public static void main(String[] args) { - BiFunction biFunction = new BiFunctionExample(); - List list1 = Stream.of(1, 3, 4, 6, 7, 9, 19).collect(Collectors.toList()); - List list2 = Stream.of(11, 3, 43, 6, 7, 19).collect(Collectors.toList()); - System.out.println("Traditional approach : " + biFunction.apply(list1, list2)); - - BiFunction,List,List> biFunction1=new BiFunction, List, List>() { - @Override - public List apply(List l1, List l2) { - return Stream.of(l1, l2) - .flatMap(List::stream) - .distinct() - .collect(Collectors.toList()); - } - }; - - System.out.println("annonymous impl : "+biFunction1.apply(list1, list2)); - - - BiFunction,List,List> biFunction2=( l1, l2) ->{ - return Stream.of(l1, l2) - .flatMap(List::stream) - .distinct() - .collect(Collectors.toList()); - }; - - Function,List> sortedFunction=(lists)->lists - .stream() - .sorted() - .collect(Collectors.toList()); - - System.out.println("Lambda approach : "+biFunction2.andThen(sortedFunction).apply(list1, list2)); - - - Map map=new HashMap<>(); - map.put("basant",5000); - map.put("santosh",15000); - map.put("javed",12000); - - BiFunction increaseSalaryBiFunction= new BiFunction() { - @Override - public Integer apply(String key, Integer value) { - return value+1000; - } - }; - - //map.replaceAll(increaseSalaryBiFunction); - - - //BiFunction increaseSalBiFunction= ( key, value) -> value+2000; - - map.replaceAll(( key, value) -> value+2500); - - System.out.println(map); - } -} diff --git a/stream/BiPredicateExample.java b/stream/BiPredicateExample.java deleted file mode 100644 index 6d2f73c..0000000 --- a/stream/BiPredicateExample.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.javatechie.stream; - -import java.util.function.BiPredicate; - -public class BiPredicateExample { - - - public static void main(String[] args) { - - - BiPredicate biPredicate=new BiPredicate() { - @Override - public boolean test(String s1, String s2) { - return s1.equals(s2); - } - }; - System.out.println(biPredicate.test("madam","madam")); - - - BiPredicate equalsPredicate= ( s1, s2) ->s1.equals(s2); - BiPredicate lengthPredicate=(s1,s2)->s1.length()==s2.length(); - - boolean output=lengthPredicate.and(equalsPredicate).test("madam","madam"); - System.out.println("output : "+output); - - boolean orOutput=lengthPredicate.or(equalsPredicate).test("abc","def"); - System.out.println("orOutput : "+orOutput); - - System.out.println(equalsPredicate.test("madam","madam")); - - } -}