Java List Performance

I’ve been taking the algorithms class with Robert Sedgewick through Coursera.  It’s been a rather humbling experience.  However, it made me begin to question the algorithms and datastructures which I turn to everyday.  How good does the JDK implementation perform?  Are there superior drop-in replacements?  If so, when should I use one versus another?

With so many questions and no answers, I decided to put them to the test along side some alternate and generally available collection libraries.

In this installment, I will focus on the list.

The Candidates

Notably missing from the list of contestants is Trove.  I chose not to assess Trove because I was limiting the test to drop-in replacements to a generic List.  Trove creates different implementations specific to the primitive type which it contains.

Java collections

In this benchmark I am using the default Java collections classes supplied with and running on the 1.7.0.06 version of the HotSpot JDK.  The two list implementations we’ll be looking at here are:

  • ArrayList
  • LinkedList

Apache commons collections

We’ll also be taking a look at the default list collections supplied with the 3.2.1 version of the Apache Commons collections libraries.  The list candidates we’ll be looking at are:

  • TreeList
  • GrowthList
  • NodeCachingLinkedList

Javalution

We’ll also take a look at the Javalution FastList implementation.

Guava

Last, but not least, we’ll be benchmarking the Guava list implementation.

The Benchmark

The benchmark will consist of a series of tests on lists of various sizes with a list payload of type Integer.

First, the test will acquiesce the machine by calling an explicit System.gc and pausing for an interval before beginning the test.  Then, the test will:

  1. add – Create a list of size N.
  2. get – Get each of the N items from the list.
  3. iterate – Iterate over each of the items in the list.
  4. randomInsert – Insert N items into the list at randomly generated locations.
  5. removeFromHead – Remove N items from the beginning of the list.
  6. removeFromTail – Remove N items from the end of the list.
  7. addToTail – Add N items to the end of the list.
  8. randomRemove – Remove N items from random locations within the list.
  9. memory – Record the deep memory usage of the list.
  10. clearMemory – Call clear(), then record the deep memory usage of the list.

Tests were conducted for the for N = [1000, 10000, 100000, 200000 ] though I stop at 100,000 in cases where the trends are evidient.

The Results

In this benchmark, we start with an empty list then iterate with I from zero to N-1 adding an unique integer value I to the list.

The TreeList has the most overhead in creating the list.  FastList also has some overhead associated with it.

At larger values of N, the performance of Java Link List, FastList and NodeCachingLinkList begin to suffer.  Linked list implementations tend to have poor get performance due to the fact that they must iterate over each element in the list until they find the location of the item in which they seek.

FastList appears to be link list based, however, with much worse performance at higher values of N.

All of the implementations are within an order of magnitude of each other with respect to iterating.  ArrayList and GrowthList being the worst performers.

In this test, we insert N items into a list of N items with a result being a list of size 2N.  In this benchmark we begin to see some drastic design tradeoffs in the various list implementations.

In order to avoid use of a logarithmic scale, I divided the chart into 2 separate charts to prevent larger values from obscuring smaller values.
Even with lists of 10k or smaller we see that TreeList is the fastest by far.  FastList is the slowest.  GuavaList performs admirably as well, beating the Java ArrayList substantially.

At even higher values of N, we can see which implementations should be avoided if the number of items we will store is large, and if random insertion is a common operation.

Random removal of N items from a list of size 2N reveals 3 poorly performing implementations:

Java ArrayList, GrowthList and GuavaList.  It is my educated guess that these three implementations are backed with some sort of resizable array which must be shifted periodically in order to efficiently reclaim memory.

Removal from the end of a list appears to be the worst case for the FastList.

In this benchmark we start with a list of 2N items, then remove N of them from random locations within the list.

In this test, we see three clusters of behavior:

  1. TreeList which has superb performance.
  2. ArrayList, GrowthList and GuavaList which have good performance.
  3. LinkedList, FastList and NodeCachingLinkedList which have relatively poor performance.

In this benchmark we seem to find the catch in TreeList.  It has overhead relative to creation time and performs worse than it’s counterparts.

Last, but not least, we measure the memory overhead of the different list structures.  Guava List and Growth Lis really shine here.  They appear to have very similar behaviors in most respects.

TreeList has additional memory overhead as does FastList.

In this benchmark we start with a list of N items, then call clear() and measure it’s memory usage.  This is a good way to measure how efficient/aggressive the structure is with respect to cleaning up after itself.

As we expect, resizable array implementations leave some buffer stranded while linked list implementations clean up pretty effectively.

FastList does not seem to perform much cleanup after itself.

So Which List Is Right For You?

It depends.

Each implementation has tradeoffs, some of them quite severe and some of them really shine in certain situations.

If you are memory constrained, you need look no further than your java.util.ArrayList.  It has great memory performance.  GrowthList and GuavaList have comparable memory profiles.  GrowthList and Guava Lists are superior to ArrayList for the case where there are many interstitial insertions into the list taking place and all three are poor performers if you need to remove items from the head of the list.

If memory is not a problem and you expect to perform many insertions and deletions upon the list, then the obvious choice is the Apache Commons TreeList.

While having a slightly larger insertion at the tail overhead, it more than makes up for this in the area of random insertions and deletions.

Javalution FastList was anything but, faring significantly worse than the rest.  I suspect that it was once quite superior to the default JDK implementations, but has since been surpassed.

The Code

package com.javainc.collections;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import javolution.util.FastList;

import org.apache.commons.collections.list.GrowthList;
import org.apache.commons.collections.list.NodeCachingLinkedList;
import org.apache.commons.collections.list.TreeList;

import com.google.common.collect.Lists;
import com.javamex.classmexer.MemoryUtil;

public class TestList
{
  private static void testPerformance(String testName, int numItems,
      int numRandom, List list)
  {
    long startTime, endTime;
    int i;
    Random rand = new Random();
    int size = numItems;

    cleanup();

    // add:
    System.out.print(testName + "," + numItems + "," + numRandom + ",");
    startTime = System.currentTimeMillis();
    for (i = 0; i < numItems; i++)
    {
      list.add(i);
    }
    endTime = System.currentTimeMillis();
    System.out.print(endTime - startTime);

    // Get:
    startTime = System.currentTimeMillis();
    for (i = 0; i < numItems; i++)
    {
      list.get(i);
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // ITERATE
    startTime = System.currentTimeMillis();
    for (Object obj : list)
    {
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // RANDOM_INSERT
    size = list.size();
    startTime = System.currentTimeMillis();
    for (i = 0; i < numRandom; i++)
    {
      list.add(rand.nextInt(size++), i);
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // REMOVE_FROM_HEAD
    startTime = System.currentTimeMillis();
    Iterator it = list.iterator();
    for (i = 0; i < numRandom; i++)
    {
      list.remove(0);
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // Ensure the list is the original size.
    for (i = 0; i < numRandom; i++)
    {
      list.add(i);
    }
    endTime = System.currentTimeMillis();

    // REMOVE_FROM_TAIL
    size = list.size();
    startTime = System.currentTimeMillis();
    for (i = 0; i < numRandom; i++)
    {
      list.remove(size - 1);
      size--;
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // Ensure the list is the original size.
    for (i = 0; i < numRandom; i++)
    {
      list.add(i);
    }
    endTime = System.currentTimeMillis();

    // ADD TO END
    startTime = System.currentTimeMillis();
    for (i = 0; i < numRandom && it.hasNext(); i++)
    {
      list.add(i);
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    // REMOVE_RANDOM
    startTime = System.currentTimeMillis();
    for (i = 0; i < numRandom && it.hasNext(); i++)
    {
      list.remove(rand.nextInt(size));
      size--;
    }
    endTime = System.currentTimeMillis();
    System.out.print("," + (endTime - startTime));

    System.out.print("," + MemoryUtil.deepMemoryUsageOf(list));
    list.clear();
    System.out.println("," + MemoryUtil.deepMemoryUsageOf(list));
  }

  private static void cleanup()
  {
    // Clean up the machine.
    System.gc();
    try
    {
      Thread.sleep(2000);
    }
    catch(Exception ex)
    {
      ex.printStackTrace();
    }
  }

  public static void main(String args[])
  {
    System.out
        .println("CLASS,ITEMS,RANDOM,add,get,iterate,randomInsert,removeFromHead"
            + ",removeFromTail,addToTail,randomRemove,memory,clearMemory");

    int testSet[][] = { { 100, 100 }, { 1000, 1000 }, { 10000, 10000 },
        { 100000, 100000 }, { 200000, 200000 } };

    for (int i = 0; i < testSet.length; i++)
    {
      testPerformance("Java ArrayList", testSet[i][0], testSet[i][1],
          new ArrayList());
      testPerformance("Java LinkedList", testSet[i][0], testSet[i][1],
          new LinkedList());
      testPerformance("Apache GrowthList", testSet[i][0], testSet[i][1],
          new GrowthList());
      testPerformance("Apache TreeList", testSet[i][0], testSet[i][1],
          new TreeList());
      testPerformance("Apache NodeCachingLinkedList", testSet[i][0],
          testSet[i][1], new NodeCachingLinkedList());
      testPerformance("Javalution FastList", testSet[i][0], testSet[i][1],
          new FastList());
      testPerformance("Guava List", testSet[i][0], testSet[i][1],
          Lists.newArrayList());
    }
  }
}

Aside | Posted on by | Tagged , | Leave a comment

Dynamically reloading stylesheets

One of the most awesome features of JavaFX is it’s integration into CSS. I was skeptical at first, but I have long since drank the koolaid and started chanting with the rest.

Recently I was working on a stylesheet for a JavaFX GUI application I am developing.  I quickly became frustrated with the process of changing some CSS behavior, reloading the application, then recreating the circumstances which would trigger that new behavior.  The process was cumbersome and I quickly grew frustrated.  So I turned to my friend Google who revealed the following:

com.sun.javafx.css.StyleManager.getInstance().reloadStylesheets(scene);

This one-liner will reload the stylesheets associated with a particular scene. This gives us an awesome amount of power to configure with very little complexity.

I then hooked it up to a reflective event handler (described in an earlier post) like so:

// Declared in main scope of class for all within to have access.
private Scene       scene;

private void init(Stage stage)
{
  ...
  MenuItem reloadStylesheetsMenuItem = new MenuItem("Reload Stylesheets");
  reloadStylesheetsMenuItem.setOnAction(new ReflectiveActionEventHandler(this, "reloadStylesheets"));
  ...
}

...

public void reloadStylesheets(ActionEvent evt)
{
  com.sun.javafx.css.StyleManager.getInstance().reloadStylesheets(scene);
}

...
}

And I plan to hook this up to an F5 hotkey so that all I have to do in order to refresh my look and feel is change the relevant CCS file and hit F5.

That’s all there is to it!

Aside | Posted on by | Leave a comment

Principle of Invention

Back in the good old days…

I have been coding for 28 years.  Seeing this on writing is kind of depressing but I will forge on.  I first started coding on a Commodore 64, I had basically 2 choices:

  • Basic
  • Machine Language

Machine language had to be input via peeks and pokes, basic ran so slow that you couldn’t really achieve much outside the boundaries of it’s limited capabilities.  An hour of coding peeks and pokes might yield a blocky 8 bit images moving across the screen.  Early on I became very accustomed to the significant barriers between ideas and their realizations.  You probably have too.  The limitations that were so ubiquitous that you can’t even perceive them any longer.

Bret Victor gave a talk on Principle of Invention.  The greater scope of the talk is around defining the way you approach life and finding a guiding principle to guide your actions, development and design.

Bret’s principle is around the importance of ideas, their fragility and how small barriers can keep these ideas from achieving fruition.

Bret’s Principle

Creators need an immediate connection with their creation.  Until watching Bret’s talk, I had never perceived the barriers between my text editor and my creation.  After all, I do not have to enter punch cards, enter peeks or pokes, or wait for a printout from a submitted job.  I have a fairly advanced editor in the form of Eclipse and NetBeans and compilation is almost instantaneous compared to the years gone by.  I am closer to my creation, but not there yet.  There can be so much more…

Bret goes on to show an IDE which brings us yet closer to our creation.  Variables which can be physically manipulated.  Time exposed as a slider.  My jaw was literally dropping.  It blew me away and made me realize that everything he is doing should apply to the way we approach UI design.  My words can’t do it justice, you have to watch.

LiveCoding With D3

Gabriel Florit has also blown my mind.  Within HTML5 technologies and the D3 framework, he has brought some of Bret’s principles to life on the the form of an interactive D3 editor called LiveCoding.

You may wonder why something like D3 is doing on a JavaFX blog.  A number of reasons.  The ideas are more important than the underlying technologies and Bret’s ideas are compelling and Gabriel gives us something tangible to experiment with.  For that, I thank him.

Secondly, HTML5 technologies fill significant gaps in the JavaFX 2.x landscape, particularly in the space of available visualization frameworks.  D3 absolutely shines there.

Anyway…

I hope you find these things as thought provoking as I have!  Now back to my weekend…

Aside | Posted on by | Leave a comment

The JVM Language Wars

The great wizard stood before his brethren.  The hall filled with Neophyte, Acolyte, Journeyman and Master alike.  Pausing to clear his weary throat before he spoke the dreaded words.  “There is a coming war…  Java is dying, however the JVM will live on!”

Surprised murmurs and cries of heresy filled the room.  Great wizards engaged in passionate discussions about static typing versus dynamic typing, type inference, lamdas, closures and the merits of less ceremony.  I quietly listened, wondering how many new books I would need to buy to learn the new spells and what I was going to have for dinner that night, probably meatloaf again.

Sorry For The Melodrama

Of course, I was speaking of the famous blog post by James Strachan, one of the coauthors of Groovy, where he foretells of the increasing complexity of Java and it’s ultimate demise.  This was July 2009 and surprisingly, representatives from Python, Groovy, Scala, Clojure and Ruby camps participated in some intriguing discussion about the future direction of the JVM and its languages.

Generally speaking, there was a feeling that Java started simple, and has since absorbed much complexity.  The specification being a whopping 600+ pages.

Empirical Statistics

I’m not going to speak about uncertain futures, or pretend to understand the complexities involved in such language design decisions.  However, let’s take a concrete look at today’s trends via Google trends.  I compare the google search terms on the following:

  • C# Language
  • Java Language
  • Python Language
  • Scala Language
  • Ruby Language
  • Groovy Language

I include the “language” search term to differentiate between C# the musical note, Java the island or drink, python the snake, ruby the gem and groovy the adjective.

Java Popularity

Image

Java popularity appears to be decreasing slightly over time but seems to have stabilized.  Over the past 12 months it appears quite stable.

Image

C# Popularity

Image

C# popularity has decreased, but over the last year seems to be holding steady if not increasing somewhat.

Image

Clojure Language

Image

Clojure is on the rise.

Image

However, this rise seems to be coming to an end.

Python Language

Image

Python appears to be rising slightly.  However, this includes the non JVM spaces which are in direct competition with Ruby, Perl and shell scripting.

Image

Over the past 12 months, python interest appears to increase very slightly.

When we compare this to Jython we see a different story:

Jython

Image

Jython trend is decreasing.

Scala Language

Image

Scala is a tough one to read.  There are gaps which I can only attribute to some potential gaps in the Google dataset.  I do not believe that the world quit searching for Scala for a month.

Ruby Language

Image

The Ruby trend has diminished since 2005, but seems to have stabilized.

Like Python, JRuby is the key component running specifically on the JVM.

JRuby

Image

The JRuby trend shows a decline.

Groovy Language

Groovy did not even show up in the trends.  I am not sure why.  My feeling was that it would be more prevalent than Scala.  I consider this to be a gap rather than an actual finding.

Java Vs C#

Image

This is an interesting view which shows that the searches for “Java Language” to be 6 times more prevalent than searches for “C# Language” over the past 12 months.  C# vs Java shows 5:1 in favor of Java.

Java Vs JVM Languages

Image

Relative popularity based upon Google Trends shows that Java remains dominant.  In terms of Scala, 15.2 times more popularity than Scala.

Keep in mind that Google Trends reflects interest rather than investment.  On this scale, Clojure is not significant.  Python and Ruby have significant interest.  However, this again must have the caveat that JRuby and Jython are the competitive subsets.  It is difficult, if not impossible, to determine which searches on ruby and python were for JVM based deployments.

Conclusion

With emerging and increasingly capable alternatives, it is no surprise that Java interest has diminished over time.  However, the trend will likely stabilize as each new language finds its long term niche.  Emerging languages will likely continue to eat away at Java’s market share.  However, I’ve said it before and must say it again, this reflects interest not investment.

Developers are quick to learn new technologies, Enterprises are very slow to adopt new technologies.  However, these trends are important to identify and understand early.  In my opinion, Java must evolve.  This evolution will break some things in order to set some things right.  Breaking things will involve some immediate loss of market share, however, increase the longevity of the language itself.  I also think that implementation decisions around Lambdas and Closures will be key decisions foretelling of the future of the language itself.  I would rather see these capabilities delayed further than an implementation involving tradeoffs.

Posted in Uncategorized | Leave a comment

JavaFX Event Handling In Groovy

In a previous article I discussed the option of using Reflective Event Handlers in Java to simplify GUI code. When combining JavaFX and Groovy, this is not necessary thanks to Groovy’s support of lambas and closures. Similar facilities will likely become available when Java supports the same.

In the following code I create 3 distinct event handlers via 3 distinct mechanisms. Button one is handled by a handler assigned to a variable. This means that we can reuse this event handler for other things.

Button Two is handled by an anonymous function which simply calls out to an internal routine within the class.

Lastly, the key press handler is defined as anonymous function and is not reusable.

package com.javainc.test.reflectiveevents

import javafx.application.Application
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.stage.Stage

class GroovyEventHandling extends Application
{
  public void start(Stage stage)
  {
    stage.setTitle("Groovy Event Handling")
    Group root = new Group()
    Scene scene = new Scene(root, 300, 250)

    Button btn1 = new Button(text: "Button 1", layoutX:100, layoutY:100)
    Button btn2 = new Button(text: "Button 2", layoutX:100, layoutY:140)

    def handlerMethod = { println "Button 1 Pressed..." }

    btn1.setOnAction(handlerMethod as EventHandler)
    btn2.setOnAction({ buttonHandler(it) } as EventHandler)
    scene.setOnKeyPressed({ println "Key Pressed..." } as EventHandler)

    root.getChildren().addAll(btn1, btn2)
    stage.setScene(scene)
    stage.show()
  }

  def void buttonHandler(it)
  {
    println "Button 2 Pressed..."
  }

  public static void main(String[] args)
  {
    Application.launch(GroovyEventHandling.class, args)
  }
}

Posted in Groovy, JavaFX | Tagged , | Leave a comment

JavaFX and Groovy : Part 1

Here is a simple PieChart demonstration written in Groovy.

package com.javainc.javafx

import javafx.application.Application
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.chart.PieChart
import javafx.stage.Stage

class PieChartDemo extends Application
{
  void start(Stage stage)
  {
    def group = new Group()
    def scene = new Scene(group, 500, 400)
    def pie = new PieChart()
    pie.getData().addAll(new PieChart.Data("Cat1", 100), new PieChart.Data("Cat2", 200))
    group.getChildren().add(pie)
    stage.setScene(scene)
    stage.show()
  }

  static void main(args)
  {
    Application.launch(PieChartDemo.class, args)
  }
}

Posted in Uncategorized | 5 Comments

JavaFX and Scala : Part 1

Combining the power of JavaFX and Scala is surprisingly simple. Below is a simple Pie Chart demonstration.

package com.javainc.javafx

import javafx.application.Application
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.chart.PieChart
import javafx.stage.Stage

class PieChartDemo extends Application {

 override def start(stage: Stage) = {
 val group = new Group
 val scene = new Scene(group, 500, 400)
 val pie = new PieChart
 pie.getData.addAll(new PieChart.Data("Cat1", 100), new PieChart.Data("Cat2", 200))
 group.getChildren.add(pie)
 stage.setScene(scene)
 stage.show
 }
}

object PieChartDemo {
 def main(args: Array[String]) = {
 Application.launch(classOf[PieChartDemo])
 }
}

I am new to Scala and functional programming in general, so there is probably a more succinct way to do all of this.

Upon first blush, it feels like Scala and JavaFX together will bring some very powerful capabilities when married together.   I am intrigued and excited to explore how the concepts of Higher Order Functions, Closures, Traits, etc… could be leveraged to bring power and elegance to GUI design.

There was a lot of hype a couple years back when James Strauchan, the co-creator of the Groovy language stated his opinion that Java was stagnant and that Scala would be it’s ultimate replacement.  I do not agree, or rather, Oracle would really have to drop the ball for this to happen.

Java is in it’s prime.  There are great tools to support it.  Great tools written in it and great tools which make the high degree of scaffolding and other idiosyncracies tolerable.  I do not see Scala supplanting Java.  So far, it feels like a very sharp tool, too sharp for large development sweatshops with high attrition where the hundreds of programmers hammer out code with a median experience level of 2 years out of college.

In such arenas you need very mature products such as Java with a plethora of Code Analysis, Profilers, Debuggers and APM tools.  You need industry support and backers with deep pockets and deep industry trust.  SUN has been a great steward for Java.  When Oracle bought SUN, I felt great apprehension.  However, I think Oracle has continued in the same spirit that SUN started out with.

My point is that Scala has a long way to go.   I hope that the decisions which guide Oracle’s evolution of Java are not affected by the desire to “Keep up with the Jones’s”.    I hope that they do not rush to add new language features before they are ready.  I personally feel that Generics are a great feature, but that the implementation based upon type erasure was a bad idea.  I fear that the addition of closures and other advanced functionality will bring greater complexity, unexpected side-effects and drive up the complexity of the language.

Posted in JavaFX | Tagged , | 3 Comments