For-each loop for Maps

Stephen Colebourne

Version 0.3

I. Problem

The Java language, since version 5, has supported an enhanced for-each loop for arrays and collections (iterables). This has proved to be a very popular feature, and greatly enhances the readability of many loops.

Unfortunately, the enhanced for-each loop does not have any direct support for maps. The result is that the code is not as readable as it might be.

This proposal is to extend the enhanced for-each loop facility to maps. For example, the new code to loop around a map would be:

  Map<String, Integer> map = new HashMap<String, Integer>();

 

  for (String str, Integer val : map) {

    System.out.println("Entry" + str + "=" + val);

  }

The current code for this is as follows:

  Map<String, Integer> map = new HashMap<String, Integer>();

 

  for (String str : map.keySet().iterator()) {

    Integer val = map.get(str);

    System.out.println("Entry" + str + "=" + val);

  }

Thus, this change can be seen to be a simple syntax sugar for the existing code.

II. For-each loop for Maps

This proposal extends the existing for-each loop to cover maps.

The simplest way to achieve this is with the following syntax [1]:

     EnhancedForMapStatement:

         for ( VariableModifiersopt Type Identifier,

               VariableModifiersopt Type Identifier : Expression) Statement

The Expression must either have type Map or a compile-time error occurs.

 The scope of the declared local variables is the contained Statement

 The enhanced for statement can be translated into a basic for statement as follows:

for (K #i = Expression.keySet().iterator(); #i.hasNext(); ) {

   VariableModifiersopt K k = #i.next();

   VariableModifiersopt V v = Expression.get(k);

   Statement

}

 Where #i is a generated identifier.

Tests should be performed to confirm whether key or entry iteration is fastest on typical map implementations, and the above adjusted as appropriate.

III. Further issues

The proposal above limits the expression to be a Map. This is not the best type that could be used here, as Map is a large and complex interface.

Ideally, implementation of this proposal should be accompanied by the addition of a MapIterable interface:

public interface MapIterable<K,V> {

  MapIterator<K,V> mapIterator();

}

where MapIterator is defined as:

public interface MapIterator<K,V> extends Iterator<K> {

  K getKey();

  V getValue();

  V setValue(V newValue);

}

The proposal would then be changed to allow both Map and MapIterable as the expression type.

All JavaSE map implementations would implement MapIterable, although the Map interface itself cannot be changed due to compatibility. However, since the vast majority of map implementations extend AbstractMap most non-JavaSE map implementations would automatically pickup the new interface.

A key advantage of adding a new interface like this is the ability of developers to add their own types which implement MapIterable but are not backed by a Map, such as direct access from IO. A further advantage is that the implementation of MapIterable can be tailored to be specific to each map type, some of which prefer key iteration and some of which prefer entry iteration.

Unfortunately, the above does not address the main problem with the new interface which is that a method that is passed an instance of Map has no immediate way to know whether the implementation supports MapIterable. These are some possible options:

a) Only support iteration over Map and do not add MapIterable.

b) Support iteration over Map and MapIterable but determine which to use at compile time, favouring MapIterable.

c) Support both Map and MapIterable with the choice made at runtime using a generated instanceof.

d) Only support MapIterable via unchecked cast, such that any Map that does not support MapIterable will throw a runtime ClassCastException.

Following traditional Java language principles, option (a) or (b) should be chosen. Option (c) seems excessive. Option (d) is not as risky as it seems, as the vast majority of maps will implement the new interface either directly, or by subclassing AbstractMap.

IV. Alternatives

The main alternate proposal is the control invocation syntax of BGGA [2] or FCM+JCA [3] closures. The advantage of this proposal is that it is very simple and self contained, being just a logical extension of the Java 5 changes, whereas closures are a much larger change. This proposal also avoids the need for importing static methods, or special casing continue/break using exceptions.

V. References

  1. The Java Language Specification, Third Editionhttp://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html
  2. G. Bracha, N. Gafter, J. Gosling, P. von der Ahé: "Closures for the Java Programming Language  (v0.5)"http://www.javac.info
  3. Java Control Abstraction - http://docs.google.com/Doc?docid=ddhp95vd_8f8zkn3