HCL RealTimeInterviewRound2
HCL RealTimeInterviewRound2
stream, ensuring that each element in the resulting stream is unique. It is part of
the **Stream API** introduced in **Java 8**.
When you invoke `distinct()` on a stream, it filters out repeated elements based on
their **natural equality** (using `equals()` method) and returns a new stream where
each element appears only once.
### Syntax:
```java
Stream<T> distinct();
```
- The `distinct()` method operates on a `Stream` and returns a new `Stream` that
contains only distinct elements.
- The underlying mechanism that determines distinctness is the **`equals()`**
method of the objects in the stream.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### Explanation:
- We create a list of integers with some duplicate values.
- By calling `numbers.stream().distinct()`, we create a stream where only unique
elements remain.
- The `collect(Collectors.toList())` operation collects the results into a `List`,
and the final output contains the distinct elements.
If you're working with custom objects (e.g., `Person` objects), the `distinct()`
method uses the `equals()` method to determine uniqueness. So, if you want to use
`distinct()` with custom objects, you must override the `equals()` and `hashCode()`
methods to define what makes two objects equal.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Person {
String name;
int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
### Explanation:
- We define a `Person` class with fields `name` and `age`.
- We override the `equals()` and `hashCode()` methods to ensure that two `Person`
objects are considered equal if they have the same `name` and `age`.
- By calling `distinct()` on the stream of `Person` objects, duplicates based on
`name` and `age` are removed.
### Performance Considerations:
- The `distinct()` operation internally uses a `HashSet` to track already
encountered elements, so it generally performs well, with a time complexity of
**O(n)** where **n** is the number of elements in the stream.
- However, you should be mindful of the performance cost of the `equals()` and
`hashCode()` methods for custom objects, as these operations will be invoked to
check for duplicates.
---
### Summary:
- The `distinct()` method in Java is used to eliminate duplicate elements from a
stream.
- It uses the `equals()` method to check for duplicates and retains only the unique
elements in the resulting stream.
- It's commonly used in scenarios where you want to filter out redundant data,
either for primitive values or custom objects (as long as `equals()` and
`hashCode()` are properly overridden).
----------------------------------------------------------------------
to filter List of duplicate elements either we can use Set Interface or we can go
with Stream Api distinct().
====================================================
In Java 8's **Stream API**, operations are classified into two types:
1. **Intermediate Operations**
2. **Terminal Operations**
These operations are used to process streams of data in a functional style. The key
difference between intermediate and terminal operations is that **intermediate
operations** are **lazy** and return a new stream, while **terminal operations**
are **eager** and consume the stream to produce a result (e.g., a collection, a
primitive value, etc.).
In this example:
- The `filter()` operation is an intermediate operation that filters out the odd
numbers.
- The `map()` operation transforms each even number by multiplying it by 2.
- The `distinct()` operation ensures that no duplicates are included.
### Explanation:
- **`forEach()`**: This terminal operation performs an action on each element. In
this case, it prints each number.
- **`count()`**: This terminal operation counts the number of elements in the
stream that match the filter condition.
- **`reduce()`**: This terminal operation is used to aggregate elements into a
single value (in this case, summing all the numbers).
- **`findFirst()`**: This terminal operation retrieves the first element of the
stream that matches the condition.
### Summary:
Internally, `HashMap` works based on a **hash table** and uses the **hashing**
mechanism to quickly locate keys and their associated values.
Let’s break down the key components and how the `HashMap` functions internally:
---
---
When a key-value pair is added to the `HashMap` using the `put()` method, the
following steps are performed:
3. **Handle Collisions**:
- If the bucket is empty, the new `Node` (key-value pair) is inserted.
- If the bucket already contains other entries, a collision occurs:
- **Linked List**: In earlier versions of Java, a linked list is formed in the
bucket to hold all elements with the same hash value (this is a simple collision
resolution strategy).
- **Balanced Tree**: Starting from Java 8, if the number of elements in a
bucket exceeds a threshold (usually 8), a **Red-Black Tree** is used for improved
performance (O(log n) lookup time).
---
The `get()` operation retrieves the value associated with the provided key:
1. **Compute the Hash Code**:
- The hash code of the key is computed again using the `hashCode()` method of
the key.
4. **Balanced Tree (if used)**: If the bucket uses a balanced tree, the search time
is O(log n), which is better than the O(n) search time in a linked list.
---
The `remove()` method works similarly to `get()`, but instead of returning the
value, it removes the key-value pair:
1. **Compute the Hash Code**: The hash code of the key is computed.
2. **Locate the Bucket**: The bucket index is determined using the hash code.
3. **Traverse the Bucket**:
- If a matching key is found, the corresponding key-value pair is removed.
- If there are other nodes in the same bucket (due to collisions), the list/tree
is updated to remove the node.
---
---
When two keys have the same hash code, they are considered "colliding" and end up
in the same bucket. This can reduce the performance of `get()` and `put()`
operations.
---
---
```java
import java.util.HashMap;
// Put operation
map.put("A", "Apple");
map.put("B", "Banana");
map.put("C", "Cherry");
// Get operation
System.out.println(map.get("A")); // Output: Apple
// Remove operation
map.remove("B");
### Conclusion:
- **HashMap** in Java works by using an array of "buckets" where each bucket is an
index in the array.
- A **hash function** computes an index based on the hash code of the key, and this
index determines where to store the key-value pair.
- Collisions are handled by either a **linked list** or a **Red-Black Tree** (in
newer versions of Java) to maintain performance.
- The map resizes when the load factor exceeds a threshold, which helps keep
operations efficient.
=========================
Sure! Here’s a **straightforward** explanation of why we need to override
`hashCode()` and `equals()` in Java:
1. **`hashCode()`**:
- It generates a unique number (hash) for an object, which helps hash-based
collections (like `HashMap`, `HashSet`) to quickly find objects.
- If we use custom objects as keys in a `HashMap` or elements in a `HashSet`, we
need to ensure that objects with the same "logical equality" (e.g., two people with
the same name and age) have the same hash code. If we don’t, the collection will
fail to find or compare them correctly.
2. **`equals()`**:
- It checks if two objects are "logically equal" based on their **content**
(e.g., if two `Person` objects have the same name and age).
- The default `equals()` compares objects by **reference** (memory address),
which means two objects with the same content may be considered "not equal." This
is not what you want when comparing custom objects for logical equality (e.g., two
`Person` objects with the same name and age).
---
### Example
```java
public class Person {
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Overriding hashCode to ensure objects with same name and age have the same
hash code
@Override
public int hashCode() {
return 31 * name.hashCode() + age;
}
}
```
---
### Summary:
- **Override `equals()`**: To define how two objects are logically equal.
- **Override `hashCode()`**: To ensure that logically equal objects have the same
hash code.
- **Why?**: To ensure that hash-based collections (like `HashMap`, `HashSet`) work
correctly, as they rely on both methods to store and retrieve objects efficiently.
=======================
To create **immutable objects** in Java, you need to follow a few specific rules
and guidelines to ensure that the object's state cannot be changed after it is
created.
```java
public final class Person {
// Fields, constructor, methods
}
```
```java
public final class Person {
private final String name;
private final int age;
}
```
```java
public final class Person {
private final String name;
private final int age;
```java
public final class Person {
private final String name;
private final int age;
// No setters allowed
}
```
For example, if the class has a `List` or `Date` as a field, you should not
expose it directly; instead, return a **new copy** of the object or **unmodifiable
view** of the collection.
```java
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
```
- **Class is `final`**: This prevents subclassing and keeps the class immutable.
- **Fields are `private` and `final`**: This ensures the fields are not directly
accessible or modifiable after the object is created.
- **No setter methods**: Since we don’t allow modification of the object’s state,
no setters are provided.
- **Deep copy for mutable fields**: The list of hobbies is copied and wrapped in an
unmodifiable collection to prevent external modification.
3. **Predictable Behavior**: Since their state can’t change, immutable objects are
easier to reason about and debug, especially in large applications.
### **Summary:**
Immutable objects are useful for ensuring thread-safety, reliability, and ease of
maintenance.
============================
### **Creational Design Patterns: Overview**
Creational design patterns are a category of design patterns that focus on **object
creation mechanisms**, trying to create objects in a manner suitable to the
situation. These patterns abstract the instantiation process, making it more
flexible and efficient.
1. **Singleton**
2. **Factory Method**
3. **Abstract Factory**
4. **Builder**
5. **Prototype**
---
#### **Definition:**
The **Singleton Pattern** ensures that a class has only one instance, and provides
a global point of access to that instance.
```java
public class Singleton {
#### **Explanation:**
- The `Singleton` class ensures only one instance is created.
- The `getInstance()` method is the key; it ensures that the instance is created
only when it's needed.
- The constructor is private, preventing instantiation from outside the class.
---
#### **Definition:**
The **Factory Method Pattern** provides an interface for creating objects, but the
**subclasses** decide which class to instantiate. It allows subclasses to alter the
type of objects that will be created.
```java
// Product interface
interface Animal {
void speak();
}
// Concrete products
class Dog implements Animal {
public void speak() {
System.out.println("Woof!");
}
}
// Creator class
abstract class AnimalFactory {
public abstract Animal createAnimal();
}
#### **Explanation:**
- The `AnimalFactory` defines a **factory method** (`createAnimal`) which is
implemented by subclasses (`DogFactory`, `CatFactory`).
- This allows the creation of different products (`Dog`, `Cat`) without altering
the client code.
---
#### **Definition:**
The **Abstract Factory Pattern** provides an interface for creating families of
related or dependent objects without specifying their concrete classes. It is
essentially a **factory of factories**.
```java
// Abstract product types
interface Button {
void render();
}
interface Checkbox {
void render();
}
// Concrete factories
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
#### **Explanation:**
- `GUIFactory` is an abstract factory that defines methods for creating `Button`
and `Checkbox`.
- Concrete factories (`WindowsFactory`, `MacFactory`) implement the creation
methods for specific platform UI components.
- The client code can switch between different families of UI components without
knowing the specific classes being used.
---
#### **Definition:**
The **Builder Pattern** is used to construct a complex object step by step. The
builder pattern allows you to create a **product** using the same construction
process, but different configurations.
```java
class Computer {
private String CPU;
private String RAM;
private String storage;
private String GPU;
@Override
public String toString() {
return "Computer [CPU=" + CPU + ", RAM=" + RAM + ", Storage=" + storage +
", GPU=" + GPU + "]";
}
}
// Builder class
class ComputerBuilder {
private String CPU;
private String RAM;
private String storage;
private String GPU;
#### **Explanation:**
- The `ComputerBuilder` class allows you to **set individual parts** of the
`Computer` object step by step.
- The builder provides a fluent interface, so you can chain method calls to build a
fully constructed object.
---
#### **Definition:**
The **Prototype Pattern** is used to create new objects by **copying** or
**cloning** an existing object (the prototype). This is useful when the creation of
new objects is costly and time-consuming.
```java
// Prototype interface
interface Cloneable {
Cloneable clone();
}
// Concrete Prototype
class PrototypeExample implements Cloneable {
private String name;
private int age;
@Override
public Cloneable clone() {
return new PrototypeExample(this.name, this.age);
}
}
#### **Explanation:**
- The `PrototypeExample` class implements the `Cloneable` interface and provides
the `clone()` method.
- The `clone()` method creates a **new object** with the same properties as the
original object.
---
These patterns provide flexible and reusable solutions to object creation, making
your code more adaptable and easier to maintain.
=============================
Throwing a custom exception in a REST API response in a Spring Boot application
involves several steps. The key goal is to handle exceptions in a way that is
informative and consistent across all the API responses.
First, create a custom exception class. This will represent the specific error
scenario in your application.
```java
package com.example.demo.exception;
This `CustomNotFoundException` takes the resource name, the field name, and the
value of the field that was not found. The message is constructed in the
constructor to make it dynamic based on these values.
---
You can use the `@ControllerAdvice` annotation to define a global exception handler
that will handle exceptions thrown in any controller.
```java
package com.example.demo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
// Handle CustomNotFoundException
@ExceptionHandler(CustomNotFoundException.class)
public ResponseEntity<ErrorResponse>
handleCustomNotFoundException(CustomNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
ex.getResourceName(),
ex.getFieldName(),
ex.getFieldValue()
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
The `ErrorResponse` class (which you will define next) will hold the structure of
the error message.
---
The `ErrorResponse` class is a POJO (Plain Old Java Object) that holds the
structure of the error response. It will contain information such as the error
code, message, and other relevant details.
```java
package com.example.demo.exception;
This class is used to structure the response returned to the client when an
exception occurs.
---
Now that you have your custom exception and handler in place, you can throw the
`CustomNotFoundException` in your controller whenever necessary.
```java
package com.example.demo.controller;
import com.example.demo.exception.CustomNotFoundException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
@GetMapping("/products/{id}")
public String getProductById(@PathVariable String id) {
// Simulating an invalid product ID scenario
if ("123".equals(id)) {
return "Product found";
} else {
throw new CustomNotFoundException("Product", "id", id);
}
}
}
```
---
**Successful Response:**
For a valid request, the response could be something like this (assuming `id=123`):
```json
{
"id": "123",
"name": "Product A",
"price": 99.99
}
```
**Error Response (404 Not Found):**
For an invalid request, such as `id=456`, where no product is found, the response
would look like:
```json
{
"statusCode": 404,
"message": "Product not found with id : '456'",
"resourceName": "Product",
"fieldName": "id",
"fieldValue": "456"
}
```
---
- **Better Error Responses**: With custom exceptions, you can structure your error
responses in a way that the client can easily understand and react to, for example,
showing a user-friendly error message in the UI or logging it for analysis.
---
### **Summary**
Without a factory pattern, the client would need to directly instantiate the
appropriate payment method class. This can quickly become messy and difficult to
maintain as the number of payment methods grows.
// PaymentFactory class
public class PaymentFactory {
public static Payment getPaymentMethod(String type) {
if ("CreditCard".equalsIgnoreCase(type)) {
return new CreditCardPayment();
} else if ("PayPal".equalsIgnoreCase(type)) {
return new PayPalPayment();
} else if ("Bitcoin".equalsIgnoreCase(type)) {
return new BitcoinPayment();
}
throw new IllegalArgumentException("Unknown payment type");
}
}
// Client code
public class PaymentProcessor {
public static void main(String[] args) {
Payment payment = PaymentFactory.getPaymentMethod("PayPal");
payment.processPayment(); // Output: Processing PayPal Payment
}
}
```
### **Conclusion**
The **Factory Design Pattern** is used to **simplify object creation** and make
your code more flexible and maintainable by decoupling object creation from the
client code. It helps manage complex object creation and ensures that clients don’t
need to know about specific concrete classes. Instead, they rely on the factory to
create objects, thus promoting loose coupling and greater maintainability in the
system.
=============================
In Spring, **`RestTemplate`** was the primary client for making HTTP requests to
RESTful web services. However, since **Spring 5**, `RestTemplate` has been
**deprecated** in favor of a more modern and reactive alternative — **WebClient**.
```java
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
---
```java
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
---
- **OkHttp**: A modern and efficient HTTP client for Java, which is often used for
its ease of use and low overhead. It also supports synchronous and asynchronous
calls.
---
- **For Legacy Code**: If you already have code using `RestTemplate` and are not
yet using reactive programming, you may continue using `RestTemplate` for the time
being, but consider migrating to `WebClient` when possible.
- **For Simplicity**: If you need a simple HTTP client and are not using Spring,
libraries like **OkHttp** or **Apache HttpClient** can also be great options.
- **For Declarative API Calls**: If you are working within the **Spring Cloud**
ecosystem, **Feign** is a good option as it allows you to declare API clients in a
more declarative manner.
---
### Conclusion
In summary:
- **`RestTemplate`** is still widely used but is now deprecated in favor of
**`WebClient`**.
- **`WebClient`** is a more modern, flexible, and non-blocking alternative to
`RestTemplate`, and it's the preferred choice for new applications, especially if
you're working with reactive programming.
- **Other options** like **OkHttp**, **Apache HttpClient**, and **Feign** are also
available depending on your use case, but **WebClient** is recommended for Spring
applications.
===================================
In **`RestTemplate`**, HTTP headers are often used to provide additional
information in the request, such as authentication credentials, content types, and
custom headers. You can set headers using the **`HttpHeaders`** class and then use
them in an **`HttpEntity`**, which is passed along with the request to the server.
Here’s an example of how to send a `GET` request with custom headers using
`RestTemplate`.
```java
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
Here’s an example of how to send a `POST` request with headers and a request body
using `RestTemplate`.
```java
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
public class RestTemplatePostExample {
public static void main(String[] args) {
// Create a RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
You can also add multiple headers in the `HttpHeaders` object. Here’s an example
with multiple headers:
```java
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;
---
### Conclusion:
To summarize, you can set HTTP headers in **`RestTemplate`** by creating an
`HttpHeaders` object, wrapping it inside an `HttpEntity`, and passing the
`HttpEntity` to methods like `exchange()`. This provides flexibility when working
with APIs that require custom headers such as authentication tokens or content
types.
=======================================
The **`filter()`** method in Java's Stream API is an intermediate operation that
allows you to filter elements from a stream based on a given predicate (a
condition). It produces a new stream that contains only the elements that satisfy
the condition specified in the predicate.
### **Syntax:**
```java
Stream<T> filter(Predicate<? super T> predicate);
```
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### **Explanation:**
- We create a stream from the list `numbers`.
- We use the `filter()` method with the predicate `n -> n % 2 == 0` to select only
the even numbers.
- The filtered elements are then collected into a new list using the `collect()`
method with `Collectors.toList()`.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### **Explanation:**
- The stream is created from a list of strings.
- We use the `filter()` method with the predicate `word -> word.length() > 4` to
select only the words that have more than 4 characters.
- The filtered list is collected using `Collectors.toList()`.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### **Explanation:**
- We first filter for even numbers (`n -> n % 2 == 0`).
- Then, we further filter the even numbers to get only those greater than 5 (`n ->
n > 5`).
- The resulting list contains `[6, 8, 10]`.
```java
List<Integer> filteredNumbers = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
```
This will filter the even numbers in parallel, which might improve performance in
multi-core processors.
---
### **Conclusion:**
The `filter()` method in Java Streams is used to select elements that match a
specific condition, making it a powerful tool for working with collections and
streams. It provides a declarative way of expressing filtering logic and is
commonly used in data transformation and querying tasks.
=============================================
The `map()` method in Java Streams is an **intermediate operation** that is used to
transform each element of the stream into another form. It takes a **Function** (a
function that applies a transformation) as an argument and applies that function to
each element of the stream, producing a new stream of transformed elements.
### **Example 1: Mapping from One Type to Another (e.g., Integer to String)**
Let’s say you have a list of integers, and you want to convert each integer to a
string.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### **Explanation:**
- The stream of integers is transformed into a stream of strings using the `map()`
method.
- We use `String::valueOf` as the function to convert each integer to a string.
- The transformed stream is collected into a list of strings using `collect()`.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
### **Explanation:**
- We apply the transformation `n -> n * n` to square each number in the stream.
- The resulting stream of squared numbers is collected into a list.
Suppose we have a list of `Person` objects, and we want to extract the names of all
persons into a list of strings.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
### **Explanation:**
- We have a stream of `Person` objects.
- The `map()` method extracts the `name` property from each `Person` object using
`Person::getName`.
- The transformed stream of names is collected into a list.
You can chain multiple `map()` calls to apply multiple transformations in sequence.
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
// Convert each word to uppercase and then find the length of each word
List<Integer> wordLengths = words.stream()
.map(String::toUpperCase) // Convert
to uppercase
.map(String::length) // Get
length of each word
.collect(Collectors.toList());
### **Explanation:**
- First, we apply the transformation `String::toUpperCase` to each word.
- Then, we apply `String::length` to each word to get its length.
- The resulting stream of word lengths is collected into a list.
---
### **Conclusion:**
The `map()` method in Java Streams is a powerful way to transform data in a stream
from one type to another or to modify the elements. It is commonly used for tasks
such as converting elements to another type, extracting specific properties from
objects, or applying transformations like mathematical operations or string
manipulations.
===============================
The issue in the code is that you are calling `Integer::max` inside the `map()`
function, which is not correct because `Integer::max` is a **static method** that
requires two arguments, but `map()` expects a function that operates on a single
argument. This will result in a compilation error.
If you want to get the maximum value of each element in the stream, you should
apply a different approach. Here's how you can correct and improve your code:
If the goal is to filter numbers greater than 100 and then map them to their
**maximum value** (relative to a given number), you would need to use `map()`
correctly. For example, if you want to map each number to its maximum with some
other value, you can do something like `map(n -> Integer.max(n, 100))`.
However, if the intent is to simply filter numbers greater than 100, here's a
simplified and corrected version:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
// Filter numbers greater than 100 and then map them to their maximum value
compared to 100
List<Integer> max = list.stream()
.filter(n -> n > 100) // Filter
numbers greater than 100
.map(n -> Integer.max(n, 100)) // Apply the
Integer.max transformation
.collect(Collectors.toList()); // Collect the
result into a list
### **Explanation:**
- **Filtering:** The `filter(n -> n > 100)` step filters out numbers less than or
equal to 100.
- **Mapping:** The `map(n -> Integer.max(n, 100))` step maps the remaining numbers
to their maximum value between the number and 100. This is redundant in this case,
because we already filtered numbers greater than 100, but it demonstrates how to
use `Integer::max`.
- **Collecting:** Finally, the resulting stream is collected into a list using
`.collect(Collectors.toList())`.
If you want to **just filter numbers greater than 100**, then you can omit the
`map()` step and just use:
```java
List<Integer> max = list.stream()
.filter(n -> n > 100)
.collect(Collectors.toList());
```
This will produce the list `[112, 134, 234]` without applying the `Integer.max`
transformation.
### **Summary:**
- The issue in the original code was trying to use `Integer::max` in a way that
wasn't compatible with `map()`. `Integer::max` is a binary method, and `map()`
requires a function that works on a single argument.
- The solution involves using `Integer.max` correctly with a lambda expression or
directly collecting the filtered values.
=========================
Ah, I see! If your goal is to first filter out numbers greater than 100 and then
find the **maximum** value among those numbers, you can do that easily using the
**`filter()`** method to first filter the numbers, and then use the **`max()`**
method from the **`Stream`** API to get the maximum number from the filtered
results.
To do this, you can use the `max()` method, which is a terminal operation, and it
requires a **comparator** to compare the elements.
Here’s how you can do it:
### **Solution:**
```java
package com.InterviewPrep;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
// Filter numbers greater than 100 and then find the maximum number
Optional<Integer> max = list.stream()
.filter(n -> n > 100) // Filter
numbers greater than 100
.max(Integer::compareTo); // Get the
maximum number
### **Explanation:**
1. **`filter(n -> n > 100)`**: This filters out the numbers in the list that are
**greater than 100**.
2. **`max(Integer::compareTo)`**: This finds the **maximum** value in the filtered
stream by comparing the numbers. `Integer::compareTo` is a method reference that
compares two integers.
- The `max()` method returns an `Optional<Integer>`, because the stream may be
empty (for example, if there are no elements greater than 100).
3. **`ifPresent(System.out::println)`**: Since `max()` returns an `Optional`, we
use `ifPresent()` to print the maximum value if it's present. If the stream is
empty (i.e., no numbers greater than 100), nothing will be printed.
```java
max.ifPresentOrElse(
System.out::println, // Print the max value if present
() -> System.out.println("No number greater than 100") // If empty, print a
default message
);
```
```java
int maxNumber = max.orElse(0); // If no max found, return 0
System.out.println(maxNumber); // Output: 234 or 0 if no number > 100
```
### **Summary:**
- First, filter the numbers greater than 100 using `filter()`.
- Then, use the `max()` method to get the maximum of the filtered numbers.
- Since `max()` returns an `Optional`, you can handle the possibility of an empty
stream safely.
In this example, the output will be `234` because it is the largest number greater
than 100 in the list.
==================================
In the context of web APIs, **Query Parameters** and **Path Parameters** are two
ways to pass data in HTTP requests. They are used in different ways and serve
different purposes.
- **When to Use:** Use path parameters when the data being passed is essential to
locate a specific resource.
- For example, retrieving a specific user or item: `/users/{userId}` or
`/products/{productId}`.
- **When to Use:** Query parameters are used when the data is **optional** and used
to modify or filter the response of the API.
- For example, for filtering results: `/search?query=apple&limit=10` or for
pagination: `/items?page=2&size=20`.
```plaintext
GET /users/12345/orders?status=shipped&date=2024-01-01
```
- Here:
- `/users/12345` is a **path parameter** identifying a specific user.
- `status=shipped` and `date=2024-01-01` are **query parameters** that filter the
orders for that user.
### **Conclusion:**
- **Path parameters** are part of the URL and are typically used for identifying a
specific resource (such as a user or a product).
- **Query parameters** are used to provide additional optional details (such as
filtering, sorting, or pagination) and are placed after the `?` in the URL.
====================================================
### What is OpenAPI?
2. **Tooling**:
OpenAPI is supported by a wide array of tools for tasks like:
- **Documentation Generation**: Automatically generate user-friendly API
documentation.
- **Client SDK Generation**: Generate client libraries for various programming
languages.
- **Server Stub Generation**: Generate server-side skeletons in different
programming languages.
- **Testing and Validation**: Tools like Swagger UI, Swagger Codegen, and
Postman can be used for testing APIs defined with OpenAPI.
4. **Language Agnostic**:
OpenAPI is **language agnostic** and can be used with any programming language,
making it a flexible choice for a variety of development ecosystems.
5. **Swagger UI**:
OpenAPI definitions can be used to generate interactive documentation through
tools like **Swagger UI**. This allows users to test API endpoints directly from
the documentation.
1. **Info**: Contains metadata about the API such as title, version, description,
etc.
```yaml
info:
title: Pet Store API
description: A sample API for managing a pet store
version: 1.0.0
```
2. **Paths**: Defines the available endpoints and their operations (GET, POST, PUT,
DELETE, etc.).
```yaml
paths:
/pets:
get:
summary: Get a list of pets
responses:
'200':
description: A list of pets
```
3. **Parameters**: Specifies the input parameters (query, path, header, body, etc.)
for each endpoint.
```yaml
parameters:
- in: query
name: petType
description: Type of pet to search for
required: false
schema:
type: string
```
```yaml
responses:
'200':
description: A list of pets
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
```
```yaml
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
name:
type: string
```
```yaml
security:
- api_key: []
```
```yaml
servers:
- url: https://api.example.com/v1
```
1. **Standardization**:
OpenAPI provides a consistent way to describe APIs. This standardization ensures
that developers and tools can easily interpret and interact with APIs, regardless
of the technology stack used to build them.
2. **Tooling Ecosystem**:
The OpenAPI specification has a large ecosystem of tools for testing,
documentation, client code generation, and server stub generation, which can save
time and effort during development.
3. **Interactive Documentation**:
With OpenAPI, you can generate **interactive documentation** (e.g., using
**Swagger UI**), which allows developers to try out the API directly from the
documentation page.
5. **Code Generation**:
OpenAPI can generate client SDKs and server stubs in multiple languages, making
it easy to integrate APIs with other systems and services.
6. **Cross-team Collaboration**:
Since OpenAPI provides a standardized format, teams working on different parts
of an application (e.g., backend and frontend teams) can easily collaborate by
sharing the same API documentation.
7. **Automated Testing**:
With OpenAPI, you can automate tests based on the API specification to validate
that the actual API implementation conforms to the specification.
### **OpenAPI Example:**
```yaml
openapi: 3.0.0
info:
title: Pet Store API
description: A sample API for managing a pet store
version: 1.0.0
paths:
/pets:
get:
summary: Get all pets
responses:
'200':
description: A list of pets
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
/pets/{petId}:
get:
summary: Get a pet by ID
parameters:
- name: petId
in: path
required: true
description: ID of the pet to fetch
schema:
type: integer
responses:
'200':
description: A pet object
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
name:
type: string
species:
type: string
```
4. **Server Stub Generation**: Server-side code can also be generated using the
OpenAPI specification. This provides a skeleton server implementation, which can be
further developed and customized.
### **Conclusion:**
OpenAPI is a powerful specification for describing and documenting REST APIs. It
promotes **standardization**, **automation**, and **interoperability** across
different teams and technologies. By using OpenAPI, you can improve the process of
designing, consuming, and maintaining APIs, and take advantage of the large
ecosystem of tools available for code generation, testing, and documentation.
==================================================