Home | FAQ | Contact me

Variable-length argument lists in Java

Per-thread data can be useful. Here's how to do it in the context of servlet code because servlet code requires a more precise approach.

Inside this code

In terms of beginning Java samples you have:

The primitive example

The simple example everyone shows is just a variable-length argument (or parameter) list consumed for printing something out. Here, we do consume String arguments to build a HashMap.

public static Map< String, String > mapTuples( String ... keys_and_values )
{
  int length = keys_and_values.length;

  if( length % 2 != 0 )
    throw new RuntimeException( "mapTuples() must be called with key/value pairs" );

  Map< String, String > map = new HashMap< String, String >( length / 2 );

  for( int i = 0; i < length; i += 2 )
  {
    String key   = keys_and_values[ i ];
    String value = keys_and_values[ i+1 ];

    map.put( key, value );
  }

  return map;
}

However, while good, this example doesn't help you get further down the road. Here's entering the problem via variable-length arguments, then dipping deeper into practical usage of the thing.

Let's imagine we wanted to create a real, flexible interface to a super logging utility. We can pass in tuples that will later appear in the log, but live as maps in the meantime. We're also going to overload our methods to show us logging stuff that's not just strings.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bson.types.ObjectId;

public class SuperLogger
{
    private Map< String, String > tuples = new HashMap< String, String >();

    ...

    public static void logTuple( String key, String value )
    {
        logTuples( key, value );
    }

    public static void logTuple( String key, ObjectId value )
    {
        logTuples( key, value.toString() );
    }

    public static void logTuples( String ... keys_and_values )
    {
        log( mapTuples( Arrays.asList( keys_and_values ) ) );
    }

    public static void logTuples( Object ... keys_and_values )
    {
        log( mapTuples( convertObjectsToStrings( Arrays.asList( keys_and_values ) ) ) );
    }


    private static List< String > convertObjectsToStrings( List< Object > objects )
    {
        List< String > strings = new ArrayList< String >( objects.size() );

        for( Object o : objects )
            strings.add( o.toString() );

        return strings;
    }

    private static Map< String, String > mapTuples( List< String > keys_and_values )
    {
        int length = keys_and_values.size();

        if( length % 2 != 0 )
            throw new RuntimeException( "mapTuples() must be called with key/value pairs" );

        Map< String, String > map = new HashMap< String, String >( length / 2 );

        for( int i = 0; i < length; i += 2 )
        {
            String key   = keys_and_values.get( i );
            String value = keys_and_values.get( i+1 );

            map.put( key, value );
        }

        return null;
    }

    private static void log( int which, Map< String, String > map )
    {
        if( this.tuples == null )
            this.tuples = map;
        else
            this.tuples.putAll( map );
    }

    ...

    public void main( String[] args )
    {
        SuperLogger logger = new SuperLogger();
        ObjectId    object = new ObjectId();

        logger.logTuple( "key1", "value1" );
        logger.logTuple( "object", object );
        logger.logTuples( "key2", "value2", "key3", "value3", "key4", "value4" );

        System.out.println( "Logged: " + logger.toString();
    }
}