Java Concepts in light of interviews
This article will explain Java concepts for those questions which were asked in my interviews.
Q1. Can two equal objects have different hash code?
The answer is no. In order to explain this we must view the internal working of Java data structures. Every object in java inherits two methods.
- hashcode()
- equals()
“equals()” method is used to compare two objects. For e.g. to compare two strings “str1” and “str2”, equals can be used in Java like str1.equals(str2). “hashcode()” method returns hash code which is simply an integer. Data structures like HashMap and HashSet use hash code to identify the bucket. For example, by default initially in an HashSet there are 16 buckets. So a HashSet will take a modulus of hash code by total number of buckets (hash code % 16) to identify the bucket number. If hash code of an object is 20 then HashSet will place this object in bucket number 4 (20 % 16 = 4). If two objects having same hash code are placed inside the HashSet by using “add” method, then the HashSet will place both of them in the same bucket and create a Linked List among them. Similarly “get” method will also identify the bucket through the above procedure but in order to retrieve the object from the linked list, it will iterate through the linked list and call equals on each object. If equals method returns true, it will return that object.
So if two equal object have different hash code then the HashSet might place them in different buckets, which will lead to erroneous behavior of HashSet.
There are also other variations of this question.
Can two unequal objects have same hash code? The answer is yes.
Is it must that two equal objects have same hash code? The answer is yes. Its a virtual contract in Java which should not be broken otherwise insertion and retrieval(get) will not work for HashSet and HashMap.
As you can see, there are various variations of this question, one must always think about the working of HashSet or HashMap before answering this question. When an interviewer ask this question, pause a moment and go through the above explained logic and you will get your answer.
Q2) What is the difference between ArrayList and LinkedList?
When a question related to Data structure is asked, think in terms of insert, retrieval (get) and delete operations.
Insertion in ArrayList: In ArrayList, insertion complexity is O(n) time for worst case scenario and for average case is O(1) time. By default the size of ArrayList is 10 in Java. If the ArrayList reaches its full capacity and wants to insert an additional 11th element, it will create a new ArrayList with a larger size and copy all the elements from the original list to the new list. This operation has time complexity of O(n). But on average insertion takes O(1) time since ArrayList keeps a size variable which stores the number of inserted items in the list and when an additional item needs to be inserted into the ArrayList, it just needs to insert at the cell with an index equal to the size of the array.
Retrieval in ArrayList: Getting an object with an index in an ArrayList takes O(1) time.
Deletion in ArrayList: Deleting from an ArrayList takes O(n) time as shifting of elements needs to take place.
Insertion in LinkedList: Insertion in LinkedList takes O(1) time since only the pointer pointing to the next element needs to be updated.
Retrieval in LinkedList: Getting an object with an index from LinkedList takes O(n) time since it has to traverse the LinkedList in order to reach a certain index.
Deletion in LinkedList: Deletion in LinkedList also takes O(n) time since it has to traverse the LinkedList in order to reach a certain index. In addition LinkedList has removeFirst and removeLast methods which takes O(1) time since only the first and last pointer needs to be adjusted and no shifting operation needs to take place like ArrayList.
Q3) When should we use ArrayList or LinkedList?
If there are more inserts then reads, then we should use LinkedList otherwise we should use ArrayList.
Q4) How many types of Exception are there in Java?
There are mainly two types of Exceptions
- Checked Exceptions
- Unchecked Exceptions
IOException or ClassNotFound Exception are examples of Checked Exceptions because they can be checked at compile time. IndexOutOfBoundException or NullpointerException are examples of Unchecked Exceptions because they can only be detected at Runtime.
Q5) How to sort a collection in ascending order in Java?
Suppose you have a collection
List<Integer> lst=new ArrayList<Integer>();
lst.add(10);
lst.add(4);
lst.add(6);
Collections.sort(lst);
The above code will sort the list. Now suppose you have a list of students and you want to sort them according to their roll number.
class Student{
private int rollNumber; public Student(int rollNumber){
this.rollNumber=rollNumber;
} public int getRollNumber(){
return this.rollNumber;
} public static void main(String[] args){
List<Student> lstStudent=new ArrayList<Student>();
lstStudent.add(new Student(10));
lstStudent.add(new Student(3));
lstStudent.add(new Student(6));
//How to sort this list according to Student Rollnumber.
}}
There can be following approaches to solve this problem:
Approach 1: Class Student implements comparable interface and then Collections.sort(lstStudent) can be used to sort the list.
class Student implements Comparable<Student>{
private int rollNumber;
public Student(int rollNumber){
this.rollNumber=rollNumber;
}public int getRollNumber(){
return rollNumber;
}//If smaller then return a negative integer, equal then 0 and if //greater then a positive integer
@Override
public int compareTo(Student arg1) {
return this.getRollNumber() — arg1.getRollNumber();
}public static void main(String[] args){
List<Student> lstStudent=new ArrayList<Student>();
lstStudent.add(new Student(10));
lstStudent.add(new Student(3));
lstStudent.add(new Student(6));
Collections.sort(lstStudent);
}}
Approach 2: An anonymous class can be used to give the implementation of the Comparator interface in the sort method of Collections API. Through anonymous class we can provide an implementation of an interface, abstract class or extend a class with a single expression at the point of use.
public static void main(String[] args){
List<Student> lstStudent=new ArrayList<Student>();
lstStudent.add(new Student(10));
lstStudent.add(new Student(3));
lstStudent.add(new Student(6));
Collections.sort(lstStudent, new Comparator(){
@Override
public int compare(Student arg0, Student arg1) {
return arg0.getRollNumber() — arg1.getRollNumber():
}
})
}
Approach 3: Provide a lambda expression in the sort method of Collections API. It is extremely simple to write. I will recommend to memorize it because it is frequently used in day to day coding problems.
public static void main(String[] args){
List<Student> lstStudent=new ArrayList<Student>();
lstStudent.add(new Student(10));
lstStudent.add(new Student(3));
lstStudent.add(new Student(6));
Collections.sort(lstStudent,(x,y)->x.getRollNumber() - y.getRollNumber())
}
As you can see, the collection is sorted using only one line of code. Since the comparator interface is a functional interface, so lambda expression can be used to provide its implementation. A functional interface is simply an interface with only one abstract method.
Lambda Expression Explaination: (x,y)->x.getRollNumber() — y.getRollNumber()
X and y are two parameters of compare function and the implementation of this compare function is x.getRollNumber() - y.getRollNumber().
Q6) Tell the output of this Overriding puzzle:
class Engine{
public Engine(){
getCarInitials();
}
public String getCarInitials(){}
}class Car extends Engine{
String carNumber;
public Car(){
carNumber=”ABC567”;
} @Override
public String getCarInitials(){
return carNumber.substring(0,3);
} public static void main(String[] args){
Car car=new Car();
System.out.println(“Car Initials are:”+car.getCarInitials());
}
}
This program will throw a Nullpointer Exception at line(Car car=new Car()). Since we know that at first the parent constructor Engine() will be called by default inside the Car() constructor before carNumber is set. Inside Engine constructor “getCarInitials()” is called which is in fact overridden in the child class. Since it is overridden, Java will call the overridden method and try to get the substring of the null “carNumber” String. This will result in the Nullpointer exception.
Q7: What will be the output of this Overloading puzzle? Explain the difference between static binding and dynamic binding.
class Shape{}
class Circle extends Shape{}
class Building{
public void setShape(Shape shape){
System.out.println(“Parameter is shape”);
} public void setShape(Circle circle){
System.out.println(“Parameter is circle”);
} public static void main(String[] args){
Building building=new Building();
Shape shape=new Circle();
building.setShape(shape);
}
}
This program will print “Parameter is shape”. Java uses static or compile time binding for overloaded functions. So here the type of parameters will be checked on compile time. On the other hand, dynamic or runtime binding is used for overridden functions. Groovy language as opposed to Java, uses dynamic binding for overloaded functions.
References:
Java lists:LinkedList vs ArrayList