Java Interview question-11
3
Question 101: How to create an immutable class in java?
An immutable class in Java is a class whose objects cannot be modified once they are created. Here are the steps to create an immutable class in Java:
Declare the class as final: By declaring the class as final, you prevent other classes from extending it and modifying its behavior.
Declare all instance variables as final: By declaring all instance variables as final, you prevent them from being modified once they are initialized.
Do not provide any setter methods: Since the instance variables are final, you cannot change their values using setter methods. Therefore, it is recommended to not provide any setter methods in an immutable class.
Initialize all instance variables in the constructor: Since the instance variables are final, they must be initialized in the constructor. This can be done either by passing the values as arguments to the constructor or by initializing them with constants or other immutable objects.
Do not allow subclasses to override methods: To ensure that the behavior of an immutable class remains consistent, it is recommended to declare all methods as final. This prevents subclasses from overriding them and modifying their behavior.
Here’s an example of an immutable class in Java:
public final class MyImmutableClass {
private final int value1;
private final String value2;
public MyImmutableClass(int value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public int getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
}
Question 102: What are the in-built immutable classes in java?
In Java, there are several built-in classes that are immutable, meaning their state cannot be changed once they are created. Here are some of the commonly used immutable classes in Java:
String: The String class represents a sequence of characters and is one of the most commonly used classes in Java. Once a String object is created, its contents cannot be modified.
Integer, Long, Double, Float, Short, Byte, and Character: These classes represent primitive data types and are also immutable. Once an object of any of these classes is created, its value cannot be changed.
BigDecimal and BigInteger: These classes are used for performing arithmetic operations on large numbers and are also immutable.
LocalDate, LocalTime, LocalDateTime, and Instant: These classes represent dates and times and are also immutable. They are part of the java.time package introduced in Java 8.
java.util.Optional: This class is used to represent a value that may or may not be present, and is also immutable.
Immutable classes have several benefits, such as being thread-safe and easy to reason about. They also make it easier to write high-quality, bug-free code.
Question 103: How to create an immutable map in java?
In Java, you can create an immutable map using the Collections
class. Here are the steps to create an immutable map:
Create a map with the desired key-value pairs:
Map<String, Integer> mutableMap = new HashMap<>();
mutableMap.put("apple", 1);
mutableMap.put("banana", 2);
mutableMap.put("orange", 3);
Use the Collections.unmodifiableMap()
method to create an immutable map from the mutable map:
Map<String,Integer> immutableMap=Collections.unmodifiableMap(mutableMap);
Note that any attempt to modify the immutableMap
will result in an UnsupportedOperationException
. For example, the following code will throw an exception:
immutableMap.put("grape", 4);
// This will throw an UnsupportedOperationException
Creating an immutable map can be useful in situations where you want to ensure that the map’s contents cannot be changed by other parts of the program. This can help prevent bugs and make the code easier to reason about.
Question 104: What s the difference List vs Set?
In Java, both List
and Set
are interfaces that represent collections of objects, but they differ in their behavior and characteristics.
Order:
List
is an ordered collection of elements, where the order of the elements is determined by their position in the list.Set
, on the other hand, is an unordered collection of elements, where the order of the elements is not defined.Duplicates:
List
can contain duplicates, i.e., it allows multiple occurrences of the same element.Set
does not allow duplicates, i.e., it only contains unique elements.Implementation:
List
is typically implemented as an array or a linked list, whileSet
is typically implemented as a hash table or a tree.Access:
List
provides access to elements based on their index, i.e., you can retrieve elements from a list by specifying their position in the list.Set
does not provide direct access to elements based on their index, i.e., you cannot retrieve elements from a set by specifying their position in the set.Performance:
List
provides efficient random access to elements, but may be slower when it comes to searching for elements or removing elements.Set
is optimized for fast searching and removal of elements, but may be slower when it comes to random access to elements.
Question 105: What difference Hashmap between LinkedHashmap?
Both HashMap
and LinkedHashMap
are implementations of the Map
interface in Java, but they differ in their underlying data structure and the order in which they store the entries.
HashMap
uses a hash table to store its entries, which provides constant-time performance for the basic operations (get, put, and remove), on average. The order in which the entries are stored in a HashMap
is not predictable and is based on the hash code of the keys.
LinkedHashMap
, on the other hand, extends HashMap
and maintains a doubly-linked list of the entries in the order in which they were inserted. This allows for predictable iteration order and provides constant-time performance for the basic operations, but with a slightly higher overhead due to the maintenance of the linked list.
In summary, HashMap
is faster for basic operations, but does not maintain insertion order, while LinkedHashMap
provides predictable iteration order and maintains insertion order, but has a slightly higher overhead. The choice between the two depends on the specific use case and the importance of the order of entries.
Question 106: Why Hashmap does not maintain the order like Linkedhashmap?
HashMap
does not maintain the order of its entries because it uses a hash table to store its entries. The hash table works by using a hash code of the key to determine the bucket (or index) where the entry should be stored. The hash function distributes the keys randomly across the buckets, and the order of the keys in the hash table depends on the hash codes of the keys.
The hash table’s primary advantage is that it provides constant-time performance for the basic operations of get
, put
, and remove
, on average, regardless of the size of the hash table. However, the disadvantage of the hash table is that it does not maintain the order of the keys or the entries.
On the other hand, LinkedHashMap
maintains the order of its entries by maintaining a doubly-linked list that runs through all the entries in the order in which they were inserted. This linked list imposes an order on the entries that are independent of their hash codes and allows for predictable iteration order.
Question 107: How Linkedhashmap is able to maintain the insertion order?
LinkedHashMap
maintains the insertion order of its entries by using a doubly-linked list that runs through all the entries in the order in which they were inserted.
When a new entry is added to a LinkedHashMap
, it is added to the end of the linked list. This ensures that the most recently added entry is always the last one on the list. When an entry is accessed, it is moved to the end of the list to ensure that it remains the last one in the list. This guarantees that the order of entries is preserved based on their insertion order.
When an entry is removed, the linked list is updated to remove the entry from the list, and the previous and next entries in the list are linked directly to each other. This ensures that the order of the remaining entries is preserved.
The linked list is maintained by each entry in the map holding a reference to the previous and next entries in the list. This allows the LinkedHashMap
to traverse the list quickly and efficiently.
Question 108: Java 8 changes in Hashmap?(This question is still asked to know if we are updated or not). How Hashmap performance is improved, and what improvements are there?
In Java 8, HashMap
has introduced a new approach for resolving hash code collisions called the "balanced tree" approach. This approach is used when the number of entries in a bucket exceeds a certain threshold (currently set to 8). In this case, instead of using a linked list to store the entries in the bucket, HashMap
will convert the bucket into a balanced tree structure.
Here are some key points related to hash code collisions and the balanced tree approach in HashMap
in Java 8:
Prior to Java 8,
HashMap
used a linked list to store the entries in a bucket that had hash code collisions. This approach could result in poor performance for large maps with many hash code collisions, as the time required to search the linked list increased linearly with the number of entries in the list.In Java 8, if the number of entries in a bucket exceeds a certain threshold (currently set to 8),
HashMap
will convert the bucket into a balanced tree structure. This approach provides better performance for large maps with many hash code collisions, as the time required to search a balanced tree is logarithmic in the number of entries.The balanced tree approach requires that the keys in the map implement the
Comparable
interface or that aComparator
is provided to theHashMap
constructor. This is because the tree structure requires a total ordering of the keys to maintaining the balance of the tree.The balanced tree approach is only used for buckets that have hash code collisions and have exceeded the threshold. Buckets with fewer than 8 entries will continue to use a linked list.
The threshold value of 8 is chosen based on performance testing and may be adjusted in future releases of Java if necessary.
Question 109: program to find the output
package com.vivek;
import java.util.HashMap;
import java.util.Map;
public class FindTheOutput {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
String e1 = new String("Java");
String e2 = new String("Java");
map.put(e1, "I");
map.put(e2, "M2");
System.out.println(map.get(e1));
System.out.println(map.get(e2));
Map<String, String> map1 = new HashMap<>();
String e3 = "Java1";
String e4 = "Java1";
map.put(e3, "I");
map.put(e4, "M2");
System.out.println(map.get(e3));
System.out.println(map.get(e4));
}
}
/**
* Output:
* M2
* M2
* M2
* M2
*/
Question 110: How to handle failures in microservice environments where one microservice is down and not responding.
Handling failures in microservice environments is a critical aspect of ensuring system reliability and resilience. When one microservice is down and not responding, there are several steps that can be taken to mitigate the impact and ensure the overall system remains functional.
Implement Circuit Breakers: Circuit breakers can be used to detect when a microservice is down and not responding. When this happens, the circuit breaker can redirect requests to another microservice or fallback mechanism. This can help to prevent cascading failures and ensure that the overall system remains functional.
Use Service Registry and Discovery: A service registry can be used to keep track of all the microservices in the environment and their locations. When one microservice is down, the service registry can be used to redirect requests to a healthy microservice. Service discovery mechanisms can also be used to automatically route requests to healthy microservices.
Implement Retry Mechanisms: When a microservice is down and not responding, retries can be used to attempt to reconnect to the service. The retries can be performed at regular intervals until the service becomes available again. This can help to reduce the impact of the failure and ensure that the overall system remains functional.
Monitor the System: Monitoring the system can help to detect when a microservice is down and not responding. This can be done using tools such as log analysis, performance metrics, and health checks. When a failure is detected, the system can be automatically alerted, and appropriate actions can be taken to mitigate the impact.
Implement Graceful Degradation: In some cases, it may be acceptable for the system to continue functioning even when one microservice is down. In these cases, the system can be designed to gracefully degrade, providing partial functionality until the microservice becomes available again.
In summary, handling failures in microservice environments requires a combination of techniques, including circuit breakers, service registry and discovery, retry mechanisms, system monitoring, and graceful degradation. By implementing these techniques, you can ensure that your microservice environment remains functional and resilient even when one microservice is down and not responding.