Java equals() vs. compareTo() contract

1 05 2016

The definitions

Object’s equals(Object obj) method:

Indicates whether some other object is "equal to" this one

compareTo(T t) is an abstract method from Comparable interface and it:

Compares this object with the specified object for order. 
Returns a negative integer, zero, or a positive integer 
as this object is less than, equal to, or greater than the specified object.

The contract

That being said here seems to appear the contract between those two methods: compareTo should return 0 if equals returns true. This is yet another contract that the equals() is involved (mind the hashCode()). In fact the Comparable JavaDoc says it clearly:

It is strongly recommended, but not strictly required that 
(x.compareTo(y)==0) == (x.equals(y)).

Since it is not strictly required, the JavaDoc clears out things and says:

Generally speaking, any class that implements the Comparable interface 
and violates this condition should clearly indicate this fact. 
The recommended language is: 
"Note: this class has a natural ordering that is inconsistent with equals."

Natural ordering

A class should implement Comparable interface if it is eligible to natural ordering. Natural ordering is perceived as an order where an object can be compared to another of the same type (alphabetical comparison for String, numerical for Number or any other specific to your domain).

If your class does not have a natural ordering that is consistent with equals() but you still need to sort it, you should consider using the Comparator interface instead of Comparable.

In fact the String class follows the equals() and compareTo() contract and it sorts Strings alphabetically according to ASCII characters ordering. Note that this ordering sorts lowercase letters in front of upper case.

The BigDecimal breaks the contract

The BigDecimal class violates the recommendation and its equals() is not consistent with compareTo() inĀ  a matter of a scale. The documentation clearly states it:

according to equals(): 2.0 is not equal to 2.00
according to compareTo(): 2.0 is equal to 2.00

Why? Who knows. There are discussions that in physics for example the 2.0 definetely should not be considered as equal to 2.00 due to the precision. However in most of the domains the values can be considered as equal.

Problems when violating the contract

The problem that arises is for example putting the BigDecimal in a HashSet:

Set<BigDecimal> set = new HashSet<>();
set.add(new BigDecimal("0.20"));
...
if (set.contains(new BigDecimal("0.2")) { // Returns false, but should return true
    ...
}

Because of that the HashSet is usable with BigDecimal if you define the scale that its elements have. So if you put the number with two digits precision, you should always look for two digits precision as well.

The other solution is to use TreeSet, since it uses the compareTo() to search for elements (ignoring the scale). However the TreeSet is sorted as opposed to the HashSet. If you do not need sorting then you ahve to consider whether it hurts your performance (putting an element to the sorted set is more expensive – due to the sorting).

Advertisements




Java 8 StringJoiner demo

24 01 2016

Finally Java has convenient and intuitive API for joining strings with delimiters! Since Java 8 there is StringJoiner class. It is an API that you may know from Guava Joiner classes (see my post: https://looksok.wordpress.com/2015/10/17/guava-joiner-join-all-strings-in-an-array-or-map/). Here is a short StringJoiner demo.

Basic String joins

The most basic usage is to create StringJoiner instance with delimiter as a constructor param and add() strings:

StringJoiner joiner = new StringJoiner(",");
joiner.add("apple");
joiner.add("banana");
joiner.add("orange");

System.out.println("Joiner result is: " + joiner.toString());

The result is:

Joiner result is: apple,banana,orange

If you prefer, you can chain add() calls:

StringJoiner joiner = new StringJoiner(",")
    .add("apple")
    .add("banana")
    .add("orange");

Join Collection of Strings

If you have Collection of Strings, the new static String.join() method can join them:

List<String> list = Arrays.asList("apple", "banana", "orange");
String joined = String.join(", ", list);

System.out.println("Join Array result is: " + joined);

With the result of:

Join Array result is: apple, banana, orange

Join inline

You can prepare joined String in one line with String.join() overloaded with varargs, like that:

String.join(", ", "apple", "banana", "orange");

Joining Collector in Stream API

When using streams you have joining Collector at your disposal:

List<String> list = Arrays.asList("apple", "banana", "orange");
String joined = list.stream()
        .collect(Collectors.joining(", "));

System.out.println("Joined with collector: " + joined);

This will result with:

Joined with collector: apple, banana, orange

Source Code

As always, I share with you the source code for this demo on my github: https://github.com/yacekmm/looksok/tree/StringJoinerDemo/Java/JavaDemo





Java 7 NIO.2 Paths to make path handling easy for you

12 12 2015

Java 7 introduced a big change in file and directory structure manipulation API. Now you have:

  • Path that represents locacation in a filesystem (filesystem can be Windows, *nix or a zip archive)
  • Files to deal with directory trees (walk directory tree, iterate over files in directory, filter files, perform recursive move, copy, delete)
  • WatchService to listen for the changes in file
  • File attributes access like size, permissions, modification time
  • Symbolic links detection and support
  • Asynchronous I/O operations (with Future or callback style) for processing big files without impact on main application Thread

Now its time for short demo of new Path features. See the demo source code on my GitHub: https://github.com/yacekmm/looksok/tree/PathsDemo/Java/Java7NIO.2

Creating a Path

See how to get the absolute path to the current working directory:

Path path = Paths.get("").toAbsolutePath();
System.out.println("Current working dir absolute path is: " + path);

Current absolute path is:

C:\Code\looksok\looksok\Java\Java7NIO.2

Path properties

On linux system you can analyze the path properties with some convenient methods:

path = Paths.get("/var/lib/jenkins/jobs/sampleJob");
System.out.println("\nSample linux path that will be analyzed: " + path.toAbsolutePath());
System.out.println("Number of segments in path: " + path.getNameCount());
System.out.println("Parent in path: " + path.getParent());
System.out.println("Root on this filesystem: " + path.getRoot());
System.out.println("Subpath (2 elements up): " + path.subpath(path.getNameCount() - 2, path.getNameCount()));

Notice that I’ve created linux Path on Windows machine. This is possible because path is an abstract concept that may point to non existing resource. Because it can be a path to resource that you are just about to create in that Path. Meaning that the path existence will be verified already when you want to do something in this location.

The properties check result is:

Sample linux path that will be analyzed: C:\var\lib\jenkins\jobs\sampleJob
Number of segments in path: 5
Parent in path: \var\lib\jenkins\jobs
Root on this filesystem: \
Subpath (2 elements up): jobs\sampleJob

Symbolic link path finding

When you work with paths containing Symbolic links (kind of shortcuts to the target file on linux OS) you need to know whether it is a file or symlink and get the real location of the file behind the link. Do it with:

path = Paths.get("/var/log/logFile");
System.out.println("Real symlink path: " + path.toRealPath());

getRealPath() throws IOException if Path does not exist. You can also getRealPath(LinkOptions.NOFOLLOW_LINKS) if you want a symlink file path, not the element behind it.

Converting paths

In NIO.2 you can easily join Paths, create a Path between two other Paths, and compare Paths against each other. This is how you join paths

Path currentDir = Paths.get("");
Path relativeConfigPath = Paths.get("conf/app.properties");
Path propertiesFilesPath = currentDir.resolve(relativeConfigPath);

The resolved path is:

C:\Code\looksok\looksok\Java\Java7NIO.2\conf\app.properties

Path between two other Paths

This is how to get the relative path between two other Paths:

Path logsDir = Paths.get("/var/log/myapp");
Path appDir = Paths.get("/var/lib/myapp");
Path relativePathToLogs = appDir.relativize(logsDir);

And the result is a relative path:

..\..\log\myapp

NIO.2 and existing Java’s File class

You can now refactor pre Java 7 code to use new constructs, however if you need to interact with old APIs using File class, be noticed that File has now toPath() method, as well as the Path having its toFile() equivalent:

File file = new File("./PathDemo.java");
Path pathFromFile = file.toPath();
file = pathFromFile.toFile();




Java 7: Try-With-Resources block that closes the resources for you

28 11 2015

Working with resources (like files stream, byte stream, network stream) in Java requires to close() the resource after you’re done. How often did you forget to close it in your finally block? Even if you didn’t – Java creators proved that 75% of close() implementations got bugs in it. Since Java 7 all resources you use can be automatically closed when try-catch block completes thanks to the new syntax.

Closing resources old-style

You needed to try to close the resource in finally block (and handle the exception if close failed). It looked like that:

InputStream is = null;
try{
	is = url.openStream();
	//read and process the stream
}catch(AllPossibleExceptions){
	//and handle them
}finally{
	try{
		if(is != null)
			is.close();
	}catch(IOException){
		//handle exception on closing the stream
	}
}

Quite a lot of repeatable exception handling and null-avoidance code.

Try With Resources (TWR) Syntax in Java 7

This is how you write resource processing code since Java 7 with improved try()-catch syntax:

try (InputStream is = url.openStream()){
	//read and process the stream
	//stream will be automatically closed for you
}catch(AllPossibleExceptions){
	//and handle them
}

Notice the new try() syntax where you initialize the resource. Stream that is declared there will be automatically closed when the try block execution ends. You can have more Streams initialized there, separated with semicolons.

Why the resource is being closed?

Because it implements AutoClosable interface – the new interface in Java 7. Class must implement that interface to appear in the try() clause. The AutoClosable is a superinterface of the Closable interface (both have only the close() method that closes the resource)

Ceveat

Be aware that nested resources will not be closed. Do not use syntax like that:

try ( ObjectInputStream in = new ObjectInputStream(new 
			FileInputStream("app.properties")) ) {
	//...
}

the issue here is that the inner resource (FileInputStream) will not be closed. If you need constructs like this, where one resource depends on another, then separate their initialization like that:

try ( FileInputStream fileStream = new FileInputStream("app.properties");
	ObjectInputStream in = new ObjectInputStream(fileStream) ) {
	//...
}

then the TWR will work its best for you.





Java 7 enhanced syntax for numeric literals

21 11 2015

Two convenient syntax improvements were implemented in Java 7 in this topic:

  • Numeric constants can now be expressed as binary literals
  • underscores can be used in integers as a separator for sake of readability

Binary literals

Before Java 7 to get an Integer value from binary literal you needed to parse it like that:

int i = Integer.parseInt("10110011", 2);

There is a set of problems with this implementation (it’s verbose, not popular two-argument parseInt(), RuntimeException on typo).

In Java 7 there is a solution – new syntax that fixes all issues and is neat:

int i = 0b10110011;

cute :)

Underscores in integers

In every day life, you prefer to have phone number written like this:

988343222

or like this:

988-343-222

This is why since Java 7 you can separate long integers with underscore to improve readability:

int milis = 3_600_000;

The underscore is removed by javac while generating .class files





Java 7 improved try-catch exception handling

7 11 2015

Java 8 with its Coin project introduced convenient exception handling with multi-catch blocks and final rethrows that make exception catching less verbose and more clean.

Multi-catch block

How often you catch two types of Exceptions and need to handle it in two separate catch blocks, even if the handle method does exact the same thing? Like that:

try{
  methodThatThrowsIOException();
  methodThatThrowsFileNotFoundException();
}catch(IOException e){
  System.out.println("Exception occured: " + e.getMessage());
}catch(FileNotFoundException e){
  System.out.println("Exception occured: " + e.getMessage());
}

Since Java 7 you can catch both types in one block with pipe operator:

try{
  methodThatThrowsIOException();
  methodThatThrowsFileNotFoundException();
}catch(IOException | FileNotFoundException e){
  System.out.println("Exception occured: " + e.getMessage());
}

Notice that here the e is of supertype Exception – it is not known at compile time. So you have access only to the Exception class API (not the IOException or FileNotFoundException). If you need type distinction, use the old try-catch API.

Final rethrow

Often times you want to rethrow caught exception to propagate it. Then you use throw mechanism and declare your method signature as throwing certain exception type. Before Java 7 yo could write:

public void myMethod() throws Exception{
  try{
    methodThatThrowsIOException();
    methodThatThrowsFileNotFoundException();
  }catch(Exception e){
    throw e;
  }
}

Notice that in that case you do not know the exception type at compile time, so myMethod() needs to throw the Exception type which is bad practice. The real type is missed in that block. On the other side you can easily notice that the exception types thrown in the try() block are IOException and FileNotFoundException. How to help compiler noticing that? Use keyword final:

public void myMethod() throws IOException, FileNotFoundException{
  try{
    methodThatThrowsIOException();
    methodThatThrowsFileNotFoundException();
  }catch(final Exception e){
    throw e;
  }
}

Now your method is aware of exception types and you can declare what subtypes are thrown.





Guava Splitter class demo

24 10 2015

Split String with a delimiter to obtain the array of parts – a common task in programming. Especially when you work on parsing files (like CSV), input streams, etc.

Let’s take the row of a sample csv file:

String csvRow = "desk,,chair,table,couch,";

Notice that it’s a bit tricky, since that particular row has second and the last column empty.

Source Code

if you want to run it on your own, get the source code from my github: https://github.com/yacekmm/looksok/tree/SplitterDemo/Guava/GettingStarted

Java String.split()
Java’s split() works not intuitive, since the result of splitting:

String[] result = stringToSplit.split(",");

is:

[desk, , chair, table, couch]

Notice that there is no last column. Java by default threw it out. I prefer such decisions to be left to the programmer. It is easier to filter out empty items than figure out if there were really just 5 columns or did Java cut out some of them…

Guava Splitter
Guava Splitter API is as simple as Java’s, but the implementation is different. To split String do:

List<String> result = Splitter.on(",").splitToList(stringToSplit);

As a result you get a List that is eagerly evaluated:

[desk, , chair, table, couch, ]

Notice that here you have the last empty string item present.

Guava Lazy Splitter
If you care about performance and lazy evaluation may be helpful in your case you can use the split() method instead of splitToList():

Iterable result = Splitter.on(",").split(stringToSplit);

The split() result of course contains exactly the same result as splitToList().

The result is a lazy evaluated Iterator instance. That means that at this point you actually don’t have your string splitted but just ready to be splitted. It will be splitted when you actually call anything on an iterator.

I don’t know the implementation details but I assume that the whole string will not be splitted at once, but it will be splitted step by step when you take the next() item from the iterator. So the real performance gain will be noticable when you do not iterate all items but break at some point.

Otherwise (if you iterate all items) the whole string will be splitted anyway (the difference is only ‘when‘ it will be evaluated).

MapSplitter

If you have key-value map as a plain String like that:

String mapString = "office=desk,kitchen=table,living room=couch";

you can use MapSplitter class to convert it directly to Java Map:

Splitter.MapSplitter mapSplitter = Splitter.on(",").withKeyValueSeparator("=");
Map<String, String> result = mapSplitter.split(mapString);

And as a result you get the map:

{office=desk, kitchen=table, living room=couch}







%d bloggers like this: