Java 9 : Collections factory methods

As of Java 8, there’s  no simple or single statement way of creating collections with a small number of elements. In scenarios like unit tests where a Set of Strings are to be created as mock data,  it is needed to create a new Set and start repeatedly adding the elements which is highly verbose. Java 9 simplifies it through varargs based factory methods to create collections with a small number of elements. List, Set and Map interfaces have of method that creates the respective immutable Collection/Map. These convenience methods are implemented based on varargs

Creating a List

Prior to Java 9, immutable list is created using

List<String> immutableList = Arrays.asList("Cersei", "Hound", "Ilyn Payne", "Melissandre", "Beric Dondarrion");

if we add an element to the immutableList,

immutableList.add("Walder Frey");

an UnsupportedOperationException is thrown. This is OK as we expect the list to be immutable. But the List returned by Arrays.asList is vulnerable to set operation

immutableList.set(2, "Walder Frey");

after this operation, Ilyn Payne is replaced by Walder Frey.

in Java 9 an immutable list can be created using

List<String> immuatbleListJ9 = List.of("Cersei", "Hound", "Ilyn Payne", "Melissandre", "Beric Dondarrion");

if we add an element to the immutableListJ9

immutableListJ9.add("Walder Frey");

an UnsupportedOperationException is thrown as expected.

if we try set operation

immutableListJ9.set(2, "Walder Frey");

an UnsupportedOperationExceptionon is thrown.

Creating a Set

Prior to Java 9, there’s no easy way to create a Set
One way is to create a new Set and add elements one by one

Set<String> theSet = new HashSet&lt;&gt;();
theSet.add("Cersei");
theSet.add("Hound");
theSet.add("Ilyn Payne");

other way is to use

Set<String> theSet = new HashSet<>(Arrays.asList("Cersei", "Hound", "Ilyn Payne", "Melissandre", "Beric Dondarrion")); 

The Set created in above ways is mutable, we can keep adding or removing elements to or from it. This Set can be converted to immutable with the help of Collections.unmodifiableSet

in Java 9, the Set can be created using

Set<String> theSetJ9 = Set.of("Cersei", "Hound", "Ilyn Payne", "Melissandre", "Beric Dondarrion");

The Set created using factory method is an immutable set, it con’t be modified. If try to add or remove an element to or from this set, an UnsupportedOperationException is thrown.

Creating a Map

Prior to Java 9, Map also doesn’t have a easy way to create

Map<Integer, String> theMap = new HashMap&gt;();
theMap.put(1, "Cersei");
theMap.put(2, "Hound");
theMap.put(3, "Ilyn Payne");

The Map created in above way is mutable, we can keep adding or removing entries to or from it. This map can be converted to immutable with the help of Collections.unmodifiableMap

in Java 9, the Map can be created using

Map<Integer, String> theMapJ9 = Map.of(1, "Cersei", 2, "Hound", 3, "Ilyn Payne", 4, "Melissandre",
5, "Beric Dondarrion");

Map.of() method expects the arguments in key1, value, key2, value2 …. keyn, valuen order. As the map needs two different types of elements, Map.of method doesn’t take varags instead it has over loaded versions that takes upto 10 key-value pairs. Map has another version that takes the varags of Map.Entry<K,V>; Map.ofEntries(Map.Entry<K,V> ....). To facilitate the easy creation of Map.Entry objects, another convenience method, Map.entry() is added to the Map interface. Using these two convenience methods a Map object can be created

Map<Integer, String> theMapJ9 = Map.ofEntries(entry(1, "Cersei"), entry(2, "Hound"), entry(3, "Ilyn Payne"),
entry(4, "Melissandre"), entry(5, "Beric Dondarrion")); 

If we perform a put operation on this map, an UnsupportedOperationException is thrown.

The convenience factory methods of List, Set and Map interfaces provides an easy way to create an instance of respective Collection or Map with a small number of elements. The Objects returned by these methods are immutable, any operation trying to modify them will throw an UnsupportedOperationException.

Different ways of String formatting in Java : Benchmarking

Java programmers use mostly the following three ways for formatting or building a string.

  1. String concatenation using + operator
  2. Using String.format
  3. Using StringBuilder

Functionally all these 3 ways will produce the same result but the question is “are all the 3 ways have the same time and space performance?“. I wrote small code snippet to benchmark these three ways.

public static long  stringConcat(int iterations) throws InterruptedException {
		String cersei = "Cersei";
		String nightKing = "Night King";
		long startTime = System.currentTimeMillis();
		for(int i=0; i&lt;iterations; i++) {
		 if(i%2 == 0) {
			String s = i+" Who"+" will"+" kill " + cersei;
		 } else {
			 String s = i+" Is"+ " it " + nightKing;
		 }
		}
		long endTime = System.currentTimeMillis();
		return endTime-startTime;
	}

	public static long stringFormat(int iterations) {
		String cersei = "Cersei";
		String nightKing = "Night King";
		long startTime = System.currentTimeMillis();
		for(int i=0; i&lt;iterations; i++) {
		 if(i%2 == 0) {
			String s = String.format("%d %s %s %s %s", i, "who", "will", "kill", cersei);
		 } else {
			 String s = String.format("%d %s %s %s", i,"is",  "it",  nightKing);
		 }

		}
		long endTime = System.currentTimeMillis();
		return endTime-startTime;
	}

	public static long stringBuilder(int iterations) {
		String cersei = "Cersei";
		String nightKing = "Night King";
		long startTime = System.currentTimeMillis();
		for(int i=0; i&lt;iterations; i++) {
		 if(i%2 == 0) {
			StringBuilder sb = new StringBuilder();
			sb.append(i).append(" who").append(" will").append(" kill ").append(cersei);
			String s = sb.toString();
		 } else {
			 StringBuilder sb = new StringBuilder();
				sb.append(i).append("Is").append(" it").append(nightKing);
				String s = sb.toString();
		 }

		}
		long endTime = System.currentTimeMillis();
		return endTime-startTime;
	}

Ran all 3 methods with an iteration count of 100000.

  1. String concatenation took ~110ms
  2. String.format took ~1600ms
  3. StringBuilder took ~80ms

String.format takes considerably more time because it internally uses the java.util.Formatter and creates several instances of Formatter and other utility Objects that consumes the considerable amount of CPU time and memory. Below is the screen shot of the profiling for this case. We can see that there lot of additional objects being created.

STringFormatProfile.jpg

Similarly I profiled the other methods as well and observed that String concatenation creates more objects than the StringBuilder.

In summary,

String concatenation and StringBuilder performs almost equal w.r.t time complexity but the StringBuilder performs better in terms of space complexity. If performance is a concern, never use String.format.  

 

 

JUnit Practices 1 : Let the Tests Talk

Each variant of the assertion in JUnit has a distinct purpose, used in a right context the assertions yield useful information on test failures. It is possible to construct tests with an assertion that is intend for a different purpose and make it work as expected; when we do so we may possibly loosing the useful information that a failure trace can give us.

Let us explore a couple of test scenarios and see what it yields to

Testing for equality, lets take case of Strings, the equality can be tested in two ways

  1. assertEquals(expected_string, actual_string)
    @Test
    public void testStringWithAssertTrue() {
    assertTrue("abc".equals("vdc"));
    }
  2. asserTure(expected_string.equals(actual_string))
     @Test
    public void testStringWithAssertTrue() {
    assertTrue("abc".equals("vdc"));
    }

Both of these will serve the purpose of testing, but the question is will they give the same insight on a test failure?
The test failure trace for the approach 1 is
java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertTrue(Assert.java:52) 

failure trace for the approach 2 is
org.junit.ComparisonFailure: expected:<[ab]c> but was:<[vd]c>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144) 

The second approach gives a better perspective of the failure; it clearly says whats expected and what is actual.

Use assertEquals(obj1, obj2) to test the equality, avoid assertTrue(obj1.equals(obj2))

Testing for identity equality, i.e. testing if two objects are same. Lets say we are testing a singleton class. We could possibly assert it in two ways

  1. assertTrue(singleton1 == singleto2);
    @Test
    public void faultySingletonMultiThread () throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newFixedThreadPool(2);
    Callable job = () -> FaultySingleton.getInstance();
    Future fSingleTonFuture1 = exec.submit(job);
    Future fSingleTonFuture2 = exec.submit(job);
    assertTrue(fSingleTonFuture1.get() == fSingleTonFuture2.get());
    } 
  2. assertSame(singleton1, singleto2);
    @Test
    public void faultySingletonMultiThread () throws InterruptedException, ExecutionException {
    ExecutorService exec = Executors.newFixedThreadPool(2);
    Callable job = () -> FaultySingleton.getInstance();
    Future fSingleTonFuture1 = exec.submit(job);
    Future fSingleTonFuture2 = exec.submit(job);
    assertSame(fSingleTonFuture1.get(), fSingleTonFuture2.get());
    } 

The failure trace of approach 1 is
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:86)
at org.junit.Assert.assertTrue(Assert.java:41)
at org.junit.Assert.assertTrue(Assert.java:52)

and the failure trace of approach 2 is
java.lang.AssertionError: expected same:<org.practprogrammer.junitpractices.FaultySingleton@6477463f> was not:<org.practprogrammer.junitpractices.FaultySingleton@3d71d552>
at org.junit.Assert.fail(Assert.java:88)

The failure trace of approach 2 is more clear and one can clearly understand that the test expects the same object.

Use assertSame(obj1, obj2) to test the identity equality, avoid assertTrue(obj1 == obj2)

To conclude,

In almost all the scenarios, it is possible to use assertTrue to verify the results. By doing so, we are muting the test, we aren’t letting the failure trace talk. Use appropriate assertion variant and let the tests talk .

The source code for the examples used in this article can be found @ my github

Handling unused imports in Eclipse

The following three ways can be used to fix the unused imports in Eclipse

  1. If you have a file or two with unused imports, open the file that has unused imports and use ctrl+shift+O, this automatically organizes the imports; removes unused and adds any missing imports
  2. Avoid unused imports by enabling the organize imports option in Window -> Preferences -> Java -> Editor -> Save Actions . Once this is done eclipse removes the imports automatically when a file is savedUnusedImport_SaveAction.jpg
  3. When there are already a large number of unused imports, all of them can be fixed at once by following the below steps
  • Open the Problems window in eclipse, select all the unused imports warnings
  • Right click on them and select the Quick Fix optionImport_QuickFix.jpg
  • Above step opens Quick Fix pop-window, click finishUnusedImport_SaveAction.jpg