PROPOSAL: Enhanced for each loop iteration control (original) (raw)
Stephen Colebourne scolebourne at joda.org
Sat Mar 28 03:40:19 PDT 2009
- Previous message: PROPOSAL: Enhanced for each loop iteration control
- Next message: PROPOSAL: Enhanced for each loop iteration control
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Were there any comments on this? Stephen
Stephen Colebourne wrote:
Enhanced for each loop iteration control
(re-sent with correct subject line) This proposal extends the for each loop to allow access to meta data including the index and the remove method. (This proposal doesn't really go into enough detail - with JSR-310 my time here is limited. I'd hope that there is just about enough though....) ----------------------------------------------------------------------------------- Enhanced for each loop iteration control AUTHOR(S): Stephen Colebourne OVERVIEW FEATURE SUMMARY: Extends the Java 5 for-each loop to allow access to the loop index, whether this is the first or last iteration, and to remove the current item. MAJOR ADVANTAGE: The for-each loop is almost certainly the most new popular feature from Java 5. It works because it increases the abstraction level - instead of having to express the low-level details of how to loop around a list or array (with an index or iterator), the developer simply states that they want to loop and the language takes care of the rest. However, all the benefit is lost as soon as the developer needs to access the index or to remove an item. The original Java 5 for each work took a relatively conservative stance on a number of issues aiming to tackle the 80% case. However, loops are such a common form in coding that the remaining 20% that was not tackled represents a significant body of code. The process of converting the loop back from the for each to be index or iterator based is painful. This is because the old loop style if significantly lower-level, more verbose and less clear. It is also painful as most IDEs don't support this kind of 'de-refactoring'. MAJOR BENEFIT: A common coding idiom is expressed at a higher abstraction than at present. This aids readability and clarity. Accessing the index currently requires using an int based loop, or placing a separate int counter outside the loop (which then remains in scope after the loop). The proposed solution doesn't result in manual manipulation of the index. Accessing the iterator remove requires using an iterator based loop. With generics this is remarkably verbose. The proposed solution is significantly shorter and cleaner. MAJOR DISADVANTAGE: The enhanced for each loop is complicated with additional functionality. (This is mitigated by being easy and obvious to use) More code is generated magically by the compiler. (This is mitigated by a simple desugaring) ALTERNATIVES: Use the existing language constructs, typically the standard for loop. Use BGGA/JCA style closures, with control statements. It should be noted that these are consistently the most controversial parts of the closure debate, making the 'let's wait for closures' argument against this proposal weaker (as any final closures implementation may not include control statements).
EXAMPLES SIMPLE EXAMPLE: StringBuilder buf = new StringBuilder(); for (String str : list : it) { if (str == null) { it.remove(); } } whereas, today we write: StringBuilder buf = new StringBuilder(); for (Iterator it = list.iterator(); it.hasNext();) { String str = it.next(); if (str == null) { it.remove(); } } ADVANCED EXAMPLE: Example1: for (String str : list : it) { System.out.println("Row " + it.index() + " has the value " + str); } whereas, today we might write: int index = 0; for (String str : list) { System.out.println("Row " + index + " has the value " + str); index++; } or for (int i = 0; i < list.size(); i++) {_ _String str = list.get(i);_ _System.out.println("Row " + index + " has the value " + str);_ _}_ _Example 2:_ _StringBuilder buf = new StringBuilder();_ _for (String str : list : it) {_ _if (it.isFirst()) {_ _buf.append(str);_ _} else {_ _buf.append(", ").append(str);_ _}_ _}_ _StringBuilder buf = new StringBuilder();_ _for (String str : list : it) {_ _if (it.isLast()) {_ _buf.append(str);_ _} else {_ _buf.append(str).append(", ");_ _}_ _}_ _*DETAILS*_ _SPECIFICATION:_ _Lexical:_ _No new tokens are added. The colon token is reused in the extended_ _enhanced for each statement._ _Syntax:_ _EnhancedForStatement:_ _for ( VariableModifiersopt Type Identifier : Expression) Statement_ _for ( VariableModifiersopt Type Identifier : Expression : Ident)_ _Statement_ _Semantics:_ _The first enhanced for each statement (the current form) will compile as_ _it does today._ _The extended enhanced for each statement will operate as follows._ _The iterator control variable is a standard variable declared to be_ _final. It will never be null. The type is dependent on whether the_ _expression is an array or an Iterable. It will either be_ _ArrayIterationControl or IterableIterationControl. The type is not specified as it is redundent information, ie. the type is inferred. It is scoped for the life of the loop. public final class IterableIterationControlIterator { public IterableIterationControlIterator(Iterable iterable) { this.control = new IterableIterationControl(iterable.iterator()); } public boolean hasNext() { return control.hasNext() } public T next() { return control.next() } public IterableIterationControl control() { return control } } public final class IterableIterationControl { public IterableIterationControl(Iterator iteratorToWrap) { this.it = iteratorToWrap; } boolean hasNext() { return it.hasNext() } T next() { originalIndex++; if (lastWasRemoved) { lastWasRemoved = false } else { index++ } return it.next() } public T remove() { removed++; lastWasRemoved = true; return it.remove() } public int index() { return index } public int originalIndex() { return originalIndex } public boolean isFirst() { return index == 1 } public boolean isLast() { return it.hasNext() == false } } public final class ArrayIterationControlIterator { public ArrayIterationControlIterator(T[] array) { this.control = new ArrayIterationControl(array); } public boolean hasNext() { return control.hasNext() } public T next() { return control.next() } public ArrayIterationControl control() { return control } } public final class ArrayIterationControl { public ArrayIterationControl(T[] array) { this.array = array; } boolean hasNext() { return index < array.length; }_ _T next() { return array[++index]; }_ _public int index() { return index - 1; }_ _public boolean isFirst() { return index == 1; }_ _public boolean isLast() { return index == array.length; }_ _}_ _Exception analysis:_ _The method remove() on the iteration control variable can throw an_ _UnsuportedOperationException. However, this is no different from any_ _other method call._ _Definite assignment:_ _The new variable iteration control variable is a final variable that is_ _definitely assigned from creation._ _COMPILATION:_ _The extended enhanced for each loop is implemented by wrapping the two_ _control classes around the Iterable or the array._ _The Iterable design is desugared from:_ _for (T item : iterable : control) { ... }_ _to:_ _{_ _IterableIterationControlIterator $it = new IterableIterationControlIterator(iterable); IterableIterationControl control = $it.control(); while ($it.hasNext()) { T item = $it.next(); ... } } The array design is desugared similarly: { ArrayIterationControlIterator $it = new ArrayIterationControlIterator(iterable); ArrayIterationControl control = $it.control(); while ($it.hasNext()) { T item = $it.next(); ... } } There is the option to optimise this if the iteration control variable is not assigned to any other variable or passed to any other method. However, that is out of scope for now. TESTING: Testing will be similar to the enhanced for loop. Arrays and Iterables of various types and sizes will be used. The null expression will also be tested. LIBRARY SUPPORT: Yes, as detailed above. REFLECTIVE APIS: No. OTHER CHANGES: The javac tree API would need to be updated to model the change. MIGRATION: Migration is not required. However, an IDE refactoring could now convert more int and Iterator based for loops than it does at present. COMPATIBILITY BREAKING CHANGES: No breaking changes are known using this conversion scheme. EXISTING PROGRAMS: This conversion is pure syntax sugar, so there are no known interactions with existing programs. REFERENCES EXISTING BUGS: I searched the bug database, but nothing came up (which is surprising). URL FOR PROTOTYPE: None DOCUMENTS: [1] Stephen Colebourne's blog: http://www.jroller.com/scolebourne/entry/java7foreachloop [2] Stephen Colebourne's original writeup: http://docs.google.com/Edit?docID=dfn5297z15ck7x5ghr DESIGN ISSUES There are numerous alternative ways in which this feature can be added. These include: - using the keyword: for (String str : list) { for.remove(); // problem is nested for loops } - using a label as a psuedo-variable: it: for (String str : list) { it:remove(); // note colon and not dot } - using an additional clause before the for each colon: for (String str, int index : list) { // no access to remove, and conflicts for for each for maps } The chosen solution involves simple Java classes, and a simple desugar. The downside of the chosen solution is performance, as it involves creating two wrapping objects and routing hasNext() and next() via additional layers of method calls. A possible extension would be for the compiler to identify if the iteration control variable is not passed to another method. If so, then the code could be all be generated inline.
- Previous message: PROPOSAL: Enhanced for each loop iteration control
- Next message: PROPOSAL: Enhanced for each loop iteration control
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]