Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, March 30, 2022

this vs super in Java

1 comment:

 this vs super

  • this can be used to read all members declared within the class
  • this can all be used to read all inherited members in the direct parent class
  • this can be used in an instance method, constructor and instance block
  • this cannot be used in a static method or static initializer block
  • super can read only inherited members declared in the parent class
  • super excludes any members found in the current class
  • super always refer to the direct parent
  • this() can be used to invoke current class constructor
  • super() invokes constructor of the parent class
  • if we choose to call this() or super() , it must be the first statement in the constructor body. There can be only one call.
  • this refers to an instance of the class, while this() refers to a constructor call within the class
  • Java compiler automatically inserts a call to the no-argument constructor super() if we do not explicitly call this() or super() as the first line of a constructor.
  • static methods do not have reference to this or super

Constructor in Java

No comments:

 

Constructor

  • A special method that matches the name of the class and has no return type
  • Constructors are executed when a new instance of the class is created (new Main()). This process is called instantiation, because it creates a new instance of the class.
  • The method name and the class name should be exactly same (note that Java is case sensitive)
  • Constructors can include parameters, like arrays, primitive types or generics. But cannot include var. The below constructor will not compile.

      class Main {
        public Main( var number ) {
    
        }
      }
    
  • There can be multiple constructors in a class, provided that the constructor parameters are distinct.
  • Declaring multiple constructors with different signatures is reffered to as constructor overloading.
  • Every class in Java will have a constructor. If you didn't code it Java will include the default no-argument constructor automatically during compilation process.
  • Private Constructor is a special constructor which is declared as private. Having private constructors prevents other classes from the class. It is generally used when the class has only static methods.

      class Main {
        private Main() {
    
        }
      }
    
  • Classes with a private constructor can be extended only by an inner class, because an inner class is the only one that can access a private constructor by calling super().

Monday, March 28, 2022

Comparable vs Comparator in Java

No comments:

Comparable Interface

is used to sort the objects of a user defined class. It contains only one method: compareTo()

public interface Comparable<T>{
  int compareTo(T o);
}

We can implement the logic of the sorting into the compareTo() method (an example is given below). compareTo() method must returns an integer based on the following rules:

  • 0 is returned when current object is equal to the argument object
  • -1 is returned when current object is smaller than the argument object
  • 1 is returned when current object is greater than the argument object
class Employee implements Comparable<Employee> {
    private String empCode;
    private String name;
    private int age;
    //not including getters, setters, constructor etc
    @Override
    public String toString() {
        return "Emp{" + name + "," + age + "," + empCode + "}";
    }

    @Override
    public int compareTo(Employee o) {
        int result = this.name.compareTo(o.getName());
        if (result != 0) return result;
        return this.getAge() - o.getAge();
    }
}

public class Main {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("402", "anu", 35));
        employees.add(new Employee("399", "binoy", 32));
        employees.add(new Employee("400", "anu", 31));
        //printing before sort
        System.out.println(employees); //outputs [Emp{anu,35,402}, Emp{binoy,32,399}, Emp{anu,31,400}]
        Collections.sort(employees);
        //printing after sort
        System.out.println(employees); //outputs [Emp{anu,31,400}, Emp{anu,35,402}, Emp{binoy,32,399}]
    }
}

Comparator Interface

Comparator is also an interface which is used to sort the objects of a user defined class. It contains compare() method. Using Comparable interface we can implement only one sorting logic. What if we need to implement sorting logic based on other data members also. At that time Comparator becomes handy. In the above example, of Comparable interface, if we need to sort the employees based on their employee code (empCode). Then we can implement it by modifying the main code as shown below:

//sorting based on empCode
Comparator<Employee> comparatorByEmpCode = (e1, e2) -> e1.getEmpCode().compareTo(e2.getEmpCode());
Collections.sort(employees, comparatorByEmpCode);
System.out.println(employees); //outputs [Emp{binoy,32,399}, Emp{anu,31,400}, Emp{anu,35,402}]

We can also combine the comparators to get a particular sorting sequence. For example say we need to display the employee based on their name. But when name is same then the employee with smallest age will be listed first. Find the example below:-

Comparator<Employee> comparatorByName = Comparator.comparing(Employee::getName);
Comparator<Employee> comparatorByAge = Comparator.comparing(Employee::getAge);
Comparator<Employee> comparatorCombined = comparatorByName.thenComparing(comparatorByAge);
Collections.sort(employees, comparatorCombined);
System.out.println(employees);

Comparable vs Comparator

As seen above both are used for sorting purposes. The differences between them are as follows:

  • Comparable provides only a single sorting logic whereas with the help of Comparator we can implement multiple sorting logic.
  • Comparable has compareTo() method with a single parameter. Comparator has compare() method with two parameters
  • Comparable is present in java.lang package while Comparator is present in java.util package
  • To implement sorting using Comparable we need to modify the class, but in the case of Comparator we do not need to modify the class.
  • If a class implements Comparable interface then collection of that object can be sorted using Collections.sort() or Arrays.sort() method. They will be sorted based on the logic defined by compareTo() method.

Friday, March 25, 2022

Internal Working of HashMap in Java

No comments:

 Lets first see what a HashMap is:

  • HashMap is an implementation of Map interface which stores key/value pairs.
  • main benefit: adds/retrieves elements by key in a constant time

The below code creates a HashMap

HashMap<String, Integer> map = new HashMap<>();

Here a hash map is created. Consider hash map is a array and one element of that array is known as a bucket. By default say that hash map is created it contains 16 buckets (indexed from 0 to 15)

Now lets insert a key value pair to our map

map.put("apple",25);

At this point the following things happen internally
  • Calculate the hash code of the key "apple":
    Hash code calculated using hashCode() method. By default returns the memory reference of object in integer form. The process of converting an object into integer by using the method hashCode() is called hashing. We can override the hashCode() method to define our own implementation.

  • Calculate the index 
    Since hash code generated for a key is a large number, we need to generate an index which is not greater than the size of our array (which is 16)
    index = hashCode(key) & (n-1)
    where n = number of buckets 

  • Create a node object
    Since we have a key value pair we create a node to store them. Node can have hashcode, key, value and next (which contains address of the next node)

  • Place the created node 
    Now place the create in one of the buckets. Say if we got the index as 5 then place the node in the bucket-5
Let insert another key value pair
map.put("banana",30);

At this point the following things happen internally
  • Calculate the hash code of the key "banana"

  • Calculate the index 

  • Create a node object

  • Place the created node 
    Say if we got the index as 7 then place the node in the bucket-7

Let insert another key value pair
map.put("mango",40);

At this point the following things happen internally
  • Calculate the hash code of the key "mango"

  • Calculate the index 

  • Create a node object

  • Place the created node 
    Say if we got the index as 5. But in bucket-5 we already have a node. Hence there is a collision. What we do is we link the node(apple) with the newly created node(mango), i.e. we place the address of mango node in the next portion of apple node.
    








Why overriding equals() and hashcode() is required in Java?

No comments:

First go through the code below

Map<String, Integer> stringMap = new HashMap<>();
stringMap.put("a", 1);
stringMap.put("b", 2);
stringMap.put("a", 3);
System.out.println(stringMap);

The output for the above code will be 

{a=3, b=2}

As you can since the key is same even though we inserted 3 key/value pairs the map contains only two key/value pairs since the key a is repeating.

Now check the below code

Map<Employee, Integer> employeeMap = new HashMap<>();
employeeMap.put(new Employee("anu"), 1);
employeeMap.put(new Employee("binoy"), 2);
employeeMap.put(new Employee("anu"), 3);
System.out.println(employeeMap);
class Employee {
private String name;

public Employee(String name) {
this.name = name;
}

@Override
public String toString() {
return "Employee{" + name + '}';
}
}

The output for the above code will be

{Employee{binoy}=2, Employee{anu}=3, Employee{anu}=1}

Even though the name of the employee is the same the map is treating it as a separate key, because default implementation only compares the object reference to decide whether an object is equal or not. Note that these references are values generated by hashcode() method. 

You can override the equals() and hashcode() method to solve the above problem. Below is the sample on how to override these methods.

class Employee {
private String name;

public Employee(String name) {
this.name = name;
}

@Override
public String toString() {
return "Employee{" + name + '}';
}

@Override
public boolean equals(Object o) {
// If object is compared with itself
if (o == this) return true;

if (!(o instanceof Employee)) return false;

// typecast o to Employee to compare data members
Employee e = (Employee) o;
// Compare the data members and return accordingly
return name.compareTo(e.name) == 0;
}

@Override
public int hashCode() {
return this.name.hashCode();
}
}
public class Main {
public static void main(String[] args) {
Map<Employee, Integer> employeeMap = new HashMap<>();
employeeMap.put(new Employee("anu"), 1);
employeeMap.put(new Employee("binoy"), 2);
employeeMap.put(new Employee("anu"), 3);
System.out.println(employeeMap);
}
}

The output  now will be

{Employee{binoy}=2, Employee{anu}=3}









Static Blocks in Java

No comments:


  • Static blocks executed only once when the class is loaded into the memory for the first time.

    public class Main {
      {
          System.out.println("inside instance block");
      }
    
      static {
          System.out.println("inside static block");
      }
    
      public static void main(String[] args) {
          new Main();
          new Main();
          System.out.println("Main Program ");
      }
    }
    //output
    inside static block
    inside instance block
    inside instance block
    Main Program
    
  • A static block will be executed even if there is no main method, for example we print something on the console without creating main() method.
    class Main {
      static {
          System.out.print("Static block without main method");
      }
    }
    //output (works only if JDK version is 1.6 or previous)
    Static block without main method
    
  • Static blocks gets executed before the constructor
  • Static blocks are used for the initialization of static variables
  • A class can have any number of static blocks, and they can appear anywhere in the class body.
  • If there are multiple static blocks then those will be executed from top to bottom.

 

Instance Blocks

No comments:
  • Instance blocks can be used to initialize variables or to execute any logic during object creation.
  • Whenever an object is created, instance blocks will be executed
  • Instance blocks will be executed before constructor
  • If there are multiple instance blocks then those will be executed from top to bottom.
  • A class can have any number of instance blocks, and they can appear anywhere in the class body.
public class Main {
    private int number = 200;
    {
        System.out.println("inside instance block");
        this.number = 300;
    }
    public Main(int number) {
        System.out.println("inside constructor");
        this.number = number;
    }
    public static void main(String[] args) {
        var mainObj = new Main(100);
        System.out.println(mainObj.number);
    }
}

//output
inside instance block
inside constructor 
100 

Lambda expressions in Java

No comments:

Lambda expressions are used to provide the implementation of a functional interface. As shown below it saves a lot of code.

@FunctionalInterface
interface Shape {
    void draw();
}

public class Main {
    public static void main(String[] args) {
        //implementing Shape using anonymous class
        Shape shape1 = new Shape() {
            @Override
            public void draw() {
                System.out.println("Drawing a shape without lambda");
            }
        };
        shape1.draw();

        //implementing Shape with lambda
        Shape shape2 = () -> System.out.println("Drawing a shape with lambda");
        shape2.draw();
    }
}

Lambda expressions can be stored in variables if the variable's type is an interface which has only one method. As shown below we have stored the filter condition in a variable.

List<String> list = new ArrayList<>();
list.add("apple");
list.add("ball");
list.add("cat");
list.add("aeroplane");
Predicate<String> startsWithLetterA = s -> s.startsWith("a");
list.stream().filter(startsWithLetterA).forEach(System.out::println);

Choose valid lambda expression for the following code

List<String> list = new ArrayList<>();
list.removeIf(______________________);
  • [x] s -> s.isEmpty() //valid
  • [ ] s -> {s.isEmpty()} //invalid since return keyword is missing
  • [ ] s -> {s.isEmpty();} //invalid since return keyword is missing
  • [x] s -> {return s.isEmpty();} //valid
  • [ ] s -> {return s.isEmpty()} //invalid since semicolon expected
  • [ ] String s -> s.isEmpty() //invalid since missing parantheses around String s
  • [x] ( String s ) -> s.isEmpty() //valid
  • [ ] s -> {String s = ""; return s.isEmpty();} // invalid since variable s is already in use and cannot be redefined