Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-5099045

(coll) Support "for (int i : Range.ascend(1, 10))" syntax

XMLWordPrintable

      Name: jl125535 Date: 09/08/2004


      A DESCRIPTION OF THE REQUEST :
      Now that the enhanced for loop is here, allow using it for a common integer looping case similar to many scripting languages. Add a class Range implementing Iterable which basically implements an integer range.

      Jython equivalent :

      for i in range( 1,, 10 )

      JUSTIFICATION :
      Ease of use, common use case
      (Incident Review ID: 302336)
      ======================================================================
      Suggested implementation provided by java.net member leouser:

      Refer to the attached file "619470.txt" for the full text.

      A DESCRIPTION OF THE FIX :
      BUG ID: 5099045 support "for( int i: Range(1,10))" syntax

      File affected: None, this is a new class
      JDK version: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin

      discussion(embedded in test docs):
      /**
       * BUG ID: 5099045, support "for( int i: Range(1,10))" syntax
       * for loops and ranges are common in programming languages. Java
       * provides support for the for loop and the enhanced for loop.
       * With the advent of the enhanced for loop in Java 5 it is now becoming
       * possible to do a good range loop. To enable Range it has been suggested
       * to create a special syntax. This I consider out of scope for this
       * code. This focuses on the implementation of a Range class,
       * which conceivably could be the target of such a special syntax.
       *
       * To be exact the Range class is a factory. It returns Iterables that in
       * turn return Iterators that traverse a range of Characters or Integers/Longs.
       * The Character iterator is produced by providing a CharSequence to one
       * of 6 factory methods. For example:
       * Range.traverse( "Java is cool" );
       * will return an Iterator that will traverse the String's characters.
       * Range.reverse( "Java is cool" );
       * will return an Iterator that will traverse the String's characters from 'l'
       * to "J".
       * For Integers this is expressed like so:
       * Range.ascend( 0 , 10000 );
       * this will produce Integers from 0 to 10000
       * Range.descend( 10000, 0 );
       * this will produce Integers from 1000 down to 0
       * In addition to these simple usages there are factory methods that take
       * an additional step parameter. This controls the increment/decrement amount.
       *
       * The CharSequence versions also take ranges and steps. So:
       * Range.traverse( "Java is Cool", 1, 5 );
       * will produce chars: a,v,a, ,i
       * Range.reverse( "Java is Cool", 5, 1 );
       * will produce chars: i, , a, v, a
       *
       * Boundaries in the parameters are inclusive. I find it much simpler to
       * remember Range.ascend( 1, 10000 ); actually stands for 1 - 10000.
       * If they end was exclusive, to get 1-1000 I would have to express it like so:
       * Range.ascend( 1, 1001 ); // isn't this odd?
       *
       * NOTE ON PACKAGING:
       * This is currently contained withing the java.lang package. I am wavering
       * between the util and lang as the proper home. Given that you can just
       * use Range when its in lang Ive put it there for now.
       *
       * IMPACT:
       * At the minimal a class will appear in a java package. This shouldn't collide
       * with any user classes out there since they are disallowed from adding classes
       * in packages. Though it is conceivable that their could be an ambiguous conflict
       * if it suddenly shows up in the java.lang package and the user recompiles. Also
       * from my perusal of javadoc there does not appear to be a Range class in existence.
       *
       * CANT WE ALREADY DO ALL THIS?
       * Well yes! But not in the same way. You certainly can do this:
       * for( char c: "Java".toCharArray() )
       * so in a certain sense that idiom is the equivilent of:
       * for( char c: Range.traverse( "Java" ))
       * the int range can be expressed like so:
       * for( int i = 0; i < 1000; i++ )
       * as opposed to:
       * for( int i: Range.ascend( 0, 1000 ) )
       * In my opinion Range starts to shine when you start doing more complex
       * things like stepping and reversing.
       * Give me numbers 1000 to 0 with an decrement of 10 please:
       * for( int i = 1000; i >= 0; i -= 10 )
       * This line of code isn't as easy to digest as:
       * for( int i: Range.descend( 1000, 0, 10 ) )
       * Almost all the thinking is reduced to simple method call. A new reader
       * of the call won't have to think about what individual component is
       * doing in the old for loop.
       * Let us try this:
       * String s = "Java";
       * for( int i = s.length() -1; i >= 0; i-- )
       * char c = s.charAt( i );
       * The Range equivilent is:
       * for(char c: Range.reverse( s ) )
       * less code, fewer components to think about. A smaller chance of error.
       * How can we revese String instances now in the absence of a String reverse
       * method?
       * StringBuilder sb = new StringBuilder();
       * for( char c: Range.reverse( "Java" ) ) sb.append( c );
       * instead of:
       * String s = "Java";
       * StringBuilder sb = new StringBuilder();
       * for( int i = s.length() -1; i >= 0; i-- )
       * sb.append( s.charAt( i ) );
       * Which do you prefer?
       *
       * WHY JUST Integers and CharSequences?
       * Well, because there the most obvious at this time. Ive considered doing
       * a long and even a random number range but their uses are less obvious to me.
       * long though could be good for some use cases. Ive targeted CharSequence with
       * this because Ive seen RFEs for just adding Iterator support to Strings and
       * such. This meets that request and goes beyond it. You can take any chunk
       * of CharSequence text and iterate on it now, easily. Even backwards. Even
       * in steps. Isn't life better for Strings suddenly?
       *
       * TESTING STRATEGY:
       * Well, since this class does not exist its hard to provide tests that prove
       * that something that was broken is no longer broken. This code is pretty much
       * an implementation of a RFE. Hence, the best we can shoot for is to exercise
       * the functionality and see if the expected results we get back are what we want.
       * Of course we test for things that should fail as well.
       *
       * java.lang.Range added to:
       * jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
       *
       * and also executed on a Suse Linux 7.3 distribution.
       *
       * Brian Harry
       * ###@###.###
       * JAN 4, 2006
       */

      code:
      package java.lang;

      import java.util.Iterator;
      import java.util.NoSuchElementException;

      /**
       * Range is a factory for Iterable/Iterator instances. The instances produced
       * by the methods iterate over Integer or Long ranges and will iterate over the char values
       * contained within a CharSequence. Integer methods are ascend and descend.
       * ascend increments, descend decrements from one int to another int. traverse
       * iterates forwards, reverse iterates backwards through the char values of a CharSequence.
       * All of these methods have variations that take a step. step controls the amount
       * by which the Range iterator will increase or decrease by. Many for loops whose
       * purpose is to produce int/long values or move over a CharSequence can be replaced by
       * using the Range iterators.
       * Some usage examples:
       * for(int i: Range.ascend(1, 100))
       * System.out.println(i);
       * //will print out numbers 1 through 100
       *
       * for(int i: Range.descend(100, 1))
       * System.out.println(i);
       * //will print out numbers 100 down to 1
       *
       * for(int i: Range.ascend(10, 100, 10))
       * System.out.println(i);
       * //will print out number 10 to 100 in increments of 10.
       *
       * for(long i: Range.ascend(-200L, 100L))
       * System.out.println(i);
       * //will print out numbers -200L to 100L
       *
       * for(char c: Range.traverse("Java is cool"))
       * System.out.println(c);
       * //will print out all the letters in the String "Java is cool".
       *
       * for( char c: Range.reverse("Java is cool"))
       * System.out.println(c);
       * //will print out all the letters, which would produce this String: "looc is avaJ"
       *
       * @author Brian Harry
       * @version 1.0, 1/5/2006
       *
       */
      public final class Range{
          
          private final static String errmessage1 = "Can't remove from a Range iterator";
          private final static String errmessage2 = "cs is null";
          enum Direction{ FORWARDS, BACKWARDS }

          private static abstract class DirectionalIterator<T> implements Iterator<T>{
      protected Direction direction = Direction.FORWARDS;
      public void reverse(){//this one method allows us to have 3 classes instead of 6.
      if(this.direction == Direction.FORWARDS)
      this.direction = Direction.BACKWARDS;
      else
      this.direction = Direction.FORWARDS;
              }

      public Direction direction(){
      return this.direction;
      }

          }
          
          //this class exists so you the iterators can be used in the foreach loop
          private static class Wrapper<T> implements Iterable<T>{

      DirectionalIterator<T> iterator;
      public Wrapper(DirectionalIterator<T> iterator){
      this.iterator = iterator;
      }

      public Iterator<T> iterator(){
      return (Iterator<T>)iterator;
      }

          }
          
          //iterators
          private static class IntegerIterator extends DirectionalIterator<Integer>{

      private int current;
      private int end;
      private int step;

      public IntegerIterator(int start, int end, int step){
      current = start;
      this.end = end;
      this.step = step;
      this.direction = Direction.FORWARDS;
      }

      public boolean hasNext(){

      if(this.direction == Direction.FORWARDS)
      return current <= end;
      else
      return current >= end;

      }

      public Integer next(){
      if(!hasNext())
      throw new NoSuchElementException("No more Integers");
      if(this.direction == Direction.FORWARDS){
      int rv = current;
      current += step;
      if(current < rv) current = end + 1;
      return rv;
      }
      else{
      int rv = current;
      current -= step;
      if(current > rv) current = end -1;
      return rv;
      }
      }

              public void remove(){
      throw new UnsupportedOperationException(errmessage1);
      }

          }

          private static class LongIterator extends DirectionalIterator<Long>{

      private long current;
      private long end;
      private long step;

      public LongIterator(long start, long end, long step){
      current = start;
      this.end = end;
      this.step = step;
      this.direction = Direction.FORWARDS;
      }

      public boolean hasNext(){
      if(this.direction == Direction.FORWARDS)
      return current <= end;
      else
      return current >= end;
      }

      public Long next(){
      if(!hasNext())
      throw new NoSuchElementException("No more Longs");
      if(this.direction == Direction.FORWARDS){
      long rv = current;
      current += step;
      if(current < rv) current = end + 1L;
      return rv;
      }
      else{
      long rv = current;
      current -= step;
      if(current > rv) current = end -1L;
      return rv;
      }
      }

              public void remove(){
      throw new UnsupportedOperationException(errmessage1);
      }

          }

          public static class CharIterator extends DirectionalIterator<Character>{

      private CharSequence cs;
      private int end;
      private int start;
      private int step;

      public CharIterator(CharSequence cs, int start, int end, int step){
      this.cs = cs.subSequence(start, end + 1);
      this.start = 0;
      this.end = this.cs.length() -1;
      this.step = step;
      this.direction = Direction.FORWARDS;
      }

      public boolean hasNext(){
      return start <= end;
      }

      public Character next(){
      if(!hasNext())
      throw new NoSuchElementException("No more Characters");
      if(this.direction == Direction.FORWARDS){
      char c = cs.charAt(start);
      int ostart = start;
      start += step;
      if(start < ostart) start = end + 1;
      return c;
      }
      else{
      char c = cs.charAt(end);
      int oend = end;
      end -= step;
      if(end > oend) end = start -1;
      return c;
      }
      }

      public void remove(){
      throw new UnsupportedOperationException(errmessage1);
      }

          }

          //factories

          /**
           * This method creates an Iterable/Iterator that will iterate through the int values
           * bounded by the start paramter to the end parameter, inclusive. So, for example:
           * Range.ascend(1, 1000);
           * the iterator will produce these int values: 1 .. 1000
           *
           * @param start an int that is the beginning number
           * @param end an int that is the final number
           * @return an Iterable that will produce the integer values
           * from start to end, inclusive.
           * @throws IllegalArgumentException
           * if start >= end.
           */
          public static Iterable<Integer> ascend(int start, int end){
      return ascend(start, end, 1);
          }

          /**
           * This method creates an Iterable/Iterator that will iterate through the int values
           * bounded by the start parameter to the end parameter, inclusive. The step parameter
           * indicates how much to increase by in each iteration. So, for example:
           * Range.ascend(1, 1000, 2);
           * the iterator will produce these int values: 1,3,5,7, ... , 999
           *
           * @param start an int that is the beginning number
           * @param end an int that is the final number
           * @param step an int that is the amount to increment
           * per iteration.
           * @return an Iterable that will produce the integer values
           * from start to end, inclusive.
           * @throws IllegalArgumentException
           * if start >= end or step <= 0.
           */
          public static Iterable<Integer> ascend(int start, int end, int step){
      if(start >= end)
      throw new IllegalArgumentException("start must be < end");
      else if(step <= 0)
      throw new IllegalArgumentException("step must be > 0");
      return new Wrapper<Integer>(new IntegerIterator(start, end, step));
          }

          /**
           * This method creates an Iterable/Iterator that will iterate through the int values
           * bounded by the start parameter to the end parameter, inclusive. So, for example:
           * Range.ascend(1L, 1000L);
           * the iterator will produce these int values: 1L .. 1000L
           *
           * @param start a long that is the beginning number
           * @param end a long that is the final number
           * @return an Iterable that will produce long values
           * from start to end, inclusive.
           * @throws IllegalArgumentException
           * if start >= end.
           */
          public static Iterable<Long> ascend(long start, long end){
      return ascend(start, end, 1L);
          }

          /**
           * This method creates an Iterable/Iterator that will iterate through the int values
           * bounded by the start parameter to the end parameter, inclusive. The step parameter
           * indicates how much to increase by in each iteration. So, for example:
           * Range.ascend(1L, 1000L, 2L);
           * the iterator will produce these long values: 1L,3L,5L,7L, ... , 999L
           *
           * @param start a long that is the beginning number
           * @param end a long that is the final number
           * @param step the amount to increment the increase per iteration.
           * @return an Iterable that will produce long values from start to end, inclusive
           * @throws IllegalArgumentException
           * if start >= end or step <= 0L.
           */
          public static Iterable<Long> ascend(long start, long end, long step){
      if(start >= end)
      throw new IllegalArgumentException("start must be < end");
      else if(step <= 0L)
      throw new IllegalArgumentException("step must be > 0L");
      return new Wrapper<Long>(new LongIterator(start, end, step));
          }
          
          /**
           * This method creates an Iterable/Iterator that will iterate through all
           * of the chars within the CharSequence passed into it. So, for example:
           * Range.traverse("Java is cool");
           * the iterator will produce these char values: J,a,v,a, ,i,s, ,c,o,o,l
           *
           * @param cs the CharSequence that will be traversed
           * @return an Iterable that will traverse the chars in the cs parameter.
           */
          public static Iterable<Character> traverse(CharSequence cs){
      return traverse(cs, 0, cs.length() - 1, 1);
          }

          /**
           * This method creates an Iterable/Iterator that will iterate through all
           * of the chars within the CharSequence passed into it. The chars iterated
           * through are the range defined by the start and end parameters. The end
           * parameter is inclusive, not exclusive. So, for example:
           * Range.traverse("Java is cool", 1, 10);
           * the iterator will produce these char values: a,v,a, ,i,s, ,c,o,o,l
           *
           * @param cs the CharSequence that will be traversed
           * @param start an int indicating what char to start from
           * @param end an int indicating what char should be the last
           * @return an Iterable that will traverse the chars in the cs parameter.
           * @throws IllegalArgumentException
           * if start >= end, start < 0,
           * end < 0, start > cs.length() -1
           * or end > cs.length() -1
           */
          public static Iterable<Character> traverse(CharSequence cs, int start, int end){
      return traverse(cs, start, end, 1);
          }
      [... incomplete ... will not fit here ...]

            martin Martin Buchholz
            jleesunw Jon Lee (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: