Java
Java
I. Language Basics:
1. Java Releases and Evolution:
Java 8
The main changes of the Java 8 release were these:
In our examples, we will see how we use lambdas and streams in the different
scenarios.
Let us take a look at how we would implement a function for something like
this in a naive way:
Our client wants to find all cars with the same criteria.
Let us see a solution where we used the stream API and lambda expression:
public class LambdaExpressions {
public static List<Car> findCarsUsingLambda(List<Car> cars) {
return cars.stream().filter(car -> car.kilometers < 50000)
.collect(Collectors.toList());
}
}
We need to transfer the list of cars into a stream by calling
the stream() method. Inside the filter() method we are setting our condition. We
are evaluating every entry against the desired condition. We are keeping only
those entries that have less than 50,000 kilometers. The last thing that we
need to do is to wrap it up into a list.
Method Reference
Without Method Reference
We still own a car dealership shop, and we want to print out all the cars in the
shop. For that, we will use a method reference.
Default Methods
Let us imagine that we have a simple method log(String message) that prints log
messages on invocation. We realized that we want to provide timestamps to
messages so that logs are easily searchable. We don’t want our clients to
break after we introduce this change. We will do this using a default method
implementation on an interface.
Use Case
Let us see how our contract looks:
public class DefaultMethods {
Type Annotations
Type annotations are one more feature introduced in Java 8. Even though we
had annotations available before, now we can use them wherever we use a
type. This means that we can use them on:
Tools like IDEs can then read these annotations and show warnings or errors
based on the annotations.
Constructor Call
We want to make sure that we cannot create an empty ArrayList:
public class TypeAnnotations {
Generic Type
One of our requirements is that each email has to be in a
format <name>@<company>.com. If we use type annotations, we can do it
easily:
public class TypeAnnotations {
A tool could use reflection to evaluate the annotation and check that each of
the elements in the list is a valid email address.
Repeating Annotations
Let us imagine we have an application with fully implemented security. It has
different levels of authorization. Even though we implemented everything
carefully, we want to make sure that we log every unauthorized action. On
each unauthorized action, we are sending an email to the owner of the
company and our security admin group email. Repeating annotations are our
way to go on this example.
@Repeatable(Notifications.class)
public @interface Notify {
String email();
}
Please note that this is a mock annotation just for demonstration purposes.
This annotation will not send emails without an annotation processor that
reads it and then sends emails.
}
}
We have our custom exception class that we will throw whenever a user tries
to do something that the user is not allowed. Our annotations to this class say
that we want to notify two emails when code throws this exception.
Java 9
Java 9 introduced these main features:
Name
Dependencies
Public Packages - by default, all packages are module private
Services Offered
Services Consumed
Reflection Permissions
Without going into many details, let us create our first module. Inside our
example, we will show several options and keywords that one can use when
creating a module.
The next keyword that we use is exports. It tells the module system that we are
making our com.reflectoring.io.app.world package visible outside of our module.
requires
requires transitive
exports to
uses
provides with
open
opens
opens to
To read more about the Java module system please refer to the docs
Try-with-resources
Try-with-resources is a feature that enables us to declare new autoclosable
resources on a try-catch block. Declaring them inside a try-catch block tells the
JVM to release them after the code has run. The only condition is that the
declared resource implements an Autoclosable interface.
Closing a Resource Manually
We want to read text using BufferedReader. BufferedReader is a closable
resource, so we need to make sure that it is properly closed after use. Before
Java 8 we would do it like this:
public class TryWithResources {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(
new StringReader("Hello world example!"));
try {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
In finally block, we would call close(). The finally block ensures that the reader
is always properly closed.
For our example, we will create the abstract class, StringAppender. The class
has only one method that appends two strings with - between them. We will
use the anonymous class for providing the implementation for
the append() method:
public class DiamondOperator {
Java 10
Local Variable Type Inference
Java always needed explicit types on local variables.
When writing and reading code, we always know which type we expect. On
the other hand, a lot of the code is just types with no usability.
The var type allows us to omit type from the left-hand side of our statements.
Old Way
Let us look into the example here. We want to create small a set of people,
put everything in one list and then go through that list in the for loop to print
out their name and last name:
public class LocalTypeVar {
List<Person> persons =
List.of(Roland, Susan, Eddie, Detta, Jake);
For more details about local type inference please visit the docs.
Java 11
Local Variable Type in Lambda Expressions
Java 11 introduced an improvement to the previously mentioned local type
inference. This allows us to use var inside lambda expressions.
We will, again, create several persons, collect them into the list and filter out
entries that don’t have an ‘a’ inside their name:
public class LocalTypeVarLambda {
var filteredPersons =
List.of(Roland, Susan, Eddie, Detta, Jake)
.stream()
.filter((var x) -> x.name.contains("a"))
.collect(Collectors.toList());
System.out.println(filteredPersons);
}
}
Inside the filter() method we are using var to infer the type instead of explicitly
mentioning the type.
Java 14
Switch Expressions
Switch expressions allowed us to omit break calls inside every case block. It
helps with the readability of the code and better understanding.
In this section, we will see several ways of how to use switch expressions.
switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER :
days = 31;
break;
case FEBRUARY :
days = 28;
break;
case APRIL, JUNE, SEPTEMBER, NOVEMBER :
days = 30;
break;
default:
throw new IllegalStateException();
}
}
}
We need to make sure that we put a break statement inside our case code
block. Failing it will result in checking on other conditions after we match the
first one.
This will do the same thing as the code shown in the previous example.
Java 15
Text Blocks
Text block is an improvement on formatting String variables. From Java 15,
we can write a String that spans through several lines as regular text.
There are some rules that we need to abide by when using a text block. We
need to make sure that we put a new line after our opening quotes, or our
compiler will throw an error:
Illegal text block start: missing new line after opening quotes.
If we want to end our string with \n we can do it by putting new line before
closing """ like in the example above.
Java 16
Records
How many POJOs (Plain Old Java Objects) have you written?
Java has had a bad reputation for boilerplate code. Lombok allowed us to
stop worrying about getters, setters, etc. Java 16 finally introduced records to
remove a lot of boilerplate code.
A record class is nothing more than regular POJO, for which most of the code
is generated from the definition.
@Override
public boolean equals(Object o) ...
@Override
public int hashCode() ...
@Override
public String toString() ...
}
There are almost 50 lines of code for object that contains only two properties.
The IDE generated this code, but still, it is there and has to be maintained.
Record Definition
Definition of a vehicle record, with the same two properties, can be done in
just one line:
public record VehicleRecord(String code, String engineType) {}
This one line has all the same getters, setters, constructors, etc. as from the
example above. One thing to note is that the record class is, by default, final,
and we need to comply with that. That means we cannot extend a record
class, but most other things are available for us.
To read more about record classes please refer to the docs.
Java 17
Sealed Classes
The final modifier on a class doesn’t allow anyone to extend it. What about
when we want to extend a class but only allow it for some classes?
We are back at our car dealership business. We are so proud of our algorithm
for calculating prices that we want to expose it. We don’t want anyone using
our Vehicle representation, though. It is valid just for our business. We can
see a bit of a problem here. We need to expose class but constrain it also.
This is where Java 17 comes into play with sealed classes. The sealed class
allows us to make class effectively final for everyone except explicitly
mentioned classes.
public sealed class Vehicle permits Bicycle, Car {...}
We added a sealed modifier to our Vehicle class, and we had to add
the permits keyword with a list of classes that we allow to extend it. After this
change, we are still getting errors from the compiler.
Constraints
Several constraints have to be met for the sealed class to work:
2. Compilation:
Java Is Compiled
Java is a compiled language rather than an interpreted language like Python.
While interpreted languages are translated at runtime, compiled languages are
compiled before reaching the virtual machine. This means that a program
called a compiler translates the Java written by the programmer into
Java bytecode which can then be interpreted by the Java virtual machine.
Compilers are quite helpful for several reasons. A few are listed below:
1. Compilers can check for errors prior to runtime. The Java compiler will
catch and report errors like TypeErrors, which can be produced by
giving a function the wrong type of object as a parameter, and
SyntaxErrors, which can be caused by forgetting syntax arguments like
parentheses or braces. Catching these and many other types of errors
prior to runtime help catch a large majority of the possible bugs caused
by programmer error and make Java programs more stable and bug-free
before they are run.
2. Compilers can help speed up programs. Interpreted languages can be
slow because they must parse text that is understandable to humans and
translate it into bits that can be understood by the machine. The
compiler does this translation work so that it happens before running
the program and does not slow down the running time of programs.
3. Compilers can verify access rights to classes and methods. Java has a
lot of built-in security because access rights can be assigned to each
class and method that limit what other classes or methods have access
to. The compiler checks that every program that uses a class or calls a
method has the correct access rights to use or call it.
3. Running Java Code:
Java Is Compiled
Java is a compiled language rather than an interpreted language like Python.
While interpreted languages are translated at runtime, compiled languages are
compiled before reaching the virtual machine. This means that a program
called a compiler translates the Java written by the programmer into
Java bytecode which can then be interpreted by the Java virtual machine.
Compilers are quite helpful for several reasons. A few are listed below:
4. Compilers can check for errors prior to runtime. The Java compiler will
catch and report errors like TypeErrors, which can be produced by
giving a function the wrong type of object as a parameter, and
SyntaxErrors, which can be caused by forgetting syntax arguments like
parentheses or braces. Catching these and many other types of errors
prior to runtime help catch a large majority of the possible bugs caused
by programmer error and make Java programs more stable and bug-free
before they are run.
5. Compilers can help speed up programs. Interpreted languages can be
slow because they must parse text that is understandable to humans and
translate it into bits that can be understood by the machine. The
compiler does this translation work so that it happens before running
the program and does not slow down the running time of programs.
6. Compilers can verify access rights to classes and methods. Java has a
lot of built-in security because access rights can be assigned to each
class and method that limit what other classes or methods have access
to. The compiler checks that every program that uses a class or calls a
method has the correct access rights to use or call it.
4. Variables:
1. Declaration and Initializing:
Assignment is whenever you’re storing data in the variable.
int a; // not an assignment
int b = 5; // assignment
a = 6; // assignment
b = 7; // assignment
Initialization is when you assign data to a variable for the first time. With all
other assignments, you’re overwriting data. With initialization, there was no
previous data to overwrite.
int a; // not an initialization
int b = 5; // initialization
a = 6; // initialization
b = 7; // not an initialization
Declaration is the point at which you create a variable. At this point, Java
knows nothing about the variable, so it’s necessary to specify the type. This is
the only time you need to specify the type since for all future time, Java can
refer to this declaration to determine what the type is.
2. Memory Location and Lifecycle:
Instance Variables
A variable which is declared inside a class and outside all the methods and
blocks is an instance variable. The general scope of an instance variable is
throughout the class except in static methods. The lifetime of an instance
variable is until the object stays in memory.
Class Variables
A variable which is declared inside a class, outside all the blocks and is
marked static is known as a class variable. The general scope of a class
variable is throughout the class and the lifetime of a class variable is until the
end of the program or as long as the class is loaded in memory.
Local Variables
All other variables which are not instance and class variables are treated as
local variables including the parameters in a method. Scope of a local
variable is within the block in which it is declared and the lifetime of a local
variable is until the control leaves the block in which it is declared.
3. Garbage Collections:
Garbage collection in java is about removing unused variables from the
memory so that there is sufficient space for the new variables. Garbage
collection works automatically in java making memory management more
efficient.
4. Primitive Types:
In Java, the primitive data types are the predefined data types of Java. They
specify the size and type of any standard values. Java has 8 primitive data
types namely byte, short, int, long, float, double, char and boolean. When a
primitive data type is stored, it is the stack that the values will be assigned.
When a variable is copied then another copy of the variable is created and
changes made to the copied variable will not reflect changes in the original
variable. Here is a Java program to demonstrate all the primitive data types in
Java.
5. Non-Primitive Types:
They are so-called because they refer to any particular object. Unlike the
primitive data types, the non-primitive ones are created by the users in Java.
Examples include arrays, strings, classes, interfaces etc. When the reference
variables will be stored, the variable will be stored in the stack and the
original object will be stored in the heap. In Object data type although two
copies will be created they both will point to the same variable in the heap,
hence changes made to any variable will reflect the change in both the
variables. Here is a Java program to demonstrate arrays(an object data type)
in Java.
When changes
are made in the Change does not reflect in the Changes reflected in the
copied variable original ones. original ones.
6. Casting:
The process of converting the value of one data type (int, float, double, etc.)
to another data type is known as typecasting.
In Java, there are 13 types of type conversion. However, in this tutorial, we
will only focus on the major 2 types:
1. Widening Type Casting: Java automatically converts one data type to
another data type.
2. Narrowing Type Casting: we manually convert one data type into another
using the parenthesis.
7. Final Variables:
In Java, the final keyword is used to denote constants. It can be used with
variables, methods, and classes.
Once any entity (variable, method or class) is declared final, it can be
assigned only once. That is,
the final variable cannot be reinitialized with another value
the final method cannot be overridden
the final class cannot be extended
5. Methods:
1. Signatures:
The method signature in java is defined as the structure of the method that is
designed by the programmer. The method signature is the combination of the
method name and the parameter list. The method signature depicts the
behavior of the method i.e types of values of the method, return type of the
method, etc.
2. Static Methods:
The static keyword is used to construct methods that will exist regardless of
whether or not any instances of the class are generated. Any method that
uses the static keyword is referred to as a static method.
Features of static method:
A static method in Java is a method that is part of a class rather
than an instance of that class.
Every instance of a class has access to the method.
Static methods have access to class variables (static variables)
without using the class’s object (instance).
Only static data may be accessed by a static method. It is unable to
access data that is not static (instance variables).
In both static and non-static methods, static methods can be
accessed directly.
3. Instance Methods:
Instance Methods are the group of codes that performs a particular task.
Sometimes the program grows in size, and we want to separate the logic of
the main method from other methods. A method is a function written inside
the class. Since java is an object-oriented programming language, we need
to write a method inside some classes.
The important points regarding instance variables are:
1. Instance methods can access instance variables and instance methods
directly and undeviatingly.
2. Instance methods can access static variables and static methods
directly.
6. Operator Precedence:
7. Math Operations:
8. String Manipulation:
2. Switch Statement:
The switch statement is a multi-way branch statement. In simple words, the
Java switch statement executes one statement from multiple conditions. It is
like an if-else-if ladder statement. It provides an easy way to dispatch
execution to different parts of code based on the value of the expression.
Basically, the expression can be a byte, short, char, and int primitive data
types. It basically tests the equality of variables against multiple values.
10. Loops:
1. For Loop:
The Java for loop is used to iterate a part of the program several times. If the
number of iteration is fixed, it is recommended to use for loop.
It consists of four parts:
1. Initialization: It is the initial condition which is executed once when
the loop starts. Here, we can initialize the variable, or we can use an
already initialized variable. It is an optional condition.
2. Condition: It is the second condition which is executed each time to
test the condition of the loop. It continues execution until the
condition is false. It must return boolean value either true or false. It is
an optional condition.
3. Increment/Decrement: It increments or decrements the variable value.
It is an optional condition.
4. Statement: The statement of the loop is executed each time until the
second condition is false.
2. While Loop:
Java while loop is a control flow statement that allows code to be executed
repeatedly based on a given Boolean condition. The while loop can be
thought of as a repeating if statement. While loop in Java comes into use
when we need to repeatedly execute a block of statements. The while loop is
considered as a repeating if statement. If the number of iterations is not
fixed, it is recommended to use the while loop.
3. Do-While Loop:
The Java do-while loop is used to iterate a part of the program repeatedly, until the specified condition
is true. If the number of iteration is not fixed and you must have to execute the loop at least once, it is
recommended to use a do-while loop.
Java do-while loop is called an exit control loop. Therefore, unlike while loop and for loop, the do-while
check the condition at the end of loop body. The Java do-while loop is executed at least once because
condition is checked after loop body.
4. Recursion:
Recursion is a process by which a function or a method calls itself again and
again. This function that is called again and again either directly or indirectly
is called the “recursive function”.
We will see various examples to understand recursion. Now let’s see the
syntax of recursion.
Recursion Syntax
Any method that implements Recursion has two basic parts:
1. Method call which can call itself i.e. recursive
2. A precondition that will stop the recursion.
Note that a precondition is necessary for any recursive method as, if we do
not break the recursion then it will keep on running infinitely and result in a
stack overflow.
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}
The System class belongs to the “java.lang” package of Java. Apart from
providing standard I/O streams, System class also provides access to
environment variables, external variables, loading files and libraries, and also
a utility method arrayCopy for copying part of an array.
As this tutorial is based on standard I/O only, we will skip the rest of the
facilities provided by the System class here.
It uses three methods from the “PrintStream” class as the standard output
derives from this class.
1. print
2. println
3. write
The methods “print” and “println” have the same functionality except for a
single difference that the println method appends a newline character (\n) to
the output.
The write method is not used frequently except in cases where non-ASCII
data is to be displayed.
Like System.out stream, the error stream also supports the three methods of
PrintStream class, print, println and writes.
2 System.out.printf(format, arguments); Prints the output using specified format string ‘format’ and
arguments.
3 System.out.printf(locale, format, Prints the output using specified format string by applying locale and
arguments); the arguments.
Note that the function System.out.format () is the same as System.out.printf
().
12. Exceptions:
Try Catch
You simply wrap the Java code which throws the checked exception within
a try catch block. This now allows you to process and deal with the
exception. With this approach it's very easy to swallow the exception and
then carry on like nothing happened. Later in the code when what the method
was doing is required you may find yourself with our good friend
the NullPointerException.
We have now caught the exception and processed the error in a meaningful
way by adding our code to the catch block, the code sequence carries on
crisis averted.
Throws
Threads
Threads allows a program to operate more efficiently by doing multiple
things at the same time.
Threads can be used to perform complicated tasks in the background without
interrupting the main program.
1
public void start()
Starts the thread in a separate path of execution, then invokes the run() method on this Thread object.
2
public void run()
If this Thread object was instantiated using a separate Runnable target, the run() method is invoked
on that Runnable object.
3
public final void setName(String name)
Changes the name of the Thread object. There is also a getName() method for retrieving the name.
4
public final void setPriority(int priority)
Sets the priority of this Thread object. The possible values are between 1 and 10.
5
public final void setDaemon(boolean on)
A parameter of true denotes this Thread as a daemon thread.
6
public final void join(long millisec)
The current thread invokes this method on a second thread, causing the current thread to block until
the second thread terminates or the specified number of milliseconds passes.
7
public void interrupt()
Interrupts this thread, causing it to continue execution if it was blocked for any reason.
8
public final boolean isAlive()
Returns true if the thread is alive, which is any time after the thread has been started but before it
runs to completion.
Differences between "extending" and "implementing" Threads
The major difference is that when a class extends the Thread class, you
cannot extend any other class, but by implementing the Runnable interface, it
is possible to extend from another class as well, like: class MyClass extends
OtherClass implements Runnable
2. Fork / Join:
Nowadays, systems are launching with multicore processors. The multicore
processors make the computation faster. Hence, it becomes necessary for a
programmer to use multicore processors effectively so that the result can be
generated in less span of time. Fork/Join in Java is used to make use of the
cores (brain of CPU that process the instructions) in an efficient manner. The
fork/join splits a bigger task into smaller sub-tasks. These sub-tasks are then
distributed among the cores. The results of these subtasks are then joined to
generate the final result. The splitting of a task and joining the results imitates
the divide-and-conquer algorithm. The fork is responsible for splitting the
task, and join is responsible for merging the results of the task to generate the
final result.
3. Mutex:
Java multi threads example to show you how to use Semaphore and Mutex to
limit the number of threads to access resources.
1. Semaphore
2. Mutex
4. Race Condition:
Race condition occurs in a multi-threaded environment when more than one
thread try to access a shared resource (modify, write) at the same time. Note
that it is safe if multiple threads are trying to read a shared resource as long as
they are not trying to change it. Since multiple threads try to race each other
to finish executing a method thus the name race condition.
14. Generics:
Why Generics?
The Object is the superclass of all other classes, and Object reference can
refer to any object. These features lack type safety. Generics add that type of
safety feature. We will discuss that type of safety feature in later examples.
Generics in Java are similar to templates in C++. For example, classes like
HashSet, ArrayList, HashMap, etc., use generics very well. There are some
fundamental differences between the two approaches to generic types.
The type parameters naming conventions are important to learn generics
thoroughly. The common type parameters are as follows:
T – Type
E – Element
K – Key
N – Number
V – Value
Advantages of Generics:
Programs that use Generics has got many benefits over non-generic code.
1. Code Reuse: We can write a method/class/interface once and use it for
any type we want.
2. Type Safety: Generics make errors to appear compile time than at run
time (It’s always better to know problems in your code at compile time
rather than making your code fail at run time). Suppose you want to create
an ArrayList that store name of students, and if by mistake the programmer
adds an integer object instead of a string, the compiler allows it. But, when
we retrieve this data from ArrayList, it causes problems at runtime.
2. Collection Interface:
The Collection interface is a member of the Java Collections Framework. It
is a part of java.util package. It is one of the root interfaces of the Collection
Hierarchy. The Collection interface is not directly implemented by any class.
However, it is implemented indirectly via its subtypes or subinterfaces
like List, Queue, and Set.
The List interface is found in java.util package and inherits the Collection
interface. It is a factory of ListIterator interface. Through the ListIterator, we
can iterate the list in forward and backward directions. The implementation
classes of the List interface are ArrayList, LinkedList, Stack, and Vector.
ArrayList and LinkedList are widely used in Java programming. The Vector
class is deprecated since Java 5.
Since List is an interface, it can be used only with a class that implements this
interface. Now, let’s see how to perform a few frequently used operations on
the List.
1. ArrayList:
Java ArrayList class uses a dynamic array
for storing the elements. It is like an array, but there is no size limit. We can
add or remove elements anytime. So, it is much more flexible than the
traditional array. It is found in the java.util package. It is like the Vector in
C++.
The ArrayList in Java can have the duplicate elements also. It implements the
List interface so we can use all the methods of the List interface here. The
ArrayList maintains the insertion order internally.
It inherits the AbstractList class and implements List interface.
2. LinkedList:
Java LinkedList class uses a doubly linked list to store the elements. It
provides a linked-list data structure. It inherits the AbstractList class and
implements List and Deque interfaces.
4. Set:
The set is an interface available in the java.util package. The set interface
extends the Collection interface. An unordered collection or list in which
duplicates are not allowed is referred to as a collection interface. The set
interface is used to create the mathematical set. The set interface use
collection interface's methods to avoid the insertion of the same
elements. SortedSet and NavigableSet are two interfaces that extend the set
implementation.
1. HashSet:
Java HashSet class is used to create a collection that uses a hash table for
storage. It inherits the AbstractSet class and implements Set interface.
2. TreeSet:
Java TreeSet class implements the Set interface that uses a tree for storage. It
inherits AbstractSet class and implements the NavigableSet interface. The
objects of the TreeSet class are stored in ascending order.
The important points about the Java TreeSet class are:
o Java TreeSet class contains unique elements only like HashSet.
o Java TreeSet class access and retrieval times are quiet fast.
o Java TreeSet class doesn't allow null element.
o Java TreeSet class is non synchronized.
o Java TreeSet class maintains ascending order.
o Java TreeSet class contains unique elements only like HashSet.
o Java TreeSet class access and retrieval times are quite fast.
o Java TreeSet class doesn't allow null elements.
o Java TreeSet class is non-synchronized.
o Java TreeSet class maintains ascending order.
o The TreeSet can only allow those generic types that are comparable.
For example The Comparable interface is being implemented by the
StringBuffer class.
5. Map:
A map contains values on the basis of key, i.e. key and value pair. Each key
and value pair is known as an entry. A Map contains unique keys.
A Map is useful if you have to search, update or delete elements on the basis
of a key.
1. HashMap:
Java HashMap class implements the Map interface which allows us to store
key and value pair, where keys should be unique. If you try to insert the
duplicate key, it will replace the element of the corresponding key. It is easy
to perform operations using the key index like updation, deletion, etc.
HashMap class is found in the java.util package.
Points to remember:
o Java HashMap contains values based on the key.
o Java HashMap contains only unique keys.
o Java HashMap may have one null key and multiple null values.
o Java HashMap is non synchronized.
o Java HashMap maintains no order.
o The initial default capacity of Java HashMap class is 16 with a load
factor of 0.75.
2. TreeMap:
Java TreeMap class is a red-black tree based implementation. It provides an
efficient means of storing key-value pairs in sorted order.
6. Stack:
The stack is a linear data structure that is used to store the collection of
objects. It is based on Last-In-First-Out (LIFO). Java collection framework
provides many interfaces and classes to store the collection of objects. One of
them is the Stack class that provides different operations such as push, pop,
search, etc.
The stack data structure has the two most important operations that
are push and pop. The push operation inserts an element into the stack and
pop operation removes an element from the top of the stack. Let's see how
they work on stack.
7. Queue:
The interface Queue is available in the java.util package and does extend the
Collection interface. It is used to keep the elements that are processed in the
First In First Out (FIFO) manner. It is an ordered list of objects, where
insertion of elements occurs at the end of the list, and removal of elements
occur at the beginning of the list.
Being an interface, the queue requires, for the declaration, a concrete class,
and the most common classes are the LinkedList and PriorityQueue in Java.
Implementations done by these classes are not thread safe. If it is required to
have a thread safe implementation, PriorityBlockingQueue is an available
option.
1. Deque:
The interface called Deque is present in java.util package. It is the subtype of
the interface queue. The Deque supports the addition as well as the removal
of elements from both ends of the data structure. Therefore, a deque can be
used as a stack or a queue. We know that the stack supports the Last In First
Out (LIFO) operation, and the operation First In First Out is supported by a
queue. As a deque supports both, either of the mentioned operations can be
performed on it. Deque is an acronym for "double ended queue".
2. Priority Queue:
A PriorityQueue is used when the objects are supposed to be processed based
on the priority. It is known that a Queue follows the First-In-First-Out
algorithm, but sometimes the elements of the queue are needed to be
processed according to the priority, that’s when the PriorityQueue comes into
play.
The PriorityQueue is based on the priority heap. The elements of the priority
queue are ordered according to the natural ordering, or by a Comparator
provided at queue construction time, depending on which constructor is
used.
8. Tree:
o A tree data structure is defined as a collection of objects or entities
known as nodes that are linked together to represent or simulate
hierarchy.
o A tree data structure is a non-linear data structure because it does not
store in a sequential manner. It is a hierarchical structure as elements in
a Tree are arranged in multiple levels.
o In the Tree data structure, the topmost node is known as a root node.
Each node contains some data, and data can be of any type. In the
above tree structure, the node contains the name of the employee, so
the type of data would be a string.
o Each node contains some data and the link or reference of other nodes
that can be called children.
In the above structure, each node is labeled with some number. Each
arrow shown in the above figure is known as a link between the two
nodes.
o Root: The root node is the topmost node in the tree hierarchy. In other
words, the root node is the one that doesn't have any parent. In the
above structure, node numbered 1 is the root node of the tree. If a
node is directly linked to some other node, it would be called a parent-
child relationship.
o Child node: If the node is a descendant of any node, then the node is
known as a child node.
o Parent: If the node contains any sub-node, then that node is said to be
the parent of that sub-node.
o Sibling: The nodes that have the same parent are known as siblings.
o Leaf Node:- The node of the tree, which doesn't have any child node,
is called a leaf node. A leaf node is the bottom-most node of the tree.
There can be any number of leaf nodes present in a general tree. Leaf
nodes can also be called external nodes.
o Internal nodes: A node has atleast one child node known as
an internal
o Ancestor node:- An ancestor of a node is any predecessor node on a
path from the root to that node. The root node doesn't have any
ancestors. In the tree shown in the above image, nodes 1, 2, and 5 are
the ancestors of node 10.
o Descendant: The immediate successor of the given node is known as a
descendant of a node. In the above figure, 10 is the descendant of node
5.
1. Binary Tree:
is a tree type non-linear data structure that are mainly used for sorting and
searching because they store data in hierarchical form. In this section, we will
learn the implementation of binary tree data structure in Java. Also, provides
a short description of binary tree data structure.
A tree in which each node (parent) has at most two-child nodes (left and
right) is called binary tree. The top most node is called the root node. In a
binary tree a node contains the data and the pointer (address) of the left and
right child node.
9. Graph:
A graph is a data structure that stores connected data. In other words, a graph
G (or g) is defined as a set of vertices (V) and edges (E) that connects
vertices. The examples of graph are a social media network, computer
network, Google Maps, etc.
Each graph consists of edges and vertices (also called nodes). Each vertex
and edge have a relation. Where vertex represents the data and edge
represents the relation between them. Vertex is denoted by a circle with a
label on them. Edges are denoted by a line that connects nodes (vertices).
Graph Terminology:
Vertex: Vertices are the point that joints edges. It represents the data. It is
also known as a node. It is denoted by a circle and it must be labeled. To
construct a graph there must be at least a node. For example, house, bus stop,
etc.
Edge: An edge is a line that connects two vertices. It represents the relation
between the vertices. Edges are denoted by a line. For example, a path to the
bus stop from your house.
Weight: It is labeled to edge. For example, the distance between two cities is
100 km, then the distance is called weight for the edge.
Path: The path is a way to reach a destination from the initial point in a
sequence.
1. Directed Graph:
A graph in which edges represent direction is called a directed graph. In a
directed graph, we use arrows instead of lines (edges). Direction denotes the
way to reach from one node to another node. Note that in a directed graph,
we can move either in one direction or in both directions. The following
figure represents a directed graph.
2. Undirected Graph:
A graph in which edges are bidirectional is called an undirected graph. In an
undirected graph, we can traverse in any direction. Note that we can use the
same path for return through which we have traversed. While in the directed
graph we cannot return from the same path.
3. Acyclic Graph:
A graph is called an acyclic graph if zero cycles are present, and an acyclic
graph is the complete opposite of a cyclic graph.
4. DAG:
A DAG for basic block is a directed acyclic graph with the following labels
on nodes:
1. The leaves of graph are labeled by unique identifier and that identifier
can be variable names or constants.
2. Interior nodes of the graph is labeled by an operator symbol.
3. Nodes are also given a sequence of identifiers for labels to store the
computed value.
4. DAGs are a type of data structure. It is used to implement
transformations on basic blocks.
5. DAG provides a good way to determine the common sub-expression.
6. It gives a picture representation of how the value computed by the
statement is used in subsequent statements.
You can use stream to filter, collect, print, and convert from one data
structure to other etc. In the following examples, we have apply various
operations with the help of stream.
III. Object Oriented Programming:
1. Classes and Objects:
1. What are Classes and Objects?
Classes and Objects are basic concepts of Object Oriented Programming
that revolve around real life entities.
CLASS:
1. Class is a set of object which shares common characteristics/ behavior
and common properties/ attributes.
2. Class is not a real world entity. It is just a template or blueprint or
prototype from which objects are created.
3. Class does not occupy memory.
4. Class is a group of variables of different data types and group of methods.
A class in java can contain:
• data member
• method
• constructor
• nested class and
• interface
Object
1. Private: The access level of a private modifier is only within the class.
It cannot be accessed from outside the class.
2. Default: The access level of a default modifier is only within the
package. It cannot be accessed from outside the package. If you do not
specify any access level, it will be the default.
3. Protected: The access level of a protected modifier is within the
package and outside the package through child class. If you do not
make the child class, it cannot be accessed from outside the package.
4. Public: The access level of a public modifier is everywhere. It can be
accessed from within the class, outside the class, within the package
and outside the package.
Access Modifier within class within package outside package by subclass only outside package
Private Y N N N
Default Y Y N N
Protected Y Y Y N
Public Y Y Y Y
4. Methods:
A method is a block of code or collection of statements or a set of code
grouped together to perform a certain task or operation. It is used to achieve
the reusability of code. We write a method once and use it many times. We
do not require to write code again and again. It also provides the easy
modification and readability of code, just by adding or removing a chunk of
code. The method is executed only when we call or invoke it.
5. Types of Constructors:
Java constructors or constructors in Java is a terminology been used to
construct something in our programs. A constructor in Java is a special
method that is used to initialize objects. The constructor is called when an
object of a class is created. It can be used to set initial values for object
attributes.
In Java, a constructor is a block of codes similar to the method. It is called
when an instance of the class is created. At the time of calling the
constructor, memory for the object is allocated in the memory. It is a special
type of method which is used to initialize the object. Every time an object is
created using the new() keyword, at least one constructor is called.
Types of Constructors in Java
Now is the correct time to discuss the types of the constructor, so primarily
there are two types of constructors in java:
No-argument constructor
Parameterized Constructor
1. No-argument constructor: A constructor that has no parameter is known
as the default constructor. If we don’t define a constructor in a class, then the
compiler creates a default constructor(with no arguments) for the class.
And if we write a constructor with arguments or no arguments then the
compiler does not create a default constructor.
2. Parameterized Constructor: A constructor that has parameters is known
as parameterized constructor. If we want to initialize fields of the class with
our own values, then use a parameterized constructor.
6. Nested Class:
Java inner class or nested class is a class that is declared inside the class or
interface.
We use inner classes to logically group classes and interfaces in one place to
be more readable and maintainable.
Additionally, it can access all the members of the outer class, including
private data members and methods.
There are three advantages of inner classes in Java. They are as follows:
Sometimes users need to program a class in such a way so that no other class
can access it. Therefore, it would be better if you include it within other
classes.
If all the class objects are a part of the outer object then it is easier to nest that
class inside the outer class. That way all the outer class can access all the
objects of the inner class.
7. Accessor:
o Getter and Setter are methods used to protect your data and make your
code more secure. Getter returns the value (accessors), it returns the
value of data type int, String, double, float, etc. For the program’s
convenience, getter starts with the word “get” followed by the variable
name.
o While Setter sets or updates the value (mutators). It sets the value for
any variable used in a class’s programs. and starts with the word “set”
followed by the variable name. Getter and Setter make the
programmer convenient in setting and getting the value for a particular
data type. In both getter and setter, the first letter of the variable
should be capital.
8. Static Variable:
If you declare any variable as static, it is known as a static variable.
o The static variable can be used to refer to the common property of all
objects (which is not unique for each object), for example, the company
name of employees, college name of students, etc.
o The static variable gets memory only once in the class area at the time
of class loading.
9. Static Method:
If you apply static keyword with any method, it is known as static method.
o A static method belongs to the class rather than the object of a class.
o A static method can be invoked without the need for creating an
instance of a class.
o A static method can access static data member and can change the
value of it.
10. Final Class:
The final modifier for finalizing the implementations of classes, methods, and
variables.
The main purpose of using a class being declared as final is to prevent the
class from being subclassed. If a class is marked as final then no class can
inherit any feature from the final class.
You cannot extend a final class. If you try it gives you a compile time error.
The purpose of creating the final methods is to restrict the unwanted and
improper use of method definition while overriding the method.
Though it is not logically incorrect, it can change the meaning of the method
and the reader might interpret it wrongly. Therefore, in such cases to prevent
the unwanted method definitions, we declare methods as final.
12. Enum:
The Enum in Java is a data type which contains a fixed set of constants.
Java Enums can be thought of as classes which have a fixed set of constants
(a variable that does not change). The Java enum constants are static and final
implicitly. It is available since JDK 1.5.
Enums are used to create our own data type like classes. The enum data type
(also known as Enumerated Data Type) is used to define an enum in Java.
Unlike C/C++, enum in Java is more powerful. Here, we can define an enum
either inside the class or outside the class.
2. Inheritance:
1. What is Inheritance:
Inheritance in Java is a mechanism in which one object acquires all the
properties and behaviors of a parent object. It is an important part of OOPs
The idea behind inheritance in Java is that you can create new classes
that are built upon existing classes. When you inherit from an existing class,
you can reuse methods and fields of the parent class. Moreover, you can add
new methods and fields in your current class also.
Inheritance represents the IS-A relationship which is also known as
a parent-child relationship.
Why use inheritance in java:
For method overriding (so runtime polymorphism can be archived).
For local reusable
Terms used in Inheritance:
o Class: A class is a group of objects which have common properties. It
is a template or blueprint from which objects are created.
o Sub Class/Child Class: Subclass is a class which inherits the other
class. It is also called a derived class, extended class, or child class.
o Super Class/Parent Class: Superclass is the class from where a
subclass inherits the features. It is also called a base class or a parent
class.
o Reusability: As the name specifies, reusability is a mechanism which
facilitates you to reuse the fields and methods of the existing class
when you create a new class. You can use the same fields and methods
already defined in the previous class.
2. Types of Inheritance:
3. Advantages of Inheritance:
Benefits of Inheritance
Inheritance helps in code reuse. The child class may use the code
defined in the parent class without re-writing it.
Inheritance can save time and effort as the main code need not be
written again.
Inheritance provides a clear model structure which is easy to
understand.
An inheritance leads to less development and maintenance costs.
With inheritance, we will be able to override the methods of the base
class so that the meaningful implementation of the base class method
can be designed in the derived class. An inheritance leads to less
development and maintenance costs.
In inheritance base class can decide to keep some data private so that it
cannot be altered by the derived class.
Costs of Inheritance
Inheritance decreases the execution speed due to the increased time and
effort it takes, the program to jump through all the levels of overloaded
classes.
Inheritance makes the two classes (base and inherited class) get tightly
coupled. This means one cannot be used independently of each other.
The changes made in the parent class will affect the behavior of child
class too.
The overuse of inheritance makes the program more complex.
4. Object Class:
3. Polymorphism:
1. What is Polymorphism:
Polymorphism in Java is a concept by which we can perform a single
action in different ways. Polymorphism is derived from 2 Greek words: poly
and morphs. The word "poly" means many and "morphs" means forms. So
polymorphism means many forms.
There are two types of polymorphism in Java: compile-time polymorphism
and runtime polymorphism. We can perform polymorphism in java by
method overloading and method overriding.
If you overload a static method in Java, it is the example of compile time
polymorphism.
2. Method Overriding:
If subclass (child class) has the same method as declared in the parent class, it
is known as method overriding in Java.
In other words, If a subclass provides the specific implementation of the
method that has been declared by one of its parent class, it is known as
method overriding.
Usage of Java Method Overriding
o Method overriding is used to provide the specific implementation of a
method which is already provided by its superclass.
o Method overriding is used for runtime polymorphism
Rules for Java Method Overriding
1. The method must have the same name as in the parent class
2. The method must have the same parameter as in the parent class.
3. There must be an IS-A relationship (inheritance).
3. Method Overloading:
If a class has multiple methods having same name but different in parameters,
it is known as Method Overloading.
If we have to perform only one operation, having same name of the methods
increases the readability of the program.
Suppose you have to perform addition of the given numbers but there can be
any number of arguments, if you write the method such as a(int,int) for two
parameters, and b(int,int,int) for three parameters then it may be difficult for
you as well as other programmers to understand the behavior of the method
because its name differs.
1. Abstraction:
Abstraction is a process of hiding the implementation details and showing
only functionality to the user.
Another way, it shows only essential things to the user and hides the internal
details, for example, sending SMS where you type the text and send the
message. You don't know the internal processing about the message delivery.
Abstraction lets you focus on what the object does instead of how it does it.
Ways to achieve Abstraction
There are two ways to achieve abstraction in java
1. Abstract class (0 to 100%)
2. Interface (100%)
Advantages of Abstraction
1. It reduces the complexity of viewing things.
2. Avoids code duplication and increases reusability.
3. Helps to increase the security of an application or program as only
essential details are provided to the user.
4. It improves the maintainability of the application.
5. It improves the modularity of the application.
6. The enhancement will become very easy because without affecting
end-users we can able to perform any type of changes in our internal
system.
2. Abstract Classes:
A class is declared abstract using the abstract keyword. It can have zero or
more abstract and non-abstract methods. We need to extend the abstract class
and implement its methods. It cannot be instantiated.
Abstract classes and Abstract methods :
1. An abstract class is a class that is declared with an abstract
keyword.
2. An abstract method is a method that is declared without
implementation.
3. An abstract class may or may not have all abstract methods. Some
of them can be concrete methods
4. A method-defined abstract must always be redefined in the
subclass, thus making overriding compulsory or making the
subclass itself abstract.
5. Any class that contains one or more abstract methods must also be
declared with an abstract keyword.
6. There can be no object of an abstract class. That is, an abstract
class can not be directly instantiated with the new operator.
7. An abstract class can have parameterized constructors and the
default constructor is always present in an abstract class.
3. Abstract Method:
A method declared using the abstract keyword within an abstract class and
does not have a definition (implementation) is called an abstract method.
When we need just the method declaration in a super class, it can be achieved
by declaring the methods as abstracts.
Abstract method is also called subclass responsibility as it doesn't have the
implementation in the super class. Therefore a subclass must override it to
provide the method definition.
Points to Remember
Following points are the important rules for abstract method in Java:
o An abstract method do not have a body (implementation), they just
have a method signature (declaration). The class which extends the
abstract class implements the abstract methods.
o If a non-abstract (concrete) class extends an abstract class, then the
class must implement all the abstract methods of that abstract class. If
not the concrete class has to be declared as abstract as well.
o As the abstract methods just have the signature, it needs to have
semicolon (;) at the end.
o Following are various illegal combinations of other modifiers for
methods with respect to abstract modifier:
o final
o abstract native
o abstract synchronized
o abstract static
o abstract private
o abstract strictfp
o If a class contains abstract method it needs to be abstract and vice versa
is not true.
4. Interfaces:
An interface in Java is a blueprint of a class. It has static constants and
abstract methods.
The interface in Java is a mechanism to achieve abstraction. There can be
only abstract methods in the Java interface, not method body. It is used to
achieve abstraction and multiple inheritance in Java.
In other words, you can say that interfaces can have abstract methods and
variables. It cannot have a method body.
Java Interface also represents the IS-A relationship.
It cannot be instantiated just like the abstract class.
Since Java 8, we can have default and static methods in an interface.
Since Java 9, we can have private methods in an interface.
Why use Java interface?
There are mainly three reasons to use interface. They are given below.
o It is used to achieve abstraction.
o By interface, we can support the functionality of multiple inheritance.
o It can be used to achieve loose coupling.
5. Multiple Inheritance:
1. Composition:
5. Encapsulation:
Encapsulation is defined as the wrapping up of data under a single unit. It is
the mechanism that binds together code and the data it manipulates. Another
way to think about encapsulation is, that it is a protective shield that prevents
the data from being accessed by the code outside this shield.
Technically in encapsulation, the variables or data of a class is hidden from
any other class and can be accessed only through any member function of its
own class in which it is declared.
As in encapsulation, the data in a class is hidden from other classes using the
data hiding concept which is achieved by making the members or methods of
a class private, and the class is exposed to the end-user or the world without
providing any details behind implementation using the abstraction concept,
so it is also known as a combination of data-hiding and abstraction.
Encapsulation can be achieved by Declaring all the variables in the class as
private and writing public methods in the class to set and get the values of
variables.
It is more defined with the setter and getter method.
Advantages of Encapsulation:
Data Hiding: it is a way of restricting the access of our data members by
hiding the implementation details. Encapsulation also provides a way for data
hiding. The user will have no idea about the inner implementation of the
class. It will not be visible to the user how the class is storing values in the
variables. The user will only know that we are passing the values to a setter
method and variables are getting initialized with that value.
Increased Flexibility: We can make the variables of the class read-only or
write-only depending on our requirement. If we wish to make the variables
read-only then we have to omit the setter methods like setName(), setAge(),
etc. from the above program or if we wish to make the variables write-only
then we have to omit the get methods like getName(), getAge(), etc. from the
above program
Reusability: Encapsulation also improves the re-usability and is easy to
change with new requirements.
Testing code is easy: Encapsulated code is easy to test for unit testing.
6. Single Responsibility Principle:
As the name suggests, this principle states that each class should have one
responsibility, one single purpose. This means that a class will do only one
job, which leads us to conclude it should have only one reason to change.
7. Open/Closed Principle:
As the name suggests, this principle states that software entities should be
open for extension, but closed for modification. As a result, when the
business requirements change then the entity can be extended, but not
modified.
IV. Algorithms:
1. Time Complexity:
The time complexity of an algorithm quantifies the amount of time taken by
an algorithm to run as a function of the length of the input. Note that the time
to run is a function of the length of the input and not the actual execution
time of the machine on which the algorithm is running on.
In order to calculate time complexity on an algorithm, it is assumed that
a constant time c is taken to execute one operation, and then the total
operations for an input length on N are calculated.
2. Space Complexity:
The space complexity of an algorithm or a computer program is the amount
of memory space required to solve an instance of the computational
problem as a function of characteristics of the input. It is the memory
required by an algorithm until it executes completely.
3. Techniques:
1. Brute Force Algorithms:
Brute force algorithm is an algorithm that goes straight forward, through all
cases to get suitable solutions.
For example, we have a dictionary, and we want to find a word in it. With
brute force algorithm, we will go with the natural order - from A to Z of this
dictionary. If this word starts with z character, it takes so much time to find it.
2. Greedy Algorithms:
Greedy is an algorithmic paradigm that builds up a solution piece by piece,
always choosing the next piece that offers the most obvious and immediate
benefit. So the problems where choosing locally optimal also leads to global
solution are the best fit for Greedy.
Divide and Conquer algorithm consists of a dispute using the following three
steps.
One pointer starts from beginning and other from the end and they proceed
towards each other
2. Different Paces
Both pointers start from the beginning but one pointer moves at a faster pace
than the other one.
We one by one consider all cycles. We first consider the cycle that includes
the first element. We find the correct position of the first element, and place
it at its correct position, say j. We consider the old value of arr[j] and find
its correct position, we keep doing this till all elements of the current cycle
are placed at the correct position, i.e., we don’t come back to the cycle
starting point.
8. Topological Sorting:
Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering
of vertices such that for every directed edge u v, vertex u comes before v in
the ordering. Topological Sorting for a graph is not possible if the graph is
not a DAG.
For example, a topological sorting of the following graph is “5 4 2 3 1 0”.
There can be more than one topological sorting for a graph. For example,
another topological sorting of the following graph is “4 5 2 3 1 0”. The first
vertex in topological sorting is always a vertex with in-degree as 0 (a vertex
with no incoming edges).
Min-Heap
In a Min-Heap the key present at the root node must be less than or equal
among the keys present at all of its children. The same property must be
recursively true for all sub-trees in that Binary Tree. In a Min-Heap the
minimum key element present at the root. Below is the Binary Tree that
satisfies all the property of Min Heap.
Max Heap
3. Insertion Sort:
Insertion sort is a simple sorting algorithm that works similar to the way
you sort playing cards in your hands. The array is virtually split into a sorted
and an unsorted part. Values from the unsorted part are picked and placed at
the correct position in the sorted part.
Characteristics of Insertion Sort:
This algorithm is one of the simplest algorithm with simple
implementation
Basically, Insertion sort is efficient for small data values
Insertion sort is adaptive in nature, i.e. it is appropriate for data sets
which are already partially sorted.
4. Merge Sort:
The Merge Sort algorithm is a sorting algorithm that is based on the Divide
and Conquer paradigm. In this algorithm, the array is initially divided into
two equal halves and then they are combined in a sorted manner.
Merge Sort Working Process:
Think of it as a recursive algorithm continuously splits the array in half until
it cannot be further divided. This means that if the array becomes empty or
has only one element left, the dividing will stop, i.e. it is the base case to
stop the recursion. If the array has multiple elements, split the array into
halves and recursively invoke the merge sort on each of the halves. Finally,
when both halves are sorted, the merge operation is applied. Merge
operation is the process of taking two smaller sorted arrays and combining
them to eventually make a larger one.
5. Quick Sort:
Like Merge Sort, QuickSort is a Divide and Conquer algorithm . It picks an
element as a pivot and partitions the given array around the picked pivot.
There are many different versions of quickSort that pick pivot in different
ways.
Always pick the first element as a pivot.
Always pick the last element as a pivot (implemented below)
Pick a random element as a pivot.
Pick median as the pivot.
The key process in quickSort is a partition(). The target of partitions is,
given an array and an element x of an array as the pivot, put x at its correct
position in a sorted array and put all smaller elements (smaller than x)
before x, and put all greater elements (greater than x) after x. All this should
be done in linear time.
6. Heap Sort:
Heap sort is a comparison-based sorting technique based on Binary
Heap data structure. It is similar to the selection sort where we first find the
minimum element and place the minimum element at the beginning. Repeat
the same process for the remaining elements.
Heap sort is an in-place algorithm.
Its typical implementation is not stable, but can be made stable
(See this)
Typically 2-3 times slower than well-implemented QuickSort. The
reason for slowness is a lack of locality of reference.
Advantages of heapsort:
Efficiency – The time required to perform Heap sort increases
logarithmically while other algorithms may grow exponentially slower
as the number of items to sort increases. This sorting algorithm is very
efficient.
Memory Usage – Memory usage is minimal because apart from what
is necessary to hold the initial list of items to be sorted, it needs no
additional memory space to work
Simplicity – It is simpler to understand than other equally efficient
sorting algorithms because it does not use advanced computer science
concepts such as recursion
7. Bucket Sort:
Bucket sort is mainly useful when input is uniformly distributed over a range.
For example, consider the following problem.
Sort a large set of floating point numbers which are in range from 0.0 to 1.0
and are uniformly distributed across the range. How do we sort the numbers
efficiently?
A simple way is to apply a comparison based sorting algorithm. The lower
bound for Comparison based sorting algorithm (Merge Sort, Heap Sort,
Quick-Sort .. etc) is Ω(n Log n), i.e., they cannot do better than nLogn.
Can we sort the array in linear time? Counting sort can not be applied here as
we use keys as index in counting sort. Here keys are floating point numbers.
5. Searching:
1. Tree Traversal Algorithms (Pre-order, In-Order, Post-
Order):
Traversal is a process to visit all the nodes of a tree and may print their values
too. Because, all nodes are connected via edges (links) we always start from
the root (head) node. That is, we cannot randomly access a node in a tree.
There are three ways which we use to traverse a tree −
In-order Traversal
Pre-order Traversal
Post-order Traversal
Generally, we traverse a tree to search or locate a given item or key in the
tree or to print all the values it contains.
In-order Traversal
In this traversal method, the left subtree is visited first, then the root and later
the right sub-tree. We should always remember that every node may
represent a subtree itself.
If a binary tree is traversed in-order, the output will produce sorted key
values in an ascending order.
We start from A, and following in-order traversal, we move to its left
subtree B. B is also traversed in-order. The process goes on until all the nodes
are visited. The output of inorder traversal of this tree will be −
D→B→E→A→F→C→G
Pre-order Traversal
In this traversal method, the root node is visited first, then the left subtree and
finally the right subtree.
We start from A, and following pre-order traversal, we first visit A itself and
then move to its left subtree B. B is also traversed pre-order. The process
goes on until all the nodes are visited. The output of pre-order traversal of
this tree will be −
A→B→D→E→C→F→G
Post-order Traversal
In this traversal method, the root node is visited last, hence the name. First we
traverse the left subtree, then the right subtree and finally the root node.
We start from A, and following Post-order traversal, we first visit the left
subtree B. B is also traversed post-order. The process goes on until all the
nodes are visited. The output of post-order traversal of this tree will be −
D→E→B→F→G→C→A
Full form BFS stands for Breadth First Search. DFS stands for Depth First Search.
Technique It a vertex-based technique to find the shortest path in It is an edge-based technique because the vertices
a graph.
along the edge are explored first from the starting to the
end node.
Definition BFS is a traversal technique in which all the nodes of DFS is also a traversal technique in which traversal is
the same level are explored first, and then we move to
the next level. started from the root node and explore the nodes as far
as possible until we reach the node that has no unvisited
adjacent nodes.
Data Structure Queue data structure is used for the BFS traversal. Stack data structure is used for the BFS traversal.
Backtracking BFS does not use the backtracking concept. DFS uses backtracking to traverse all the unvisited
nodes.
Number of edges BFS finds the shortest path having a minimum number In DFS, a greater number of edges
of edges to traverse from the source to the destination
vertex. are required to traverse from the source vertex to the
destination vertex.
Optimality BFS traversal is optimal for those vertices which are DFS traversal is optimal for those
to be searched closer to the source vertex.
graphs in which solutions are away from the source
vertex.
Suitability for It is not suitable for the decision tree because it It is suitable for the decision tree. Based on the decision,
decision tree requires exploring all the neighboring nodes first. it explores all the paths. When the goal is found, it stops
its traversal.
Memory efficient It is not memory efficient as it requires more memory It is memory efficient as it requires less memory than
than DFS. BFS.
3. Linear Search:
Linear search is used to search a key element from multiple elements. Linear
search is less used today because it is slower than binary search and hashing.
Algorithm:
4. Binary Search:
Binary Search is a searching algorithm used in a sorted array
by repeatedly dividing the search interval in half. The idea of binary search
is to use the information that the array is sorted and reduce the time
complexity to O(Log n).
Binary Search Algorithm: The basic steps to perform Binary Search are:
Begin with the mid element of the whole array as a search key.
If the value of the search key is equal to the item then return an index
of the search key.
Or if the value of the search key is less than the item in the middle of
the interval, narrow the interval to the lower half.
Otherwise, narrow it to the upper half.
Repeatedly check from the second point until the value is found or the
interval is empty.
Binary Search Algorithm can be implemented in the following two ways
1. Iterative Method
2. Recursive Method