Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 6

MY JAVA LEARNINGS

1. Java Collections
I. Comparator and comparable:
 Comparable and Comparator both are interfaces and can be used
to sort collection elements.
 If any class implements Comparable interface in Java then collection
of that object either List or Array can be sorted automatically by
using Collections.sort() or Arrays.sort() method and objects will be
sorted based on their natural order defined by CompareTo method.

Note: if sorting of objects needs to be based on natural order


then use Comparable whereas if you sorting needs to be done
on attributes of different objects, then use Comparator in Java.

Comparable Comparator

1) Provides Single sorting sequence. Provides Multiple sorting sequences.

2) Provides Natural Sorting order Provides Customized Sorting order

3) Affects the original class, i.e., the Comparator doesn't affect the original class,
actual class is modified. i.e., the actual class is not modified.

4) Provides compareTo() method to sort Comparator provides compare() method to


elements. sort elements.

5) java.lang package. java.util package.

6) Sort the list elements of Comparable Sort the list elements of Comparator type
type by Collections.sort(List) method. by Collections.sort(List,Comparator) method.

7) Capable of comparing itself with Capable of  comparing different objects


another object of same class of different classes
The Comparable interface is a good choice to use for defining the default
ordering, or in other words, if it's the main way of comparing objects.
So why use a Comparator if we already have Comparable?
There are several reasons why:
 Sometimes we can't modify the source code of the class whose objects we
want to sort, thus making the use of Comparable impossible
 Using Comparators allows us to avoid adding additional code to our domain
classes
 We can define multiple different comparison strategies, which isn't possible
when using Comparable

Java 8 provides new ways of defining Comparators by using lambda


expressions, and the comparing() static factory method.

Let's see a quick example of how to use a lambda expression to create


a Comparator:
Comparator byRanking = (Player player1, Player player2) ->
Integer.compare(player1.getRanking(),player2.getRanking());

The Comparator.comparing method takes a method calculating the property that


will be used for comparing items, and returns a matching Comparator instance:
Comparator<Player> byRanking = Comparator.comparing(Player::getRanking);
Comparator<Player> byAge = Comparator.comparing(Player::getAge);

Avoid the Substraction trick:


Integer.compare() method to compare two integers. However, one might argue
that we should use this clever one-liner instead:
Comparator<Player> comparator = (p1, p2) -> p1.getRanking() -
p2.getRanking();

Although it's much more concise than other solutions, it can be a victim of
integer overflows in Java

Player player1 = new Player(59, "John",


Integer.MAX_VALUE);
Player player2 = new Player(67, "Roger", -1);
List<Player> players = Arrays.asList(player1, player2);
players.sort(comparator);

Since -1 is much less than the Integer.MAX_VALUE, “Roger” should come before


“John” in the sorted collection. However, due to integer overflow,
the “Integer.MAX_VALUE – (-1)” will be less than zero. So based on
the Comparator/Comparable contract, the Integer.MAX_VALUE is less than -1,
which is obviously incorrect.
Therefore, despite what we expected, “John” comes before “Roger” in the sorted
collection:

II. Double colon operator (::)


(Or) Method Reference operator: is used to call a
method by referring to it with the help of its class directly.

It can be used to refer:


 a static method,
 an instance method, or
 a constructor.

They behave exactly as the lambda expressions. The only


difference it has from lambda expressions is that this uses direct
reference to the method by name instead of providing a delegate
to the method.

Stream<String> stream
            = Stream.of("Geeks", "For",
                        "Geeks", "A",
                        "Computer",
                        "Portal");

 Lambda expression: stream.forEach(s-> System.out.println(s));


Double colon operator: stream.forEach( System.out::println);
class GFG {

    // static function to be called


    static void someFunction(String s)
    {
        System.out.println(s);
    }

// non-static function to be called


    void someFunction1(String s)
    {
        System.out.println(s);
    }

    public static void main(String[] args) {


        List<String> list = new ArrayList<String>();
        list.add("Geeks");
        list.add("For");
        list.add("GEEKS");

        // call the static method


        list.forEach(GFG::someFunction);
        // call the non-static method of same class
        list.forEach((new GFG())::someFunction1);

Call the super method


super::methodName;
Call the class constructor
ClassName::new
Call the instance method of same class
objectOfClass::methodName
Ex: System.out::println
Instance method of an arbitrary object of
a particular type/different class
ClassName::methodName
Ex
class Test {
String str=null;
Test(String s)
{
this.str=s;
}
// instance function to be called
void someFunction()
{
System.out.println(this.str);
}
}
class GFG {
public static void main(String[] args) {
List<Test> list = new ArrayList<Test>();
list.add(new Test("Geeks"));
list.add(new Test("For"));
list.add(new Test("GEEKS"));
// call the instance method of type test
// using double colon operator
list.forEach(Test::someFunction);
}
}
III. Boxed Stream
Streams do not treat the primitive types the same as objects.

In Stream API, a stream of primitives can be represented by the


following 3 classes:

 IntStream
 LongStream
 DoubleStream

To convert from a stream of primitives to a stream of


objects, these classes provide boxed() method that returns
a Stream consisting of the elements of the given stream,
each boxed to an object of the corresponding wrapper class.
Stream<Integer> stream = IntStream.of(1, 2, 3, 4, 5).boxed();

Stream<Long> stream1 = LongStream.of(1, 2, 3, 4, 5).boxed();

Stream<Double> stream2 = DoubleStream.of(1.0, 2.0, 3.0, 4.0, 5.0).boxed();

Need of Boxed Streams

Without boxing the stream items, we cannot perform the regular


stream operations on them. For example, we cannot collect the int
values to a list, directly.
//Compilation issue

/*List<Integer> list = IntStream.of(1,2,3,4,5)

.collect(Collectors.toList());*/

To make the above collecting process work, we must box the stream items first.

List<Integer> list = IntStream.of(1,2,3,4,5)

.boxed()
.collect(Collectors.toList());

Polymorphism:
1. Overriding:
 Run-time (or dynamic) polymorphism
 A subclass (or derived class) provides a specific implementation
of a method in superclass (or base class) at runtime.
Overloading:
 Compile-time (or static) polymorphism
 Allows different methods to have same name, but different
signatures, especially number of input parameters and type of
input parameters.

 We cannot override static methods in java because it is


looked up at compile-time statically.
 Overriding occurs dynamically during run-time
 We can have two or more static methods with the same
name, but differences in input parameters

You might also like