Java-21 features

Index

  1. Language feauteres
    • String Template
    • record pattern
    • pattern matching for switch
  2. Libraries improvement
    • Virtual threads
    • Sequenced collections
  3. Performace improvement
    • ZGC


String Template[Preview]

Index

  1. Why Introduced
  2. syntax
    • Usage Example

There are three components to a template expression:

  1. A processor
    • STR Template Processor
  2. A template which contains the data with the embedded expressions
    • e,g, “\{var_name}
  3. A dot (.) character
String interpolationUsingSTRProcessor(String feelsLike, String temperature, String unit) {
    return STR
      . "Today's weather is \{ feelsLike }, with a temperature of \{ temperature } degrees \{ unit }" ;
}


Record pattern

Index

  1. Why Introduced
  2. syntax
    • Usage Example


Pattern matching for switch

Index

  1. Why Introduced
  2. syntax
    • Usage Example


Virtual threads

Index

  1. Why Introduced
  2. syntax
    • Usage Example

Sequenced collections

Index

  1. Why Introduced
  2. syntax
    • Usage Example

Java-17 features

Index

  1. NullPointerException message enhancement
  2. Null allowed in switch
  3. Switch expression enhancement
    • switch can come with arrow sign -> which returns a value and
    • use of keyword yield to return default value in default section
    • multiple cases can be separated by comma
  4. Sealed classes
    • Only permitted class can inherit
  5. Record class
    • reduced boilerplate,
    • immutable and final class – they are not extensible.  
    • No setters
    • temporarily hold immutable data i.e traditional POJO

Java-8 features

Index (for more depth refer drive java-8book)

  1. Lambda Expression / Functional Interfaces
    1. Why it was introduced in first place
    2. What problem does it solve
    3. Lambda expression internal working 
    4. Need of functional interface 
    5. Ever used functional interface 
  2. Streams
    1. Why it was introduced in first place
    2. What problem does it solve
    3. Sorting, filtering, mapping streams 
    4. Map, flatmap usage 
  3. Interface – Default/Static methods
    1. Why it was introduced in first place
    2. What problem does it solve
  4. Method and Constructor References
    1. Why it was introduced in first place
    2. What problem does it solve
  5. Optional
    1. Why it was introduced in first place
    2. What problem does it solve
  6. Functionality added to Hash-map
    1. Why it was introduced in first place
    2. What problem does it solve
  7. Functionality added for Garbage Collection
    1. Why it was introduced in first place
    2. What problem does it solve
  8. Programming Questions – java -8 features

Lambda

Index

  1. Introduction
  2. Representation

How does lambda expressions fit into Java’s type system?

Each lambda corresponds to a given type, specified by an interface. A functional interface must contain exactly one abstract method declaration. Each lambda expression of that type will be matched to this abstract method. Since default methods are not abstract you’re free to add default methods to your functional interface.

Representation and usage:

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

The static utility method Collections.sort accepts a list and a comparator in order to sort the elements of the given list. You often find yourself creating anonymous comparators and pass them to the sort method.

Instead of creating anonymous objects all day long, Java 8 comes with a much shorter syntax, lambda expressions:

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

Streams

Index

  1. Introduction
    1. Definition
    2. How streams differ from collection
  2. Representation – ways to obtain stream
  3. Stream Operations and pipeline
  4. Parallelism
  5. FAQs

Stream –

Definition – It conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.

Description in context to java and programming-

The package java.util.stream provides the classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections. For example:

int sum = widgets.stream()
                      .filter(b -> b.getColor() == RED)
                      .mapToInt(b -> b.getWeight())
                      .sum();

Here we use widgets, a Collection<Widget>, as a source for a stream, and then perform a filter-map-reduce on the stream to obtain the sum of the weights of the red widgets.

Streams differ from collections in several ways:

  1. No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
  2. Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
    • Q- if it does not modify the source, how is it functional ? Ans- it performs the operation[functions are known for this and not modifying the source])
  3. Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, “find the first String with three consecutive vowels” need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
    • Q- Meaning of intermediate operations being lazy? Ans – doesn’t go to next operation unless all streams elements consumed(Or intermediate operation task completed e.g find first String with 3 vowels in beginning) (is it – what in case of infinite stream?)[guess based on understanding but doubt based on working senario]
    • Q- so how is for loop different from stream pipeline – once we find specific string we can break,
      • Ans1- In-build operations are provided, we don’t need to initialise extra variables for looping.
      • Ans2- for looping through we need to store the values in DS,in case we have infinite source of data – it will be a challange to store the data in-memory!
      • Q2-Is it thread safe-the stream operation? Yes because – Streams are not modifiable i.e one can’t add or remove elements from streams.(ref- gfg) where as once can easily remove/add elements from collection.
  4. Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  5. Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.
    1. E.g https://www.java67.com/2016/03/how-to-use-flatmap-in-java-8-stream.html
    2. Look for official examples in Oracle website

Streams can be obtained in a number of ways. Some examples include:

  1. From a Collection via the stream() and parallelStream() methods;
  2. From an array via Arrays.stream(Object[]);
  3. From static factory methods on the stream classes, such as Stream.of(Object[])IntStream.range(int, int) or Stream.iterate(Object, UnaryOperator);
  4. The lines of a file can be obtained from BufferedReader.lines();
  5. Streams of file paths can be obtained from methods in Files;
  6. Streams of random numbers can be obtained from Random.ints();
  7. Numerous other stream-bearing methods in the JDK, including BitSet.stream()Pattern.splitAsStream(java.lang.CharSequence), and JarFile.stream().

Stream Operations and Pipeline

A stream pipeline consists of a source (such as a Collection, an array, a generator function, or an I/O channel); followed by zero or more intermediate operations such as Stream.filter or Stream.map; and a terminal operation such as Stream.forEach or Stream.reduce.

  1. Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.
  2. Terminal operations, such as Stream.forEach or IntStream.sum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator() are not; these are provided as an “escape hatch” to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.
  3. Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state. Laziness also allows avoiding examining all the data when it is not necessary; for operations such as “find the first string longer than 1000 characters”, it is only necessary to examine just enough strings to find one that has the desired characteristics without examining all of the strings available from the source. (This behavior becomes even more important when the input stream is infinite and not merely large.)
  4. Intermediate operations are further divided into stateless and stateful operations. Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element — each element can be processed independently of operations on other elements. Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.
  5. Stateful operations may need to process the entire input before producing a result. For example, one cannot produce any results from sorting a stream until one has seen all elements of the stream. As a result, under parallel computation, some pipelines containing stateful intermediate operations may require multiple passes on the data or may need to buffer significant data. Pipelines containing exclusively stateless intermediate operations can be processed in a single pass, whether sequential or parallel, with minimal data buffering.
  6.  some operations are deemed short-circuiting operations. An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. A terminal operation is short-circuiting if, when presented with infinite input, it may terminate in finite time. Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.

Parallelism

The stream implementations in the JDK create serial streams unless parallelism is explicitly requested. For example, Collection has methods Collection.stream() and Collection.parallelStream(), which produce sequential and parallel streams respectively; other stream-bearing methods such as IntStream.range(int, int) produce sequential streams but these streams can be efficiently parallelized by invoking their BaseStream.parallel() method. To execute the prior “sum of weights of widgets” query in parallel, we would do:

int sumOfWeights = widgets.parallelStream()
                               .filter(b -> b.getColor() == RED)mmel, 3. Nginx, 4. Spring Boot, 5. Liel, 3. Nginx, 4. Spring Boot, 5. Li
                               .mapToInt(b -> b.getWeight())
                               .sum();

When the terminal operation is initiated, the stream pipeline is executed sequentially or in parallel depending on the orientation of the stream on which it is invoked.

Non-Interference

Streams enable you to execute possibly-parallel aggregate operations over a variety of data sources, including even non-thread-safe collections such as ArrayList. This is possible only if we can prevent interference with the data source during the execution of a stream pipeline. Except for the escape-hatch operations iterator() and spliterator(), execution begins when the terminal operation is invoked, and ends when the terminal operation completes.

List<String> l = new ArrayList(Arrays.asList("one", "two"));
     Stream<String> sl = l.stream();
     l.add("three");
     String s = sl.collect(joining(" "));

First a list is created consisting of two strings: “one”; and “two”. Then a stream is created from that list. Next the list is modified by adding a third string: “three”. Finally the elements of the stream are collected and joined together. Since the list was modified before the terminal collect operation commenced the result will be a string of “one two three”.


Stream Operations summarised:

OperationMethod SignatureTypeDescriptionUsage Example
filterStream<T> filter(Predicate<? super T> p)IntermediateReturns a stream of elements that match the given predicate.stream.filter(x -> x > 5).forEach(System.out::println);
mapStream<R> map(Function<? super T, ? extends R> mapper)IntermediateTransforms each element using the provided function.stream.map(x -> x * 2).collect(Collectors.toList());
flatMapStream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)IntermediateSimilar to map, but each input element is mapped to a stream and the resulting streams are concatenated.stream.flatMap(str -> Arrays.stream(str.split(" "))).distinct().collect(Collectors.toList());
distinctStream<T> distinct()IntermediateReturns a stream of distinct elements.stream.distinct().forEach(System.out::println);
sortedStream<T> sorted()IntermediateReturns a stream sorted according to the natural order of elements.stream.sorted().forEach(System.out::println);
peekStream<T> peek(Consumer<? super T> action)IntermediateApplies the specified action to each element and returns a new stream.stream.peek(x -> System.out.println("Processing: " + x)).count();
forEachvoid forEach(Consumer<? super T> action)TerminalPerforms an action for each element of the stream.stream.forEach(System.out::println);
toArrayObject[] toArray()TerminalReturns an array containing the elements of the stream.Object[] array = stream.toArray();
collectR collect(Collector<? super T, A, R> collector)TerminalPerforms a mutable reduction on the elements of the stream using a Collector.List<Integer> list = stream.collect(Collectors.toList());
reduceOptional<T> reduce(BinaryOperator<T> accumulator)TerminalPerforms a reduction on the elements of the stream using an associative accumulation function and returns an Optional.Optional<Integer> result = stream.reduce((a, b) -> a * b);
countlong count()TerminalReturns the count of elements in the stream as a long.long count = stream.count();
anyMatchboolean anyMatch(Predicate<? super T> predicate)TerminalReturns true if any elements of the stream match the given predicate.boolean anyMatch = stream.anyMatch(x -> x > 5);
allMatchboolean allMatch(Predicate<? super T> predicate)TerminalReturns true if all elements of the stream match the given predicate.boolean allMatch = stream.allMatch(x -> x > 0);
noneMatchboolean noneMatch(Predicate<? super T> predicate)TerminalReturns true if no elements of the stream match the given predicate.boolean noneMatch = stream.noneMatch(x -> x == 0);
findFirstOptional<T> findFirst()TerminalReturns the first element of the stream as an Optional.Optional<Integer> firstElement = stream.findFirst();
findAnyOptional<T> findAny()TerminalReturns any element of the stream as an Optional.Optional<Integer> anyElement = stream.findAny();

Interface Default Methods

Index

  1. Introduction
  2. Representation
  3. Why it was introduced in first place
interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }

Why it was introduced in first place


To provide a mechanism for adding new methods to interfaces without breaking existing implementations.

Before Java 8, interfaces in Java could only declare abstract methods, and any class implementing an interface was required to provide concrete implementations for all of those methods. This created challenges when evolving interfaces in existing codebases, as adding a new method to an interface would force all implementing classes to provide an implementation,

Standard library example:

a new method is introduced to a widely used built-in interface like List. Without default methods, every class that implements List would need to be updated to provide an implementation for the new method.

Example

import java.util.Arrays;
import java.util.List;

public class IterableExample {
    public static void main(String[] args) {
        List<String> myList = Arrays.asList("Java", "is", "powerful", "and", "flexible");

        // Using forEach method added to the Iterable interface
        myList.forEach(System.out::println);
    }
}
//In this example, the forEach method is called on the List class, which implements the Iterable interface.
//The addition of forEach as a default method in the Iterable interface made it possible to perform concise iteration on any class implementing the Iterable interface without the need for boilerplate code.

What key issues it addressed for interface?

  1. Backward Compatibility: Existing code that implements an interface isn’t required to provide an implementation for new default methods. This ensures backward compatibility with classes that were written before the introduction of the new methods.
  2. Library Evolution: Developers can add new methods to interfaces in standard Java libraries without breaking existing code that relies on those interfaces.
  3. Functional Interfaces and Lambda Expressions: Default methods played a crucial role in the adoption of lambda expressions and the functional programming paradigm in Java 8. Functional interfaces are interfaces with a single abstract method, and default methods allow these interfaces to provide additional utility methods without violating their functional nature.


Functional Interface

Index

  1. Introduction
  2. Representation
  3. FunctionalInterfaces
    1. Predicates
    2. Function
    3. Supplier
    4. Consumer

To ensure that your interface meet the requirements, you should add the @FunctionalInterface annotation. The compiler is aware of this annotation and throws a compiler error as soon as you try to add a second abstract method declaration to the interface.

Example:

@FunctionalInterface
interface Converter {
T convert(F from);
}

Built-in Functional Interfaces (from java.util.function package)

The JDK 1.8 API contains many built-in functional interfaces. Some of them are well known from older versions of Java like Comparator or Runnable. Those existing interfaces are extended to enable Lambda support via the @FunctionalInterface annotation.

Broadly the functional interface can be categorized in following types-

  1. Predicates
  2. Functions
  3. Suppliers
  4. Consumers

Predicates- Predicates are boolean-valued functions of one argument. The interface contains various default methods for composing predicates to complex logical terms (and, or, negate)

//Syntax : Predicate<TYPE> predicate= [lambda-function with condition on TYPE] 
//Example-1
Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false
//Example2
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

//Example3 - external Method
    import java.util.function.Predicate;  
    public class PredicateInterfaceExample {  
        static Boolean checkAge(int age){  
            if(age>17)  
                return true;  
            else return false;  
        }  
        public static void main(String[] args){  
            // Using Predicate interface  
            Predicate<Integer> predicate =  PredicateInterfaceExample::checkAge;  
            // Calling Predicate method  
            boolean result = predicate.test(25);  
            System.out.println(result);  
        }  
    }  -

Functions-Functions accept one argument and produce a result. Default methods can be used to chain multiple functions together (compose, andThen).

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Suppliers – Suppliers produce a result of a given generic type. Unlike Functions, Suppliers don’t accept arguments.

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

Consumers– Consumers represents operations to be performed on a single input argument.

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));



:: Operator – Method and Constructor References

Index

  1. Introduction
  2. Representation

Java 8 enables you to pass references of methods or constructors via the :: keyword.
The below example shows how to reference a static method. But we can also reference object methods:

Converter<String, Integer> converter = Integer::parseInt;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123

Optional -Handles the Null values

Index

  1. Introduction
  2. Representation
	private static String someString;// = "someString";
	public static void main(String arg[]) {
		Optional<String> optinalString = Optional.ofNullable(someString); // of(someString);
		if (optinalString.isPresent()) {//If the Optional contains a non-null value, isPresent() returns true.
			System.out.println(optinalString.get());
		}
		// System.out.println(optinalString.isPresent());
		System.out.println(optinalString.orElse("DefaultString"));
	}

Programming Questions – java -8 features

  1. Basic Lambda Expression:
    • Write a lambda expression that takes two integers and returns their sum.
  2. Stream Filtering:
    • Given a list of strings, use streams to filter out strings that start with the letter “A” and print the remaining strings.
  3. Mapping with Lambdas:
    • Given a list of integers, use streams to square each integer and store the results in a new list.
  4. Combining Filters and Mapping:
    • Given a list of persons (similar to the previous example), use streams to filter out persons with an age less than 25 and print their names.
  5. Grouping with Streams:
    • Given a list of persons, use streams to group them by age and print the groups.
  6. Sorting with Streams:
    • Given a list of strings, use streams to sort them alphabetically and print the sorted list.
  7. Collectors and Joining:
    • Given a list of strings, use streams and collectors to concatenate all the strings into a single comma-separated string.
  8. Parallel Streams:
    • Write a program that demonstrates the use of parallel streams to process a large list of numbers and print the result.
  9. FlatMap Operation:
    • Given a list of lists of integers, use streams and flatMap to flatten the lists and print the unique integers.
  10. Exception Handling with Streams:
    • Write a program using streams that reads a list of numbers as strings, converts them to integers, and handles any NumberFormatException gracefully, printing an error message for invalid input.

Programs:

   /**
     * 1.Write a lambda expression that takes two integers and returns their sum.
     * @return
     */
    static int returnSumOFwoInteger(){
        int a=5;
        int b=10;
        SumFunction sumF=(x,y)->x+y;
        return  sumF.sum(a,b);
    }
//Helper Functional-interface
@FunctionalInterface
interface SumFunction{
    int sum(int a,int b);
}

 /**
     * 2.Given a list of strings, use streams to filter out strings that start with the letter "A" and print the remaining strings.
     * @param 
     */
    private static void filterPrefixStingANdPringRest() {
        List<String> al= Arrays.asList("Akram","ABhijit","zimba");
        al.stream().filter(s->s.startsWith("A")).map(a->a.substring(1)).forEach(System.out::println);
    }
    /**
     * 3.Given a list of integers, use streams to square each integer and store the results in a new list.
     * @param 
     */
    private static void squareListOfIntegers() {
        List<Integer> arrINt=Arrays.asList(1,2,3,4,5);
        List<Integer>  res=  arrINt.stream().map(a->a*a).collect(Collectors.toList());
        //print
        System.out.println(res);
    }
   /**
     * 4.Given a list of persons (similar to the previous example), use streams to filter out persons with an age less than 25 and print their names.
     */
    private static void printPersonsNAmeshoseAgeAgreaterThan20() {
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Charlie", 28),
                new Person("David", 35)
        );
        persons.stream().filter(p->p.getAge()>26).map(Person::getName).forEach(System.out::println);
    }

//helper class
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
    /**
     * 5.Given a list of persons, use streams to group them by age and print the groups.
     */
    private static void printPersonsGroupByAge() {
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Charlie", 22),
                new Person("David", 35),
                new Person("Eva", 30),
                new Person("Frank", 22)
        );
       Map<Integer,List<Person>> groupBYAge=persons.stream().collect(Collectors.groupingBy(Person::getAge));

       groupBYAge.forEach((age,group)->{
           System.out.println("age::"+age);
       group.forEach(p->System.out.println(p.getName()));
       });
    }

/**
*variation person with - 0-18 in a group - 19-35 in a group
*/

Map<String, List<Person>> pGroupMap=personList.stream().collect(Collectors.groupingBy((p)->{
			if(p.getAge()<18) {
				return "0-18";
			}
			if(p.getAge()>18&&p.getAge()<35) {
				return "19-35";
			}
			else {
				return "35+";
			}
			
		}));
		pGroupMap.forEach((group,p)->
		System.out.println(group+":"+p));	
};

/*by default the value of group is the object itself if none returned in secoind param  or else iof returned then that will be the group e.g*/
/*WAP in java 8 streams
class Resource {
int id;
String resourceName;
List skills;
}
count how many resources are their in each skillSet?
--
*/
public class Demo {
    public static void main(String[] args) {

        List<Resource> resourceList = getListOFResources();

        Map<String, Long> mapOFSkills = resourceList.stream().flatMap(r -> r.getSkills().stream()).collect(Collectors.groupingBy(skil -> skil, Collectors.counting()));
        mapOFSkills.forEach((group, rl) -> {
            System.out.println(group + " Group and resocues " + rl);
        });
    }

    private static List<Resource> getListOFResources() {
        Resource r1 = new Resource(1, "Sevaand", Arrays.asList("java", "spring", "springboot"));
        Resource r2 = new Resource(2, "aand", Arrays.asList("react", "spring", "springboot"));
        Resource r3 = new Resource(3, "Seva", Arrays.asList("typescript", "spring", "springboot"));
        Resource r4 = new Resource(4, "Shiva", Arrays.asList("javascript", "spring", "springboot"));
        Resource r5 = new Resource(5, "raavan", Arrays.asList("python", "spring", "springboot"));
        Resource r6 = new Resource(6, "Rama", Arrays.asList("python", "django"));
        Resource r7 = new Resource(7, "laskhamana", Arrays.asList("angular", "javascript", "typescript"));

        List<Resource> resourceList = new ArrayList<>();
        resourceList.add(r1);
        resourceList.add(r2);
        resourceList.add(r3);
        resourceList.add(r4);
        resourceList.add(r5);
        resourceList.add(r6);
        resourceList.add(r7);


        return resourceList;
    }
}

/*
output:
spring Group and resocues 5
angular Group and resocues 1
python Group and resocues 2
django Group and resocues 1
java Group and resocues 1
react Group and resocues 1
typescript Group and resocues 2
javascript Group and resocues 2
springboot Group and resocues 5

*/

 /**
     * 6.Given a list of strings, use streams to sort them alphabetically and print the sorted list.
     */
    private static void printSortedListedAlphabetically() {
        List<String> arrS=Arrays.asList(
                "Bullbog",
                "Abra",
                "Ka",
                "Dabra"
        );

        arrS.stream().sorted().forEach(System.out::println);

    }
//sorting in descending order
//string
 arrS.stream().sorted((s1,s2)->s2.compareTo(s1)).forEach(System.out::println);
//objects in descending order  
personList.stream().sorted((p1,p2)->p2.getName().compareTo(p1.getName())).forEach(p->System.out.println(p));
/*note :- 1.ByDefault sorted() on stream expects that the class should implement Comparable else you case use overloaded method sorted(Comparator<?> arg)
2.if class overrides comparable -& lambda also mention the sorted order by comparator - lambda's input will be given preference i.e i class has sort by age and lambda has sort by name - result will be sort by name*/

/*Also you can use the overloaded cmethod as follows : 
1.	personList.stream().sorted(Comparator.comparingInt(Person::getAge).reversed()).forEach(System.out::println);
2.		personList.stream().sorted(Comparator.comparing(Person::getName).reversed()).forEach(System.out::println);*/
  /**
     * 7.Given a list of strings, use streams and collectors to concatenate all the strings into a single comma-separated string.
     */
    private static void printListedOfStringAsSIgnleStringSpeatrtedByCOmma() {
        List<String> arrS=Arrays.asList(
                "Bullbog",
                "Abra",
                "Ka",
                "Dabra"
        );
        String commnSe=arrS.stream().collect(Collectors.joining(","));
        System.out.println(commnSe);
    }
   /**
     * 8.Write a program that demonstrates the use of parallel streams to process a large list of numbers and print the result as sum.
     */
    private static void processLargeListedOfIntWithParallelStream() {
        List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
       int sum= numberList.parallelStream().mapToInt(Integer::intValue).sum();
        System.out.println(sum);
    }

   /**
     *9.Given a list of lists of integers, use streams and flatMap to flatten the lists and print the unique integers.
     */
    private static void flatListOfLIstAndPrintUniqueValue() {
        List<List<Integer>> listOfLists = Arrays.asList(
                Arrays.asList(1, 2, 3),
                Arrays.asList(4, 5, 6),
                Arrays.asList(7, 8, 9)
        );
        listOfLists.stream().flatMap(List::stream).distinct().forEach(System.out::println);
    }

 /**
     *10.Write a program using streams that reads a list of numbers as strings, converts them to integers, and handles any NumberFormatException gracefully, printing an error message for invalid input..
     */   
 private static void handleExceptionWIthLambdaFOrStringToIntConversion() {
        List<String> numberStrings = Arrays.asList("10", "20", "30", "abc", "40");

            numberStrings.stream().map(Java8Features::convertToInt).filter(n -> n != null).forEach(System.out::println);

    }
//helper method in same class i.e Java8Features.java
   private static Integer convertToInt(String stringInterger) throws RuntimeException {
    try {
      return   Integer.parseInt(stringInterger);
    }catch (NumberFormatException nfe ){
        System.out.println("nfe"+nfe);
        return  null;

    }
    }

Executor Service

FAQs

Q-What do you understand by Executor Framework in Java

Executor Framework in java has been introduced in JDK 5. Executor Framework handles creation of thread, creating the thread pool and checking health while running and also terminates if needed.

2: What is the role of ExecutorService in Java?

ExecutorService provides different methods to start and terminate thread. There are two methods execute() and submit() in ExecutorService. Execute() method is used for threads which is Runnable and submit() method is used for Callable threads.

3: What is Executors in java Executor Framework?

Ans: Executors is a factory that provides the methods to return ExecutorService, ScheduledExecutorService, ThreadFactory. Find some method details.

newFixedThreadPool(): It returns the pool with fixed number of size. We need to pass the number of threads to this method. If concurrently task are submitted more than the pool size, then rest of task need to wait in queue. It returns ExecutorService.
newScheduledThreadPool: This also creates a fixed size pool but it can schedule the thread to run after some defined delay. It is useful to schedule the task. It returns ScheduledExecutorService.
newCachedThreadPool(): There is no fixed size of this pool. Thread will be created at run time and if there is no task it will alive for 60 second and then die. For short lived threads this pool works good. It returns ExecutorService.

4: What is the role of FutureTask and Future in java?

Ans: FutureTask is a cancellable asynchronous computation in java. It can cancel the task which is running. Once the FutureTask will be cancelled, it cannot be restarted. Future is result of asynchronous computation. Future checks if task is complete and if completed it gets the output.

5: What is difference between shutdownNow() and shutdown() in Executor Framework in java?

shutdown() and shutdownNow() methods belongs to ExecutorService. shutdown() method tries to stop the threads and do not accept new task to execute but it completes the execution which has been submitted. shutdownNow() methods also tries to stop the running threads and will not execute any task which has been submitted but not started.

6: How to terminate a thread in Executor Framework in java?

ExecutorService provides a method awaitTermination(long timeout, TimeUnit unit) that takes time and unit of time as an arguments. After that time thread pool is terminated. Suppose we need to terminate a task just now, then we can do as

ExecutorService.awaitTermination(0, TimeUnit.SECONDS)

How to get return value of a callable thread in java Executor Framework?

Ans: Using Future, we can get the return value of callable thread.

ExecutorService exService = Executors.newCachedThreadPool();
Future<Integer> future=exService.submit(new CallableThread());
int val=future.get();

Java Tutorial

Index

  1. About platform
    1. JDK/JRE/JVM
    2. JVM Internals Architecture
    3. JIT
    4. GCTypes
    5. GC algos
    6. JVM Config
  2. Java Core basics
    • Rules for identifiers
    • Auto-conversion
    • Static Keyword in java
    • this and super keyword
    • foreach syntax(enhanced for loop)
  3. Inheritance
    1. Diamond problem
    2. Polymorphism
      1. Static
      2. Dynamic
    3. instanceof keyword
    4. abstract keyword
    5. final keyword
    6. Special note on protected
    7. Access modifiers
  4. Class and interface
    1. Interface
    2. Abstract class vs interface
    3. Inner classes
      1. Instance inner class
      2. Static inner class
      3. method local inner class
    4. Enum
  5. String class
  6. Date-Time Handling (come up with exact method of using it)
  7. day-8 -Collections readme
  8. Collection

Introduction

Index

  1. Introduction – Note : released every 6 months i.e in march and September every year
    • LTS are generally supported for 4 years – it is more oriented towards stability than features ans had been in past versions
SnoJava VersionRelease DateSupport-DateLTS/STSMain-feature Additioncomment
1Java 5.0LTS – Covariant
– var-arg
2Java 8.0March 2014extended support until December 2030*LTS– Lambda
– Stream
– Optional
– Interface -Default/Static addition
3Java -11Released- Sept 2018extended support until September 2026LTS– HTTP Client (Standardized)
– var keyword
– String Methods (isBlank, lines, etc.)
Focused on APIs, performance, and GC;
4Java -17 Released- September 2021*extended support until September 2029LTS– Sealed classes
– record classes
Modern Java syntax (records, sealed classes), security tightening, pattern matching,
5Java-21Sept2023LTSMajor LTS with virtual threads, structured concurrency, foreign memory API, and pattern matching finalization—a powerful upgrade.
6Java-25Sept2025LTS– Full support for generics over primitives (Valhalla)
– Finalized string templates
– Universal generics
– Foreign function and memory API (Final)
– Pattern matching (Final)

Features in a version are release under one of following category:

  1. GA (General release)
    • production-ready versions containing all the finalized features
  2. Preview Features:These features are not considered final and could be changed, removed, or have their APIs modified in future releases.

Note- LTS has only GA release

Official release Notes:

Methods

Memory map

Objective

Following rules are described
  1. Item 49: Check parameters for validity
  2. Item 50: Make defensive copies when needed
  3. Item 51: Design method signatures carefully
  4. Item 52: Use overloading judiciously
  5. Item 53: Use varargs judiciously
  6. Item 54: Return empty collections or arrays, not nulls
  7. Item 55: Return optionals judiciously
  8. Item 56: Write doc comments for all exposed API elements