1.1 Java Different Version Features
| Java 8 | Java 11 | Java 21 |
|---|---|---|
|
|
|
1.2 Java 8 Features
Streams
Intermediate operations and terminal operations.
Functional Interfaces
Function, BiFunction, Consumer, BiConsumer, Supplier, Predicate, BiPredicate, UnaryOperator and BinaryOperator.
Lambda Expressions
Concise implementation of functional interfaces.
Method References
Concise references to methods.
Default / Static Methods
Default and static methods in interfaces.
Optional
Container class for optional values.
Date and Time API
java.time API.
Parallel Streams
Parallel stream processing.
CompletableFuture
Asynchronous programming support.
Collectors Utility
Collecting and reducing Stream results.
Stream Operations
| Category | Operations / API |
|---|---|
| Intermediate operations | filter (Predicate), map (Function), flatMap (Function<T, Stream<R>>), distinct, sorted (Comparator<T>), limit(n), skip(n), peek (Consumer<T>) |
| Terminal operations | collect(Collectors), forEach(Consumer), count(), anyMatch(Predicate<T>), allMatch(Predicate<T>), noneMatch(Predicate<T>), findFirst(), findAny(), reduce(BinaryOperator<T>) |
Java 8 Map New Features
| Method | Source-listed operation |
|---|---|
| forEach | Iterate over Map entries. |
| getOrDefault | Get a value or a default value. |
| putIfAbsent | Put a value when absent. |
| computeIfAbsent | Compute a value when absent. |
| computeIfPresent | Compute a value when present. |
| compute | Compute a new value. |
| merge | Merge a value with the existing value. |
| replace | Replace an existing value. |
| remove | Remove an entry. |
1.3 Aggregate (Reduction) Functions in Java 8 Streams
| Function | Description | Example |
|---|---|---|
count() |
Returns the count of elements in the stream. | long c = list.stream().count(); |
sum() |
Returns the sum (only for primitive streams). | int s = list.stream()
.mapToInt(i -> i)
.sum(); |
min(Comparator) |
Returns the minimum element using a comparator. | Optional<Integer> min =
list.stream().min(Integer::compare); |
max(Comparator) |
Returns the maximum element using a comparator. | Optional<Integer> max =
list.stream().max(Integer::compare); |
average() |
Returns the average of elements (primitive streams). | OptionalDouble avg =
list.stream()
.mapToInt(i -> i)
.average(); |
reduce(identity, accumulator) |
Reduces elements to a single value with identity. | int sum =
list.stream()
.reduce(0, (a, b) -> a + b); |
reduce(accumulator) |
Same as above but returns Optional<T>. |
Optional<Integer> total =
list.stream()
.reduce((a, b) -> a + b); |
collect(Collectors.summarizingInt()) |
Returns summary statistics: count, sum, min, max, average. | IntSummaryStatistics stats =
list.stream().collect(
Collectors.summarizingInt(i -> i)
); |
collect(Collectors.joining()) |
Joins String elements. | String joined =
list.stream().collect(
Collectors.joining(", ")
); |
groupingBy |
Groups elements by a classifier function and applies aggregation. | list.stream()
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
); |
1.4 Optional
The Optional class in Java 8 is a container object used to contain not-null objects. Optional is part of the java.util package and was introduced to reduce the risk of NullPointerException and provide a better way to handle optional values.
| Method | Return Type | Description |
|---|---|---|
empty() | Optional<T> | Returns an empty Optional instance. |
of(T value) | Optional<T> | Returns Optional with non-null value; throws NullPointerException if null. |
ofNullable(T value) | Optional<T> | Returns Optional of value or empty if null. |
get() | T | Returns value if present; throws NoSuchElementException if not. |
isPresent() | boolean | Returns true if value is present. |
ifPresent(Consumer<T> action) | void | Executes action if value is present. |
ifPresentOrElse(Consumer, Runnable) | void | Added in Java 9. Executes action if present, else runs Runnable. |
filter(Predicate<T>) | Optional<T> | Returns same Optional if predicate matches, else empty. |
map(Function<T,R>) | Optional<R> | Applies mapper function if value is present. |
flatMap(Function<T, Optional<R>>) | Optional<R> | Similar to map but avoids nested Optionals. |
orElse(T other) | T | Returns value if present; else returns other. |
orElseGet(Supplier<T>) | T | Returns value if present; else returns value from Supplier. |
orElseThrow() | T | Returns value if present; else throws NoSuchElementException. |
orElseThrow(Supplier<E>) | T | Returns value or throws exception from supplier. |
Optional Example
Optional<String> name =
Optional.of("Srikanth");
name.ifPresent(
n -> System.out.println(
n.toUpperCase()
)
);
String result =
name.orElse("Default");
System.out.println(result);
Optional<String> emptyName =
Optional.empty();
String result2 =
emptyName.orElse("Default");
System.out.println(result2);
When to Use Optional
- As a return type when the result might be absent.
- To replace null checks.
1.5 Lambda Expression
Lambda expressions provide a concise way to implement the abstract method of a functional interface.
Lambda expressions let you write shorter, cleaner, and more expressive code, especially for functional interfaces.
They are extensively used with Streams, Collections, and multithreading.
1.6 Functional Interfaces (SAM)
A functional interface is an interface that contains exactly one abstract method.
These interfaces are used primarily with lambda expressions and method references, enabling you to write cleaner and more concise code.
| Functional Interface | Example |
|---|---|
Function<T, R> |
Function<String, Integer> lengthFunction =
str -> str.length();
System.out.println(
lengthFunction.apply("Sree")
); |
BiFunction<T, U, R> |
BiFunction<Integer, Integer, Integer> sum =
(a, b) -> a + b;
System.out.println(
sum.apply(5, 3)
); |
Consumer<T> |
Consumer<String> consumer =
name -> System.out.println(
"Hello, " + name
);
consumer.accept("Alice"); |
BiConsumer<T, U> |
BiConsumer<String, Integer> print =
(name, age) ->
System.out.println(
name + " is " + age +
" years old"
);
print.accept("Bob", 30); |
Supplier<T> |
Supplier<String> supplier =
() -> "Hello from Supplier!";
System.out.println(
supplier.get()
); |
Predicate<T> |
Predicate<String> isLongerThan5 =
s -> s.length() > 5;
System.out.println(
isLongerThan5.test("Hello")
); |
BiPredicate<T, U> |
BiPredicate<String, String> isEqual =
(a, b) -> a.equalsIgnoreCase(b);
System.out.println(
isEqual.test("Java", "java")
); |
UnaryOperator<T> |
UnaryOperator<String> toUpperCase =
s -> s.toUpperCase();
System.out.println(
toUpperCase.apply("hello")
); |
BinaryOperator<T> |
BinaryOperator<Integer> multiply =
(a, b) -> a * b;
System.out.println(
multiply.apply(4, 5)
); |
Runnable | Listed in the source as a functional interface. |
Callable | Listed in the source as a functional interface. |
Comparable | Listed in the source as a functional interface. |
Comparator | Listed in the source as a functional interface. |
Predicate Example
Predicate<Integer> isEven =
number -> number % 2 == 0;
System.out.println(
"Is 10 even? " +
isEven.test(10)
);
Function Example
Function<String, Integer> stringLength =
str -> str.length();
System.out.println(
"Length of 'Hello': " +
stringLength.apply("Hello")
);
1.7 Collection Internal Data Structure
Java8_Features.htm contains an embedded image for this section. The referenced image asset is not included as a separately accessible file in the uploaded HTML, so the visual diagram has not been recreated here.
Collection Internal Data Structure
This section is retained from the source document as a dedicated topic placeholder so the converted HTML preserves the original document structure.
1.8 Comparable vs Comparator
| Aspect | Comparable | Comparator |
|---|---|---|
| Package | java.lang | java.util |
| Purpose | Defines natural ordering of objects | Defines custom ordering of objects |
| Method | int compareTo(T o) | int compare(T o1, T o2) |
| Implemented By | The class itself | A separate class or lambda |
| Affects Original Class? | Yes — class must implement it | No — can be defined externally |
| Used With | Collections.sort(list) or Arrays.sort(array) | Same, with custom comparator passed as argument |
| Java 8 Enhancements | No major change | Supports lambda expressions and default methods like comparing, thenComparing |
| Example Use Case | Sorting employees by id (natural order) | Sorting employees by name, salary, etc. (custom) |
1.13.1 Comparator Java 8 Enhancements
Comparator in Java 8 is more powerful. You can use static methods like:
people.sort(
Comparator.comparing(Person::getAge)
.thenComparing(Person::getName)
);
people.forEach(
person ->
System.out.println(
person.getName()
)
);
List<Integer> sortedArrayList =
al.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
int k = 2;
System.out.println(
sortedArrayList.get(k - 1)
);
1.9 Method References
A method reference is essentially a shorthand notation for calling a method using a lambda expression.
Method references can be used in the context of functional interfaces (interfaces with a single abstract method), commonly used in places where lambda expressions are applicable.
Types of Method References
Reference to a Static Method
A static method in a class can be referred to with ClassName::staticMethodName.
Reference to an Instance Method of a Particular Object
You can refer to an instance method of an object using objectInstance::instanceMethodName.
Reference to an Instance Method of an Arbitrary Object of a Particular Type
This is useful when you don't have a specific object but want to refer to an instance method of an object of a specific type. It is often used with Streams.
Reference to a Constructor
You can refer to a constructor using the ClassName::new syntax. This is particularly useful when using factory methods or Streams.
Method references provide a more concise and expressive way to refer to methods and are especially useful with Java's functional programming features (like Streams). They can simplify your code by replacing lambda expressions where the lambda body is just calling a method.