Java 8 - Strings, IO's and other bits and pieces


Hi, I am Malathi Boggavarapu working at Volvo Group and i live in Gothenburg, Sweden. I have been working on Java since several years and had vast experience and knowledge across various technologies. In this course we will discuss about Strings, IO enhancements, Comparators and many more. So let's get started

Strings, IO's and other bits and pieces

Java 8 is not only about lambda expressions and Streams.

String class
Java I/O package
Collection interface
Comparators - New way of writing them in Java8.
Numbers
Map
Annotations

Strings in Java 8

String s = "Hello world!"
IntStream stream = s.chars(); // creates a stream on letters of s
stream.mapToObj(letter -> (char)letter)
           .map(Character::toUpperCase)           
           .forEach(System.out::print);

Output will be HELLO WORLD!

StringJoiner



String s1 = "Hello"
String s2 = "World"
String s = s1 + s2;

The above way of string concatenation is not efficient and should not be used because of the multiple creations/deletions of intermediary strings. So StringBuffer class is used using append method.

But StringBuffer is synchronized. So in Java5 StringBuilder is used which is not synchronized and would be better to append the strings than StringBuffer

Join Strings with builtin seperator.

StringJoiner sj = new StringJoiner(",");
s1.add("one").add("two").add("three")
System.out.println(sj.toString()); // outputs one, two, three

We can also pass prefix and suffix as arguments to StringJoiner class

StringJoiner sj = new StringJoiner(",", "{", "}");
s1.add("one").add("two").add("three")
System.out.println(sj.toString()); // outputs {one, two, three}

StringJoiner can also be used from String class but we can not add prefix and suffix to the join method

String s = String.join(",", "one", "two", "three");

we can also write the above line as follows

String s[] = {"one", "two", "three"};
String s = String.join(",", s);

Java I/O enhancements

1) Reading text files

The lines() method has been added to the BufferedReader class

try(
 BufferedReader reader = new BufferedReader(new FileReader(new File("D:/debug.log")));
){
   Stream<String> stream = reader.lines(); // new method in Java 8
 stream.filter(line -> line.contains("ERROR")).findFirst().ifPresent(System.out::println)
}catch(IOExeption e){
     .....
}

If we see the above code, we write BufferedReader code as an argument of try block. This is the feature in Java7. In this way, we no need to explicitly close the reader of the inputStream. It is automatically done for us in the bytecode of java class.

lines()  method is newly added in Java8 which returns the stream of lines from debug.log file
And the stream is operated using several intermediary methods findFirst,isPresent...

File.lines(path)
----------------
Path path = Paths.get("D:", "tmp", "debug.log");
try(
Stream<String> stream = File.lines(path);
){
   Stream<String> stream = reader.lines(); // new method in Java 8
 stream.filter(line -> line.contains("ERROR")).findFirst().ifPresent(System.out::println)
}catch(IOExeption e){
     .....
}

Stream implements AutoCloseable and will close the underlying file.

Reading Directory Entries


Method File.list(path) - Used to read the directory files taking Path as an argument but it does not read the sub directories inside the directory. But no problem, we can acheive this using different method available in java8

Path path = Paths.get("D:", "windows");
try(Stream<Path> stream : Files.list(path)){
   stream.filter(path -> path.tiFile().isDirectory()).forEach(System.out::println);
}catch(Exception e){
 ......
}

Files.walk(path) method - Used to list the files even from sub directories from a directory. The code is same as above except that we use Files.walk(path)


Path path = Paths.get("D:", "windows");
try(Stream<Path> stream : Files.walk(path)){
   stream.filter(path -> path.tiFile().isDirectory()).forEach(System.out::println);
}catch(Exception e){
 ......
}

we can also limit the depth of exploration of sub directories inside a directory by simply passing an additional parameter to walk method

Ex: Follow the same code as above but pass an integer to walk method as below
Files.walk(path, 2). It limits exploration of sub directories. Try it!!

New methods in Iterable, Collection and List
------------------------------------------------------------
New method on Iterable
forEach() - strings.forEach(System.out::println);

New methods on Collection
removeIf() - It accepts a predicate and return boolean value

Collection<String> strings = Arrays.asList("one", "two", "three", "four", "five");
Collection<String> list = new ArrayList(strings);
boolean b = list.removeIf(s -> s.length() > 4);
System.out.println(list.stream().collect(Collectors.joining(",")));

New methods on list
------------------------------

replaceAll() method - It takes function as a parameter which is special type of function called unary operator that takes element from that list and returns another element of the same type. Each element of the list is processed by the function

Ex:

List<String> strings = Arrays.asList("one", "two", "three", "four", "five");
List<String> list  = new ArrayList(strings);
 list.replaceAll(String::toUpperCase);
 System.out.println(list.stream().collect(Collectors.joining(",")));

Method sort() - Sorts content of a list by providing Comparator

Example:
list.sort(Comparator.naturalOrder());
 System.out.println(list.stream().collect(Collectors.joining(",")));

Comparators


Comparator is an interface. In java 7, we should implement our own Comparator interface to compare and arrange the elemnts in an order. We need to address all sort of conditions on the objects that we compare.

Example: while we compare two objects using a field called lastName (Ex: Person), and if they are equal, we should consider some other fields on the objects to compare with.
Also we should add null check on the objects while comparing

All these problems are addressed easily in java8 using comparing method

Comparator<Person> compareLastName =
                                       Comparator.comparing(Person::getLastName)
                                      .thenComparing(Person::getFirstName)

The above code is much better and is very readable. If we see the code, if lastName is same for two objects, thenComparing method is called and compare the objects depending on firstName. thenComparing is used to chain the comparisons of different fields.

thenComparing  is the default method of the interface Comparator

We can reverse a given comparator using reversed method. This is also a default method on interface
Comparator<Person> comp = ....;
Comparator reversedComp = comp.reversed();

naturalOrder method is static method of Comparator interface. It compares comparable objects, for example Strings in a natural order - alphabetical order

Comparator comp =  Comparator.naturalOrder();

nullsFirst is a static method of Comparator interface. If the list have null values, then they are put at the starting of the list.

Comparator comp =  Comparator.nullsFirst(Comparator.naturalOrder());

nullsLast is a static method of Comparator interface. If the list have null values, then they are put at the end of the list

Comparator comp =  Comparator.nullsLast(Comparator.naturalOrder());

Numbers
-------------

In java we have primitive types such int, float, double, char, short and they all got associated wrapper type

Java8 provides new useful methods such as sum, max and min
long max = Long.max(1L, 2L);

The methods are useful to create reduction operations
BinaryOperator<Long> max = (i1, i2) -> i1 + i2; // we can write it as Long::sum

Hashcode computation

Hashcode of integer is an integer itself. But we need to follow special algorithm to generate hascode of Long

Following is the solution in jdk7 to compute hashcode for long variables.

long l = 3141592653589793238L;
int hash = new Long(l).hashCode();

Here there is costly boxing and unboxing of primitive type  to compute the hashcode.

So we have a method in java8 called  hashCode which accepts the primitve type as an argument to compute hashcode for primitive types. See below example.

long l = 3141592653589793238L;
int hash = Long.hashCode(l);

This method is available on 8 wrapper types.

That's all about this course. Hope it is helpful. Please post your comments below.

Happy Learning!!

Comments

Popular posts from this blog

Bash - Execute Pl/Sql script from Shell script

Gradle Fundamentals

Load Balancing using Spring Cloud Netflix Ribbon