Monthly Archives: March 2014

Visualizing a three dimensional array, and its length

To visualize a three-dimensional array, and to comprehend its length, you can think of it in terms of literal physical dimensions (remember the book Flat Land?).

One object:

.

An array of objects:

.............

The length of this array (array.length) is how many elements, or dots, counting from left to right.

A two-dimensional array of objects:

.............
.............
.............
.............
.............
.............

Each element in the previous array now has its own array, going down. The length of the first element in this array (array[0].length, the top-left-most) is counting the dots top to bottom.

A three dimensional array is like a cube (this is all assuming uniform lengths among elements)

.............
............. .
............. . .
............. . . .
............. . . .
............. . . .
   . . . . . . . . .
    . . . . . . . ..
     . . . . . . . .

Each element in the previous array now has its own array (going deep "into" the screen). The length of the first element in this array (array[0][0].length, the top-left-most element) is the number of dots deep.

Not once have I (directly) used a three dimensional array.

Advertisements

Practical and performance differences between for vs. foreach in Java (and a generic timer-testing class to boot)

(This is also posted as an answer on stackoverflow. See the bottom of this post for the time-difference testing class.)

The foreach loop, added in Java 5 (also called the "enhanced for loop"), is equivalent to using a java.util.Iterator–it’s syntactic sugar for the same thing. Therefore, when reading each element, one by one and in order, a foreach should always be chosen over an iterator, as it is more convenient and concise.

foreach

for(int i : intList)  {
   System.out.println("An element in the list: " + i);
}

Iterator

Iterator<Integer> intItr = intList.iterator();
while(intItr.hasNext())  {
   System.out.println("An element in the list: " + intItr.next());
}

There are situations where you must use an Iterator directly. For example, attempting to delete an element while using a foreach can (will?) result in a ConcurrentModificationException.

foreach vs. for: Basic differences

The only practical difference between for and foreach is that, in the case of indexable objects, you do not have access to the index. An example when the basic for loop is required:

for(int i = 0; i < array.length; i++)  {
   if(i < 5)  {
      //Do something special
   }  else  {
      //Do other stuff
   }
}

Although you could manually create a separate index int-variable with foreach

int idx = -1;
for(int i : intArray)  {
   idx++;
   ...
}

it is not recommended, since variable-scope is not ideal, and the basic for loop is simply the standard and expected format for this use-case.

foreach vs. for: Performance

When accessing collections, a foreach is significantly faster than the basic for loop’s array access. When accessing arrays, however–at least with primitive and wrapper-arrays–access via indexes is dramatically faster.

Timing the difference between iterator and index access for primitive int-arrays

Indexes are 23-40 percent faster than iterators when accessing int or Integer arrays. Here is the output from the testing class at the bottom of this post, which sums the numbers in a 100-element primitive-int array (A is iterator, B is index):

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 358,597,622 nanoseconds
Test B: 269,167,681 nanoseconds
B faster by 89,429,941 nanoseconds (24.438799231635727% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 377,461,823 nanoseconds
Test B: 278,694,271 nanoseconds
B faster by 98,767,552 nanoseconds (25.666236154695838% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 288,953,495 nanoseconds
Test B: 207,050,523 nanoseconds
B faster by 81,902,972 nanoseconds (27.844689860906513% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,373,765 nanoseconds
Test B: 283,813,875 nanoseconds
B faster by 91,559,890 nanoseconds (23.891659337194227% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,790,818 nanoseconds
Test B: 220,770,915 nanoseconds
B faster by 155,019,903 nanoseconds (40.75164734599769% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 326,373,762 nanoseconds
Test B: 202,555,566 nanoseconds
B faster by 123,818,196 nanoseconds (37.437545972215744% faster)

I also ran this for an Integer array, and indexes are still the clear winner, but only between 18 and 25 percent faster.

For collections, iterators are faster than indexes

For a List of Integers, however, iterators are the clear winner. Just change the int-array in the test-class to

List<Integer> intList = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100});

and make the necssary changes to the test-function (int[] to List<Integer>, length to size(), etc)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,429,929,976 nanoseconds
Test B: 5,262,782,488 nanoseconds
A faster by 1,832,852,512 nanoseconds (34.326681820485675% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,907,391,427 nanoseconds
Test B: 3,957,718,459 nanoseconds
A faster by 1,050,327,032 nanoseconds (26.038700083921256% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,566,004,688 nanoseconds
Test B: 4,221,746,521 nanoseconds
A faster by 1,655,741,833 nanoseconds (38.71935684115413% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,770,945,276 nanoseconds
Test B: 3,829,077,158 nanoseconds
A faster by 1,058,131,882 nanoseconds (27.134122749113843% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,467,474,055 nanoseconds
Test B: 5,183,149,104 nanoseconds
A faster by 1,715,675,049 nanoseconds (32.60101667104192% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,439,983,933 nanoseconds
Test B: 3,509,530,312 nanoseconds
A faster by 69,546,379 nanoseconds (1.4816434912159906% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,451,101,466 nanoseconds
Test B: 5,057,979,210 nanoseconds
A faster by 1,606,877,744 nanoseconds (31.269164666060377% faster)

In one test they’re almost equivalent, but clearly, with collections, iterator wins.

This post is based on two answers I wrote on stackexchange:

Some more information: http://stackoverflow.com/questions/2113216/which-is-more-efficient-a-for-each-loop-or-an-iterator

The full testing class

I created this compare-the-time-it-takes-to-do-any-two-things class after reading this question on stackoverflow

   import  java.text.NumberFormat;
   import  java.util.Locale;
/**
   &lt;P&gt;{@code java TimeIteratorVsIndexIntArray 1000000}&lt;/P&gt;

   @see  &lt;CODE&gt;&lt;A HREF=&quot;http://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java&quot;&gt;http://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java&lt;/A&gt;&lt;/CODE&gt;
 **/
public class TimeIteratorVsIndexIntArray  {
   public static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
   public static final void main(String[] tryCount_inParamIdx0)  {
      int testCount;
      //Get try-count from command-line parameter
         try  {
            testCount = Integer.parseInt(tryCount_inParamIdx0[0]);
         }  catch(ArrayIndexOutOfBoundsException | NumberFormatException x)  {
            throw  new IllegalArgumentException("Missing or invalid command line parameter: The number of testCount for each test. " + x);
         }

      //Test proper...START
         int[] intArray = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100};

         long lStart = System.nanoTime();
            for(int i = 0; i < testCount; i++)  {
               testIterator(intArray);
            }
         long lADuration = outputGetNanoDuration("A", lStart);

         lStart = System.nanoTime();
            for(int i = 0; i < testCount; i++)  {
               testFor(intArray);
            }
         long lBDuration = outputGetNanoDuration("B", lStart);

         outputGetABTestNanoDifference(lADuration, lBDuration, "A", "B");
   }
      private static final void testIterator(int[] int_array)  {
         int total = 0;
         for(int i = 0; i < int_array.length; i++)  {
            total += int_array[i];
         }
      }
      private static final void testFor(int[] int_array)  {
         int total = 0;
         for(int i : int_array)  {
            total += i;
         }
      }
      //Test proper...END

//Timer testing utilities...START
   public static final long outputGetNanoDuration(String s_testName, long l_nanoStart)  {
      long lDuration = System.nanoTime() - l_nanoStart;
      System.out.println("Test " + s_testName + ": " + nf.format(lDuration) + " nanoseconds");
      return  lDuration;
   }

   public static final long outputGetABTestNanoDifference(long l_aDuration, long l_bDuration, String s_aTestName, String s_bTestName)  {
      long lDiff = -1;
      double dPct = -1.0;
      String sFaster = null;
      if(l_aDuration > l_bDuration)  {
         lDiff = l_aDuration - l_bDuration;
         dPct = 100.00 - (l_bDuration * 100.0 / l_aDuration + 0.5);
         sFaster = "B";
      }  else  {
         lDiff = l_bDuration - l_aDuration;
         dPct = 100.00 - (l_aDuration * 100.0 / l_bDuration + 0.5);
         sFaster = "A";
      }
      System.out.println(sFaster + " faster by " + nf.format(lDiff) + " nanoseconds (" + dPct + "% faster)");
      return  lDiff;
   }
//Timer testing utilities...END
}

Hangman in Java — Post education version

User Bohemian on stackoverflow responded to the Hangman code in my previous post with this comment:

Just FYI, you can do the whole thing in a just a few lines of code, and the main logic in two lines.

He wasn’t joking. Here’s his take:

import  java.util.Random;
import  java.util.Scanner;
/**
   &lt;P&gt;{@code java HangmanByBohemian}&lt;/P&gt;
 **/
public class HangmanByBohemian  {
   static int MAX_MISSES = 5;
   static String[] WORDS = { &quot;QUICK&quot;, &quot;BROWN&quot;, &quot;JUMPS&quot; }; // etc

   public static void main(String[] args) throws Exception {
      String word = WORDS[new Random().nextInt(WORDS.length)], guesses = &quot; &quot;;
      int misses = -1;
      for (Scanner in = new Scanner(System.in); 
            !word.matches(&quot;[&quot; + guesses + &quot;]+&quot;) &amp; (misses += word.contains(guesses.substring(0, 1)) ? 0 : 1) &lt;= MAX_MISSES; 
            guesses = in.nextLine().toUpperCase().charAt(0) + guesses)
         System.out.println(word.replaceAll(&quot;(?&lt;=.)&quot;, &quot; &quot;).replaceAll(&quot;[^&quot; + guesses + &quot;]&quot;, &quot;_&quot;));
      System.out.println(word + (misses &gt; MAX_MISSES ? &quot; not&quot; : &quot;&quot;) + &quot; solved with &quot; + misses + &quot; incorrect guesses&quot;);
   }
}

That’s 110 lines shorter. (Post continues below output.)

Success output (condensed a bit)

[C:\java_code\]java HangmanByBohemian
_ _ _ _ _   a
_ _ _ _ _   b
_ _ _ _ _   c
_ _ _ C _   d
_ _ _ C _   k
_ _ _ C K   q
Q _ _ C K   a
Q _ _ C K   u
Q U _ C K   i
Q U I C K solved with 4 incorrect guesses

Failure output

[C:\java_code\]java HangmanByBohemian
_ _ _ _ _   a
_ _ _ _ _   b
B _ _ _ _   c
B _ _ _ _   d
B _ _ _ _   e
B _ _ _ _   r
B R _ _ _   n
B R _ _ N   x
B R _ _ N   y
B R O W N not solved with 6 incorrect guesses

I’ve taken this dense-and-great answer, and made it my own. It’s not better, I did this only for the sake of learning it. While this doesn’t fulfill the specifications as originally asked, the specifications are very different than normal hangman (you have to explicitly specify indexes at which the guessed-letter applies to). This application is much closer to normal Hangman rules.

The major differences, as compared to Bohemian’s, are:


  • It’s a do-while, instead of a for loop.

  • It only keeps unique characters, and compares against a separate currCharGuessAsStr, instead of the first character in allGuessCharsStr.

  • It notifies the user of bad input (no characters) instead of crashing, and requires input to be a single letter.

  • It displays state at each step.

Pre loop

public class Hangman  {
   static int MAX_BAD_GUESSES = 10;
   static String[] WORDS = { &quot;QUICK&quot;, &quot;BROWN&quot;, &quot;JUMPS&quot; }; // etc

   public static void main(String[] args) throws Exception {
      String word = WORDS[new Random().nextInt(WORDS.length)];
      Scanner in = new Scanner(System.in);
      int badGuessCount = 0;
      String guessInput = null;

      //This is keyed by a one-character STRING instead of an
      //actual character. This is to avoid translating it back
      //and forth between string and char.
      Map&lt;String,Object&gt; unqGuessedCharsAsStrMap = new TreeMap&lt;String,Object&gt;();

      //Must be initialized to the empty string. Initializing to null
      //Will result in the literal string &quot;null&quot; being added to it.
      String allGuessCharsStr = &quot;&quot;;
      String currCharGuessAsStr = &quot; &quot;;

The loop

      do  {

         System.out.print(getMasked(word, allGuessCharsStr) + &quot; Guess a character [Bad guesses: &quot; + badGuessCount + &quot; of &quot; + MAX_BAD_GUESSES + &quot;]: &quot;);

         guessInput = in.nextLine();

         if(guessInput != null)  {  //null on first iteration only
            if(!guessInput.matches(&quot;^[a-zA-Z]$&quot;))  {
               System.out.println(&quot;Bad input. Must a single letter.&quot;);
               badGuessCount++;

            }  else  {
               //Definitely valid input, and exactly one character
               currCharGuessAsStr = guessInput.toUpperCase();

               if(unqGuessedCharsAsStrMap.containsKey(currCharGuessAsStr))  {
                  //Trim to eliminate initialization space
                  System.out.println(&quot;Already guessed that letter. All guesses: &quot; + allGuessCharsStr.trim());

               }  else  {
                  unqGuessedCharsAsStrMap.put(currCharGuessAsStr, null);

                  //Prepend just-guessed character and sort it.
                  allGuessCharsStr += currCharGuessAsStr;
                  char[] allGuessedChars = allGuessCharsStr.toCharArray();
                  Arrays.sort(allGuessedChars);
                  allGuessCharsStr = new String(allGuessedChars);
               }

               if(!word.contains(currCharGuessAsStr))  {
                  badGuessCount++;
               }
            }
         }

      }  while(!word.matches(&quot;[&quot; + allGuessCharsStr + &quot;]+&quot;)  &amp;&amp;
                  badGuessCount &lt;= MAX_BAD_GUESSES);

Post-loop, and getMasked(s,s) function

      System.out.println(getMasked(word, allGuessCharsStr));

      System.out.println(word + (badGuessCount &gt; MAX_BAD_GUESSES ? &quot; not&quot; : &quot;&quot;) + &quot; solved with &quot; + badGuessCount + &quot; incorrect guesses (max allowed=&quot; + MAX_BAD_GUESSES + &quot;).&quot;);
   }
   /**
      @param  all_guessesStrESIfNone  May not be null. If empty, no guesses have been made yet.
      @exception  PatternSyntaxException  If all_guessCharsStr is empty.
    **/
   private static final String getMasked(String secret_word, String all_guessesStrESIfNone)  {
      try  {
         if(all_guessesStrESIfNone.length() == 0)  {
            all_guessesStrESIfNone = &quot; &quot;;   //Any non-letter will suffice
         }
      }  catch(NullPointerException npx)  {
         throw  new NullPointerException(&quot;all_guessesStrESIfNone&quot;);
      }

      //Mask all not-yet-guessed characters with an underscore.
      secret_word = secret_word.replaceAll(&quot;[^&quot; + all_guessesStrESIfNone + &quot;]&quot;, &quot;_&quot;);

      //Insert a space between every character (trim eliminates the final).
      return  secret_word.replaceAll(&quot;(.)&quot;, &quot;$1 &quot;).trim();
   }
}

Solved output

[R:\jeffy\programming\sandbox\xbnjava]java Hangman
_ _ _ _ _ Guess a character [Bad guesses: 0 of 10]: a
_ _ _ _ _ Guess a character [Bad guesses: 1 of 10]: b
_ _ _ _ _ Guess a character [Bad guesses: 2 of 10]: c
_ _ _ _ _ Guess a character [Bad guesses: 3 of 10]: d
_ _ _ _ _ Guess a character [Bad guesses: 4 of 10]: e
_ _ _ _ _ Guess a character [Bad guesses: 5 of 10]: f
_ _ _ _ _ Guess a character [Bad guesses: 6 of 10]: j
J _ _ _ _ Guess a character [Bad guesses: 6 of 10]: q
J _ _ _ _ Guess a character [Bad guesses: 7 of 10]: m
J _ M _ _ Guess a character [Bad guesses: 7 of 10]: p
J _ M P _ Guess a character [Bad guesses: 7 of 10]: u
J U M P _ Guess a character [Bad guesses: 7 of 10]: s
J U M P S
JUMPS solved with 7 incorrect guesses (max allowed=10).

Unsolved output

[R:\jeffy\programming\sandbox\xbnjava]java Hangman
_ _ _ _ _ Guess a character [Bad guesses: 0 of 10]: a
_ _ _ _ _ Guess a character [Bad guesses: 1 of 10]: b
_ _ _ _ _ Guess a character [Bad guesses: 2 of 10]: c
_ _ _ C _ Guess a character [Bad guesses: 2 of 10]: d
_ _ _ C _ Guess a character [Bad guesses: 3 of 10]: e
_ _ _ C _ Guess a character [Bad guesses: 4 of 10]: f
_ _ _ C _ Guess a character [Bad guesses: 5 of 10]: j
_ _ _ C _ Guess a character [Bad guesses: 6 of 10]: e
Already guessed that letter. All guesses: ABCDEFJ
_ _ _ C _ Guess a character [Bad guesses: 7 of 10]:
Bad input. Must a single letter.
_ _ _ C _ Guess a character [Bad guesses: 8 of 10]: tt
Bad input. Must a single letter.
_ _ _ C _ Guess a character [Bad guesses: 9 of 10]: q
Q _ _ C _ Guess a character [Bad guesses: 9 of 10]: k
Q _ _ C K Guess a character [Bad guesses: 9 of 10]: l
Q _ _ C K Guess a character [Bad guesses: 10 of 10]: z
Q _ _ C K
QUICK not solved with 11 incorrect guesses (max allowed=10).

Full code

   import  java.util.Arrays;
   import  java.util.Map;
   import  java.util.TreeMap;
   import  java.util.Random;
   import  java.util.Scanner;
/**
   &lt;P&gt;{@code java Hangman}&lt;/P&gt;
 **/
public class Hangman  {
   static int MAX_BAD_GUESSES = 10;
   static String[] WORDS = { &quot;QUICK&quot;, &quot;BROWN&quot;, &quot;JUMPS&quot; }; // etc

   public static void main(String[] args) throws Exception {
      String word = WORDS[new Random().nextInt(WORDS.length)];
      Scanner in = new Scanner(System.in);
      int badGuessCount = 0;
      String guessInput = null;

      //This is keyed by a one-character STRING instead of an
      //actual character. This is to avoid translating it back
      //and forth between string and char.
      Map&lt;String,Object&gt; unqGuessedCharsAsStrMap = new TreeMap&lt;String,Object&gt;();

      //Must be initialized to the empty string. Initializing to null
      //Will result in the literal string &quot;null&quot; (those four characters,
      //sorted among all the guessed characters) being added to it.
      String allGuessCharsStr = &quot;&quot;;
      String currCharGuessAsStr = &quot; &quot;;

      do  {

         System.out.print(getMasked(word, allGuessCharsStr) + &quot; Guess a character [Bad guesses: &quot; + badGuessCount + &quot; of &quot; + MAX_BAD_GUESSES + &quot;]: &quot;);

         guessInput = in.nextLine();

         if(guessInput != null)  {  //null on first iteration only
            if(!guessInput.matches(&quot;^[a-zA-Z]$&quot;))  {
               System.out.println(&quot;Bad input. Must a single letter.&quot;);
               badGuessCount++;

            }  else  {
               //Definitely valid input, and exactly one character
               currCharGuessAsStr = guessInput.toUpperCase();

               if(unqGuessedCharsAsStrMap.containsKey(currCharGuessAsStr))  {
                  //Trim to eliminate initialization space
                  System.out.println(&quot;Already guessed that letter. All guesses: &quot; + allGuessCharsStr.trim());

               }  else  {
                  unqGuessedCharsAsStrMap.put(currCharGuessAsStr, null);

                  //Prepend just-guessed character and sort it.
                  allGuessCharsStr += currCharGuessAsStr;
                  char[] allGuessedChars = allGuessCharsStr.toCharArray();
                  Arrays.sort(allGuessedChars);
                  allGuessCharsStr = new String(allGuessedChars);
               }

               if(!word.contains(currCharGuessAsStr))  {
                  badGuessCount++;
               }
            }
         }

      }  while(!word.matches(&quot;[&quot; + allGuessCharsStr + &quot;]+&quot;)  &amp;&amp;
                  badGuessCount &lt;= MAX_BAD_GUESSES);

      System.out.println(getMasked(word, allGuessCharsStr));

      System.out.println(word + (badGuessCount &gt; MAX_BAD_GUESSES ? &quot; not&quot; : &quot;&quot;) + &quot; solved with &quot; + badGuessCount + &quot; incorrect guesses (max allowed=&quot; + MAX_BAD_GUESSES + &quot;).&quot;);
   }

   /**
      @param  all_guessesStrESIfNone  May not be null. If empty, no guesses have been made yet.
      @exception  PatternSyntaxException  If all_guessCharsStr is empty.
    **/
   private static final String getMasked(String secret_word, String all_guessesStrESIfNone)  {
      try  {
         if(all_guessesStrESIfNone.length() == 0)  {
            all_guessesStrESIfNone = &quot; &quot;;   //Any non-letter will suffice
         }
      }  catch(NullPointerException npx)  {
         throw  new NullPointerException(&quot;all_guessesStrESIfNone&quot;);
      }

      //Mask all not-yet-guessed characters with an underscore.
      secret_word = secret_word.replaceAll(&quot;[^&quot; + all_guessesStrESIfNone + &quot;]&quot;, &quot;_&quot;);

      //Insert a space between every character (trim eliminates the final).
      return  secret_word.replaceAll(&quot;(.)&quot;, &quot;$1 &quot;).trim();
   }
}

Implementing a text-based Hangman game in Java

This describes an implementation of a text-based Hangman game in Java, as asked in this question on stackoverflow.

I had a little too much fun with this.

Example output:

[C:\java_code\]java HangmanWord onomatopoeia
The secret word is: ------------. Guess (char idx idx idx ...): t 2 3 4
No characters guessed.
The secret word is: ------------. Guess (char idx idx idx ...): e 6 7 8 9
The secret word is: ---------e--. Guess (char idx idx idx ...): a 6 7 8
No characters guessed.
The secret word is: ---------e--. Guess (char idx idx idx ...): a 4
The secret word is: ----a----e--. Guess (char idx idx idx ...): o 0 3
The secret word is: o---a----e--. Guess (char idx idx idx ...): o 0 2 8 6
The secret word is: o-o-a-o-oe--. Guess (char idx idx idx ...): p 0 1 2 3 4 5 6 7 8 9 10 11
The secret word is: o-o-a-opoe--. Guess (char idx idx idx ...): n 1
The secret word is: ono-a-opoe--. Guess (char idx idx idx ...): t 4 5
The secret word is: ono-atopoe--. Guess (char idx idx idx ...): z 0
No characters guessed.
The secret word is: ono-atopoe--. Guess (char idx idx idx ...): m 3 4
The secret word is: onomatopoe--. Guess (char idx idx idx ...): i 10
The secret word is: onomatopoei-. Guess (char idx idx idx ...): a 11
SUCCESS: onomatopoeia

The main class:

public class HangmanWord  {
   private static final char MASK_CHAR = '-';
   private final String word;
   private final char[] maskedWordChars;
   private int totalMatched;
   public HangmanWord(String alphaNum_secretWord)  {
      int wordLen = -1;
      try  {
         wordLen = alphaNum_secretWord.length();
      }  catch(NullPointerException npx)  {
         throw  new NullPointerException("alphaNum_secretWord");
      }

      if(wordLen == 0)  {
         throw  new IllegalArgumentException("alphaNum_secretWord is non-null, but empty.");
      }

      //default is automatically false
      maskedWordChars = new char[wordLen];
      Arrays.fill(maskedWordChars, MASK_CHAR);
      totalMatched = 0;

      if(!Pattern.compile("^\\w+").matcher(alphaNum_secretWord).matches())  {
         throw  new IllegalArgumentException("alphaNum_secretWord (\"" + alphaNum_secretWord + "\") must contain only letters and digits.");
      }
      word = alphaNum_secretWord;
   }
   public int length()  {
      return  maskedWordChars.length;
   }
   public int getTotalMatchedCount()  {
      return  totalMatched;
   }
   public boolean didWin()  {
      return  (getTotalMatchedCount() == length());
   }

The main logic function in HangmanWord, which processes the guess, updates the letter-mask array, and returns the number of letters matched in this guess:

   public int getMatchedCountFromGuess(char guessChar, int... idxGuesses)  {
      if(idxGuesses.length == 0)  {
         throw  new IllegalArgumentException("idxGuesses.length is zero.");
      }
      int guessedCount = 0;
      for(int idx : idxGuesses)  {
         try  {
            if(maskedWordChars[idx] == MASK_CHAR  &&  word.charAt(idx) == guessChar)  {
               maskedWordChars[idx] = guessChar;
               guessedCount++;
            }
         }  catch(ArrayIndexOutOfBoundsException abx)  {
            throw  new IllegalArgumentException("Index " + idx + " is invalid, given length() is " + length(), abx);
         }
      }
      totalMatched += guessedCount;
      return  guessedCount;
   }
   public String getMasked()  {
      return  (new String(maskedWordChars));
   }

Demo use

The below main function takes advantage of the HangmanGuess class, which is below.

(Output is at the top of this post.)

Note that this does not accept user-input. Hence the below HangmanGuess class, in which I can store simulated input. There is an array of guess-objects below.

(Testing a significant program such as this with user input is a nightmare. I actually created the user-input code, but deleted by accident. I was going to put it in a separate function, so it could be optionally used, but got distracted before I pasted it back. It is so stinking cumbersome to enter all this data by hand every time you run it. I just don’t get why user-input is required so much in CS education/chosen by newbies, for use in anything but the most trivial programs.)

   public static final void main(String[] idx0_alphaNumSecretWord)  {
      HangmanWord hword = null;
      try  {
         hword = new HangmanWord(idx0_alphaNumSecretWord[0]);
      }  catch(ArrayIndexOutOfBoundsException abx)  {
         throw  new IllegalArgumentException("Missing one required parameter: The alpha-numeric secret word.");
      }

      //TESTING ONLY:
         //#=matches in guess
         HangmanGuess[] testGuesses = new HangmanGuess[]{ //012345678901
            new HangmanGuess('t', 2, 3, 4),               //onomatopoeia
            new HangmanGuess('e', 6, 7, 8, 9), //1
            new HangmanGuess('a', 6, 7, 8),
            new HangmanGuess('a', 4),          //1
            new HangmanGuess('o', 0, 3),       //1
            new HangmanGuess('o', 0, 2, 8 ,6), //3
            new HangmanGuess('p', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),  //1
            new HangmanGuess('n', 1),          //1
            new HangmanGuess('t', 4, 5),       //1
            new HangmanGuess('z', 0),
            new HangmanGuess('m', 3, 4),       //1
            new HangmanGuess('i', 10),         //1
            new HangmanGuess('a', 11)          //1
         };
         int testIdx = 0;

Main usage-logic:

      while(!hword.didWin())  {
         System.out.print("The secret word is: " + hword.getMasked() + ". Guess (char idx idx idx ...): ");

         //LIVE only:  (must create!)
         //  HangmanGuess hguess = getGuessFromUserInput();
         //Testing only...START
            HangmanGuess hguess = testGuesses[testIdx++];
            System.out.print(hguess);
         //Testing only...END

         int matchesThisGuess = hword.getMatchedCountFromGuess(hguess.chr, hguess.idxGuesses);

         System.out.println();

         if(matchesThisGuess == 0)  {
            System.out.println("No characters guessed.");
         }
      }
      System.out.println("SUCCESS: " + hword.getMasked());

   }
}

The guess test-class

class HangmanGuess  {
   public final char chr;
   public final int[] idxGuesses;
   public HangmanGuess(char chr, int... idxGuesses)  {
      this.chr = chr;
      this.idxGuesses = idxGuesses;
   }
   public String toString()  {
      StringBuilder bldr = (new StringBuilder()).append(chr).append(" ");
      for(int i : idxGuesses)  {
         bldr.append(i + " ");
      }
      return  bldr.toString();
   }
}

Full source-code

   import  java.util.Scanner;
   import  java.util.regex.Matcher;
   import  java.util.regex.Pattern;
   import  java.util.Arrays;
/**
   <P>{@code java HangmanWord onomatopoeia}</P>
 **/
public class HangmanWord  {
   private static final char MASK_CHAR = '-';
   private final String word;
   private final char[] maskedWordChars;
   private int totalMatched;
   public HangmanWord(String alphaNum_secretWord)  {
      int wordLen = -1;
      try  {
         wordLen = alphaNum_secretWord.length();
      }  catch(NullPointerException npx)  {
         throw  new NullPointerException("alphaNum_secretWord");
      }

      if(wordLen == 0)  {
         throw  new IllegalArgumentException("alphaNum_secretWord is non-null, but empty.");
      }

      //default is automatically false
      maskedWordChars = new char[wordLen];
      Arrays.fill(maskedWordChars, MASK_CHAR);
      totalMatched = 0;

      if(!Pattern.compile("^\\w+").matcher(alphaNum_secretWord).matches())  {
         throw  new IllegalArgumentException("alphaNum_secretWord (\"" + alphaNum_secretWord + "\") must contain only letters and digits.");
      }
      word = alphaNum_secretWord;
   }
   public int length()  {
      return  maskedWordChars.length;
   }
   public int getTotalMatchedCount()  {
      return  totalMatched;
   }
   public boolean didWin()  {
      return  (getTotalMatchedCount() == length());
   }
   public int getMatchedCountFromGuess(char guessChar, int... idxGuesses)  {
      if(idxGuesses.length == 0)  {
         throw  new IllegalArgumentException("idxGuesses.length is zero.");
      }
      int guessedCount = 0;
      for(int idx : idxGuesses)  {
         try  {
            if(maskedWordChars[idx] == MASK_CHAR  &&  word.charAt(idx) == guessChar)  {
               maskedWordChars[idx] = guessChar;
               guessedCount++;
            }
         }  catch(ArrayIndexOutOfBoundsException abx)  {
            throw  new IllegalArgumentException("Index " + idx + " is invalid, given length() is " + length(), abx);
         }
      }
      totalMatched += guessedCount;
      return  guessedCount;
   }
   public String getMasked()  {
      return  (new String(maskedWordChars));
   }

   public static final void main(String[] idx0_alphaNumSecretWord)  {
      HangmanWord hword = null;
      try  {
         hword = new HangmanWord(idx0_alphaNumSecretWord[0]);
      }  catch(ArrayIndexOutOfBoundsException abx)  {
         throw  new IllegalArgumentException("Missing one required parameter: The alpha-numeric secret word.");
      }

      //TESTING only
         //#=matches in guess
         HangmanGuess[] testGuesses = new HangmanGuess[]{ //012345678901
            new HangmanGuess('t', 2, 3, 4),               //onomatopoeia
            new HangmanGuess('e', 6, 7, 8, 9), //1
            new HangmanGuess('a', 6, 7, 8),
            new HangmanGuess('a', 4),          //1
            new HangmanGuess('o', 0, 3),       //1
            new HangmanGuess('o', 0, 2, 8 ,6), //3
            new HangmanGuess('p', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),  //1
            new HangmanGuess('n', 1),          //1
            new HangmanGuess('t', 4, 5),       //1
            new HangmanGuess('z', 0),
            new HangmanGuess('m', 3, 4),       //1
            new HangmanGuess('i', 10),         //1
            new HangmanGuess('a', 11)          //1
         };
         int testIdx = 0;

      while(!hword.didWin())  {
         System.out.print("The secret word is: " + hword.getMasked() + ". Guess (char idx idx idx ...): ");

         //LIVE only:  (must create!)
         //  HangmanGuess hguess = getGuessFromUserInput();
         //Testing only...START
            HangmanGuess hguess = testGuesses[testIdx++];
            System.out.print(hguess);
         //Testing only...END

         int matchesThisGuess = hword.getMatchedCountFromGuess(hguess.chr, hguess.idxGuesses);

         System.out.println();

         if(matchesThisGuess == 0)  {
            System.out.println("No characters guessed.");
         }
      }
      System.out.println("SUCCESS: " + hword.getMasked());

   }
}
class HangmanGuess  {
   public final char chr;
   public final int[] idxGuesses;
   public HangmanGuess(char chr, int... idxGuesses)  {
      this.chr = chr;
      this.idxGuesses = idxGuesses;
   }
   public String toString()  {
      StringBuilder bldr = (new StringBuilder()).append(chr).append(" ");
      for(int i : idxGuesses)  {
         bldr.append(i + " ");
      }
      return  bldr.toString();
   }
}

GridPosition: Freely traverse the elements in a two-dimensional array by cardinal direction: UP, DOWN, LEFT, RIGHT

This question on stackoverflow is about a two-dimensional array of ints, which is a 1/0 maze. The zeros are the “path”, and the 1s are the “wall”. For example:

1 0 1 1 1
1 0 0 0 1
1 0 1 0 1
1 1 1 0 1
1 1 1 0 1

The goal is to traverse the maze from start to finish, via a recursive function. I came up with the following classes to eliminate much of this problem’s complexity (the poster actually submitted four questions on this one problem within five hours), although I don’t believe it’s useful to the poster, since I think it’s an assignment that would forbid its use.

There are two classes, and an Enum.

Enum: Direction

First, the enum, which defines the direction you want to move in the grid and determines the new indexes (one-at-a-time) based on its movement.

enum Direction {
   UP(-1, 0),
   DOWN(1, 0),
   LEFT(0, -1),
   RIGHT(0, 1);

   private final int rowSteps;
   private final int colSteps;
   private Direction(int rowSteps, int colSteps)  {
      this.rowSteps = rowSteps;
      this.colSteps = colSteps;
   }
   public int getNewRowIdx(int currentRowIdx)  {
      return  (currentRowIdx + getRowSteps());
   }
   public int getNewColIdx(int currentColIdx)  {
      return  (currentColIdx + getColSteps());
   }
   public int getRowSteps()  {
      return  rowSteps;
   }
   public int getColSteps()  {
      return  colSteps;
   }
};

Class: MazePosition

The main class is called MazePosition (below). First you set the maze-grid double-array into it, via its int[][] constructor, and store that instance statically:

private static final MazePosition MAZE_HOLDER = new MazePosition(MAZE_GRID);

(This step could be designed better, but it works.)

After setting the maze-grid (which is a one-time-only thing, per-execution), then the x/y constructor is used to declare an initial position:

MazePosition pos = new MazePosition(0, 0);

And after that, just move as necessary:

pos = pos.getNeighbor(Direction.RIGHT).
   getNeighbor(Direction.RIGHT).
   getNeighbor(Direction.DOWN);

The value of each position is retrieved by pos.getValue() or pos.isPath() —- 1 is the “wall” and 0 is the “path”. (As an aside: The huge 2d-array should really contain one-bit booleans, instead of 4-byte ints, but *looking* at the array’s code makes sense with ints, and doesn’t with booleans… Note that it should at least be changed to bytes.)

So regarding movement, if you attempt to get a neighbor when there is none, such as moving left at the left edge, an IllegalStateException is thrown. Use the is*Edge() functions to avoid this.

The MazePosition class also has a convenient debugging function called getNineByNine(), which returns a 9×9 grid of the array values (as a string), where the middle item is the current position.

   import  java.util.Arrays;
   import  java.util.Objects;
class MazePosition  {
//state
   private static int[][] MAZE_GRID;
   private final int rowIdx;
   private final int colIdx;
//internal
   private final int rowIdxMinus1;
   private final int colIdxMinus1;
   public MazePosition(int[][] MAZE_GRID)  {
      if(this.MAZE_GRID != null)  {
         throw  new IllegalStateException("Maze double-array already set. Use x/y constructor.");
      }
      MazePosition.MAZE_GRID = MAZE_GRID;

      //TODO: Crash if null or empty, or sub-arrays null or empty, or unequal lengths, or contain anything but 0 or -1.

      rowIdx = -1;
      colIdx = -1;
      rowIdxMinus1 = -1;
      colIdxMinus1 = -1;
   }
   public MazePosition(int rowIdx, int colIdx)  {
      if(MazePosition.MAZE_GRID == null)  {
         throw  new IllegalStateException("Must set maze double-array with: new MazePosition(int[][]).");
      }

      if(rowIdx < 0  ||  rowIdx >= MazePosition.getRowCount())  {
         throw  new IllegalArgumentException("rowIdx (" + rowIdx + ") is invalid.");
      }
      if(colIdx < 0  ||  colIdx >= MazePosition.getColumnCount())  {
         throw  new IllegalArgumentException("colIdx (" + colIdx + ") is invalid.");
      }

      this.rowIdx = rowIdx;
      this.colIdx = colIdx;
      rowIdxMinus1 = (rowIdx - 1);
      colIdxMinus1 = (colIdx - 1);
   }

   public boolean isPath()  {
      return  (getValue() == 0);  //1???
   }
   public int getValue()  {
      return  MazePosition.MAZE_GRID[getRowIdx()][getColumnIdx()];
   }
   public int getRowIdx()  {
      return  rowIdx;
   }
   public int getColumnIdx()  {
      return  colIdx;
   }
   public MazePosition getNeighbor(Direction dir)  {
      Objects.requireNonNull(dir, "dir");
      return  (new MazePosition(
         dir.getNewRowIdx(getRowIdx()),
         dir.getNewColIdx(getColumnIdx())));
   }
   public MazePosition getNeighborNullIfEdge(Direction dir)  {
      if(isEdgeForDirection(dir))  {
         return  null;
      }
      return  getNeighbor(dir);
   }
   public int getNeighborValueNeg1IfEdge(Direction dir)  {
      MazePosition pos = getNeighborNullIfEdge(dir);
      return  ((pos == null) ? -1 : pos.getValue());
   }
   public static final int getRowCount()  {
      return  MAZE_GRID.length;
   }
   public static final int getColumnCount()  {
      return  MAZE_GRID[0].length;
   }
   public boolean isEdgeForDirection(Direction dir)  {
      Objects.requireNonNull(dir);
      switch(dir)  {
         case UP:    return isTopEdge();
         case DOWN:  return isBottomEdge();
         case LEFT:  return isLeftEdge();
         case RIGHT: return isRightEdge();
      }
      throw  new IllegalStateException(toString() + ", dir=" + dir);
   }
   public boolean isLeftEdge()  {
      return  (getColumnIdx() == 0);
   }
   public boolean isTopEdge()  {
      return  (getRowIdx() == 0);
   }
   public boolean isBottomEdge()  {
      return  (getRowIdx() == rowIdxMinus1);
   }
   public boolean isRightEdge()  {
      return  (getColumnIdx() == colIdxMinus1);
   }
   public String toString()  {
      return  "[" + getRowIdx() + "," + getColumnIdx() + "]=" + getValue();
   }
   public String getNineByNine()  {
      int[][] nineByNine = new int[3][3];

      //Middle row
         nineByNine[1][1] = getValue();
         nineByNine[1][0] = getNeighborValueNeg1IfEdge(Direction.LEFT);
         nineByNine[1][2] = getNeighborValueNeg1IfEdge(Direction.RIGHT);

      //Top
         MazePosition posUp = getNeighborNullIfEdge(Direction.UP);
         if(posUp != null)  {
            nineByNine[0][0] = posUp.getNeighborValueNeg1IfEdge(Direction.LEFT);
            nineByNine[0][1] = posUp.getValue();
            nineByNine[0][2] = posUp.getNeighborValueNeg1IfEdge(Direction.RIGHT);
         }

      //Bottom
         MazePosition posDown = getNeighborNullIfEdge(Direction.DOWN);
         if(posDown != null)  {
            nineByNine[2][0] = posDown.getNeighborValueNeg1IfEdge(Direction.LEFT);
            nineByNine[2][1] = posDown.getValue();
            nineByNine[2][2] = posDown.getNeighborValueNeg1IfEdge(Direction.RIGHT);
         }

      String sLS = System.getProperty("line.separator", "\r\n");
      return  "Middle position in 9x9 grid is *this*: " + toString() + sLS +
         Arrays.toString(nineByNine[0]) + sLS +
         Arrays.toString(nineByNine[1]) + sLS +
         Arrays.toString(nineByNine[2]);
   }
}

What this does not have, is “collision detection” or anything that actually figures out the maze-path for you. It just moves throughout the grid, regardless if it’s moving through walls or not. There could easily be some getNeighborIfNotWall(Direction) and isWallToLeft() functions added, but I’ll leave that to you. ;)

It would not take too much work to make this useable with any two-dimensional array, although I’d probably add diagonal directions, such as UP_LEFT, and the ability to move multiple steps, such as getNeighbor(3, Direction.DOWN).

Demo usage

public class MazePosDemo  {
   private static final int[][] MAZE_GRID = new int[][] {

   //mega maze grid goes here...

   };

   private static final MazePosition MAZE_HOLDER = new MazePosition(MAZE_GRID);

   public static final void main(String[] ignored)  {
      MazePosition pos = new MazePosition(0, 0);
      System.out.println("start: " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.LEFT);
      System.out.println("left:  " + pos);

      pos = pos.getNeighbor(Direction.UP);
      System.out.println("up:    " + pos);

      pos = pos.getNeighbor(Direction.UP);
      System.out.println("up:    " + pos);

      System.out.println(pos.getNineByNine());
   }
}

Output:

[C:\java_code\]java MazePosDemo
start: [0,0]=1
right: [0,1]=1
right: [0,2]=1
down: [1,2]=1
down: [2,2]=1
right: [2,3]=1
down: [3,3]=0
left: [3,2]=1
up: [2,2]=1
up: [1,2]=1
Middle position in 9×9 grid is *this*: [1,2]=1
[1, 1, 1]
[0, 1, 0]
[0, 1, 1]

Full source code

And here’s the entire source-code file, containing all of the above (including the mega-maze-array):

   //Needed only by MazePosition
   import  java.util.Arrays;
   import  java.util.Objects;

enum Direction {
   UP(-1, 0),
   DOWN(1, 0),
   LEFT(0, -1),
   RIGHT(0, 1);
//config
   private final int rowSteps;
   private final int colSteps;
   private Direction(int rowSteps, int colSteps)  {
      this.rowSteps = rowSteps;
      this.colSteps = colSteps;
   }
   public int getNewRowIdx(int currentRowIdx)  {
      return  (currentRowIdx + getRowSteps());
   }
   public int getNewColIdx(int currentColIdx)  {
      return  (currentColIdx + getColSteps());
   }
   public int getRowSteps()  {
      return  rowSteps;
   }
   public int getColSteps()  {
      return  colSteps;
   }
};

class MazePosition  {
//config
   private static int[][] MAZE_GRID;
   private final int rowIdx;
   private final int colIdx;
//internal
   private final int rowIdxMinus1;
   private final int colIdxMinus1;
   public MazePosition(int[][] MAZE_GRID)  {
      if(this.MAZE_GRID != null)  {
         throw  new IllegalStateException("Maze double-array already set. Use x/y constructor.");
      }
      MazePosition.MAZE_GRID = MAZE_GRID;

      //TODO: Crash if null or empty, or sub-arrays null or empty, or unequal lengths, or contain anything but 0 or -1.

      rowIdx = -1;
      colIdx = -1;
      rowIdxMinus1 = -1;
      colIdxMinus1 = -1;
   }
   public MazePosition(int rowIdx, int colIdx)  {
      if(MazePosition.MAZE_GRID == null)  {
         throw  new IllegalStateException("Must set maze double-array with: new MazePosition(int[][]).");
      }

      if(rowIdx < 0  ||  rowIdx >= MazePosition.getRowCount())  {
         throw  new IllegalArgumentException("rowIdx (" + rowIdx + ") is invalid.");
      }
      if(colIdx < 0  ||  colIdx >= MazePosition.getColumnCount())  {
         throw  new IllegalArgumentException("colIdx (" + colIdx + ") is invalid.");
      }

      this.rowIdx = rowIdx;
      this.colIdx = colIdx;
      rowIdxMinus1 = (rowIdx - 1);
      colIdxMinus1 = (colIdx - 1);
   }

   public boolean isPath()  {
      return  (getValue() == 0);  //1???
   }
   public int getValue()  {
      return  MazePosition.MAZE_GRID[getRowIdx()][getColumnIdx()];
   }
   public int getRowIdx()  {
      return  rowIdx;
   }
   public int getColumnIdx()  {
      return  colIdx;
   }
   public MazePosition getNeighbor(Direction dir)  {
      Objects.requireNonNull(dir, "dir");
      return  (new MazePosition(
         dir.getNewRowIdx(getRowIdx()),
         dir.getNewColIdx(getColumnIdx())));
   }
   public MazePosition getNeighborNullIfEdge(Direction dir)  {
      if(isEdgeForDirection(dir))  {
         return  null;
      }
      return  getNeighbor(dir);
   }
   public int getNeighborValueNeg1IfEdge(Direction dir)  {
      MazePosition pos = getNeighborNullIfEdge(dir);
      return  ((pos == null) ? -1 : pos.getValue());
   }
   public static final int getRowCount()  {
      return  MAZE_GRID.length;
   }
   public static final int getColumnCount()  {
      return  MAZE_GRID[0].length;
   }
   public boolean isEdgeForDirection(Direction dir)  {
      Objects.requireNonNull(dir);
      switch(dir)  {
         case UP:    return isTopEdge();
         case DOWN:  return isBottomEdge();
         case LEFT:  return isLeftEdge();
         case RIGHT: return isRightEdge();
      }
      throw  new IllegalStateException(toString() + ", dir=" + dir);
   }
   public boolean isLeftEdge()  {
      return  (getColumnIdx() == 0);
   }
   public boolean isTopEdge()  {
      return  (getRowIdx() == 0);
   }
   public boolean isBottomEdge()  {
      return  (getRowIdx() == rowIdxMinus1);
   }
   public boolean isRightEdge()  {
      return  (getColumnIdx() == colIdxMinus1);
   }
   public String toString()  {
      return  "[" + getRowIdx() + "," + getColumnIdx() + "]=" + getValue();
   }
   public String getNineByNine()  {
      int[][] nineByNine = new int[3][3];

      //Middle row
         nineByNine[1][1] = getValue();
         nineByNine[1][0] = getNeighborValueNeg1IfEdge(Direction.LEFT);
         nineByNine[1][2] = getNeighborValueNeg1IfEdge(Direction.RIGHT);

      //Top
         MazePosition posUp = getNeighborNullIfEdge(Direction.UP);
         if(posUp != null)  {
            nineByNine[0][0] = posUp.getNeighborValueNeg1IfEdge(Direction.LEFT);
            nineByNine[0][1] = posUp.getValue();
            nineByNine[0][2] = posUp.getNeighborValueNeg1IfEdge(Direction.RIGHT);
         }

      //Bottom
         MazePosition posDown = getNeighborNullIfEdge(Direction.DOWN);
         if(posDown != null)  {
            nineByNine[2][0] = posDown.getNeighborValueNeg1IfEdge(Direction.LEFT);
            nineByNine[2][1] = posDown.getValue();
            nineByNine[2][2] = posDown.getNeighborValueNeg1IfEdge(Direction.RIGHT);
         }

      String sLS = System.getProperty("line.separator", "\r\n");
      return  "Middle position in 9x9 grid is *this*: " + toString() + sLS +
         Arrays.toString(nineByNine[0]) + sLS +
         Arrays.toString(nineByNine[1]) + sLS +
         Arrays.toString(nineByNine[2]);
   }
}
public class MazePosDemo  {
   private static final int[][] MAZE_GRID = new int[][] {
      {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
      {0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1},
      {1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1},
      {1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1},
      {1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1},
      {1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1},
      {1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,0,1},
      {1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1},
      {1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1},
      {1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1},
      {1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1},
      {1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1},
      {1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1},
      {1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
      {1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1},
      {1,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1},
      {1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1},
      {1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1},
      {1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1},
      {1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
      {1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1},
      {1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,1},
      {1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1},
      {1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
      {1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1},
      {1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1},
      {1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1,0,1},
      {1,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1},
      {1,0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1},
      {1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1},
      {1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1},
      {1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1},
      {1,0,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1},
      {1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1},
      {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1}};
   private static final MazePosition MAZE_HOLDER = new MazePosition(MAZE_GRID);

   public static final void main(String[] ignored)  {
      MazePosition pos = new MazePosition(0, 0);
      System.out.println("start: " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.RIGHT);
      System.out.println("right: " + pos);

      pos = pos.getNeighbor(Direction.DOWN);
      System.out.println("down:  " + pos);

      pos = pos.getNeighbor(Direction.LEFT);
      System.out.println("left:  " + pos);

      pos = pos.getNeighbor(Direction.UP);
      System.out.println("up:    " + pos);

      pos = pos.getNeighbor(Direction.UP);
      System.out.println("up:    " + pos);

      System.out.println(pos.getNineByNine());
   }

}

From every-time checks, to Functors, to Java 8 Lamdas (sort of)

The problem

Take this example class, which adapts an Appendable into a Writer:

   import  java.io.Closeable;
   import  java.io.Flushable;
   import  java.io.IOException;
   import  java.io.Writer;
   import  java.util.Objects;
/**
   <P>{@code java WriterForAppendableWChecksInFunc}</P>
 **/
public class WriterForAppendableWChecksInFunc extends Writer  {
   private final Appendable apbl;
   public WriterForAppendableWChecksInFunc(Appendable apbl)  {
      if(apbl == null)  {
         throw  new NullPointerException("apbl");
      }
      this.apbl = apbl;
   }

      //Required functions, but not relevant to this post...START
         public void write(char[] a_c, int i_ndexStart, int i_ndexEndX) throws IOException {
         public Writer append(char c_c) throws IOException {
         public Writer append(CharSequence text) throws IOException {
         public Writer append(CharSequence text, int i_ndexStart, int i_ndexEndX) throws IOException  {
      //Required functions, but not relevant to this post...END

   public void flush() throws IOException {
      if(apbl instanceof Flushable)  {
         ((Flushable)apbl).flush();
      }
   }
   public void close() throws IOException {
      flush();
      if(apbl instanceof Closeable)  {
         ((Closeable)apbl).close();
      }
   }
}

Not all Appendables are Flushable or Closeable, but those that are, must also be closed and flushed. The actual type of the Appendable object must therefore be checked in every call to flush() and close() and, when it is indeed that type, it is casted and the function is called.

Admittedly, this isn’t the greatest example, since close() is only called once per-instance, and flush() isn’t necessarily called that often either. Also, instanceof, while reflective, is not too bad given this particular example-usage. Still, the concept of having to check something everytime you need to do something else is a real one, and avoiding these "every-time" checks, when it really matters, provides significant benefits.

Move all "heavy duty" checks to the constructor

So where do you start? How do you avoid these checks without compromising your code?

In our example, the easiest step is to move all instanceof checks to the constructor.

public class WriterForAppendableWChecksInCnstr extends Writer  {
   private final Appendable apbl;
   private final boolean isFlshbl;
   private final boolean isClsbl;
   public WriterForAppendableWChecksInCnstr(Appendable apbl)  {
      if(apbl == null)  {
         throw  new NullPointerException("apbl");
      }
      this.apbl = apbl;
      isFlshbl = (apbl instanceof Flushable);
      isClsbl = (apbl instanceof Closeable);
   }

         //write and append functions go here...

   public void flush() throws IOException {
      if(isFlshbl)  {
         ((Flushable)apbl).flush();
      }
   }
   public void close() throws IOException {
      flush();
      if(isClsbl)  {
         ((Closeable)apbl).close();
      }
   }
}

Now that these “heavy duty” checks are done only once, only boolean checks need to be done by flush() and close(). While certainly an improvement, how can these in-function checks be eliminated entirely?

If only you could somehow define a function which could be stored by the class and then used by flush() and close()

public class WriterForAppendableWChecksInCnstr extends Writer  {
   private final Appendable apbl;
   private final FlushableFunction flshblFunc;  //If only!
   private final CloseableFunction clsblFunc;   //If only!
   public WriterForAppendableWChecksInCnstr(Appendable apbl)  {
      if(apbl == null)  {
         throw  new NullPointerException("apbl");
      }
      this.apbl = apbl;

      if(apbl instanceof Flushable)  {
         flshblFunc = //The flushable function
      }  else  {
         flshblFunc = //A do-nothing function
      }
      if(apbl instanceof Closeable)  {
         clsblFunc = //The closeable function
      }  else  {
         clsblFunc = //A do-nothing function
      }
   }

          //write and append functions go here...

   public void flush() throws IOException {
      flshblFunc();                             //If only!
   }
   public void close() throws IOException {
      flush();
      clsblFunc();                              //If only!
   }
}

But passing functions is not possible…at least not until Java 8 Lamdas. So how do you do it in pre-8 versions of Java?

Functors

With a Functor. A Functor is basically a Lambda, but one that is wrapped in an object. While functions cannot be passed into other functions as parameters, objects can. So essentially, Functors and Lambdas are a ways to pass around functions.

So how can we implement a Functor into our writer-adapter? What we know is that close() and flush() are only useful with Closeable and Flushable objects. And that some Appendables are Flushable, some Closeable, some neither, some both.

Therefore, we can store a Flushable and Closeable object at the top of the class:

public class WriterForAppendable extends Writer  {
   private final Appendable apbl;
   private final Flushable  flshbl;
   private final Closeable  clsbl;
   public WriterForAppendable(Appendable apbl)  {
      if(apbl == null)  {
         throw  new NullPointerException("apbl");
      }

      //Avoids instanceof at every call to flush() and close()

      if(apbl instanceof Flushable)  {
         flshbl = apbl;              //This Appendable *is* a Flushable
      }  else  {
         flshbl = //??????           //But what goes here????
      }

      if(apbl instanceof Closeable)  {
         clsbl = apbl;               //This Appendable *is* a Closeable
      }  else  {
         clsbl = //??????            //And here????
      }

      this.apbl = apbl;
   }

          //write and append functions go here...

   public void flush() throws IOException {
      flshbl.flush();
   }
   public void close() throws IOException {
      flush();
      clsbl.close();
   }
}

The "every-time" checks have now been eliminated. But when the Appendable is not a Flushable or not a Closeable, what should be stored?

Do nothing Functors

A do nothing Functor…

class CloseableDoesNothing implements Closeable  {
   public void close() throws IOException  {
   }
}
class FlushableDoesNothing implements Flushable  {
   public void flush() throws IOException  {
   }
}

…which can be implemented as an anonymous inner class:

public WriterForAppendable(Appendable apbl)  {
   if(apbl == null)  {
      throw  new NullPointerException("apbl");
   }
   this.apbl = apbl;

   //Avoids instanceof at every call to flush() and close()
   flshbl = ((apbl instanceof Flushable)
      ?  (Flushable)apbl
      :  new Flushable()  {
            public void flush() throws IOException  {
            }
         });
   clsbl = ((apbl instanceof Closeable)
      ?  (Closeable)apbl
      :  new Closeable()  {
            public void close() throws IOException  {
            }
         });
   }

   //the rest of the class goes here...
}

To be most efficient, these do-nothing functors should be implemented as static final objects. And with that, here is the final version of our class:

package  xbn.z.xmpl.lang.functor;
   import  java.io.Closeable;
   import  java.io.Flushable;
   import  java.io.IOException;
   import  java.io.Writer;
public class WriterForAppendable extends Writer  {
   private final Appendable apbl;
   private final Flushable  flshbl;
   private final Closeable  clsbl;

   //Do-nothing functors
      private static final Flushable FLUSHABLE_DO_NOTHING = new Flushable()  {
         public void flush() throws IOException  {
         }
      };
      private static final Closeable CLOSEABLE_DO_NOTHING = new Closeable()  {
         public void close() throws IOException  {
         }
      };

   public WriterForAppendable(Appendable apbl)  {
      if(apbl == null)  {
         throw  new NullPointerException("apbl");
      }
      this.apbl = apbl;

      //Avoids instanceof at every call to flush() and close()
      flshbl = ((apbl instanceof Flushable)
         ?  (Flushable)apbl
         :  FLUSHABLE_DO_NOTHING);
      clsbl = ((apbl instanceof Closeable)
         ?  (Closeable)apbl
         :  CLOSEABLE_DO_NOTHING);
   }

   public void write(char[] a_c, int i_ndexStart, int i_ndexEndX) throws IOException {
      apbl.append(String.valueOf(a_c), i_ndexStart, i_ndexEndX);
   }
   public Writer append(char c_c) throws IOException {
      apbl.append(c_c);
      return  this;
   }
   public Writer append(CharSequence c_q) throws IOException {
      apbl.append(c_q);
      return  this;
   }
   public Writer append(CharSequence c_q, int i_ndexStart, int i_ndexEndX) throws IOException  {
      apbl.append(c_q, i_ndexStart, i_ndexEndX);
      return  this;
   }
   public void flush() throws IOException {
      flshbl.flush();
   }
   public void close() throws IOException {
      flush();
      clsbl.close();
   }
}

This particular example comes from this question on stackoverflow. A fully working, and fully-documented version of this example (including a testing function) can be found at the bottom of that question-post (above the answer).

Implementing Functors with an Enum

Leaving our WriterAppendable example, let’s take a look at another way to implement Functors: with an Enum.

As an example, this enum has a move function for each cardinal direction:

public enum CardinalDirection  {
   NORTH(new MoveNorth()),
   SOUTH(new MoveSouth()),
   EAST(new MoveEast()),
   WEST(new MoveWest());

   private final MoveInDirection dirFunc;

   CardinalDirection(MoveInDirection dirFunc)  {
      if(dirFunc == null)  {
         throw  new NullPointerException("dirFunc");
      }
      this.dirFunc = dirFunc;
   }
   public void move(int steps)  {
      dirFunc.move(steps);
   }
}

Its constructor requires a MoveInDirection object (which is an interface, but could also be an abstract class):

interface MoveInDirection  {
   void move(int steps);
}

There are naturally four concrete implementations of this interface, one per direction. Here is a trivial implementation for north:

class MoveNorth implements MoveInDirection  {
   public void move(int steps)  {
      System.out.println("Moved " + steps + " steps north.");
   }
}

Using this Functor is done with this simple call:

CardinalDirection.WEST.move(3);

Which, in our example, outputs this to the console:

Moved 3 steps west.

And here is a full working example:

/**
   <P>Demonstrates a Functor implemented as an Enum.</P>

   <P>{@code java EnumFunctorXmpl}</P>
 **/
public class EnumFunctorXmpl  {
   public static final void main(String[] ignored)  {
       CardinalDirection.WEST.move(3);
       CardinalDirection.NORTH.move(2);
       CardinalDirection.EAST.move(15);
   }
}
enum CardinalDirection  {
   NORTH(new MoveNorth()),
   SOUTH(new MoveSouth()),
   EAST(new MoveEast()),
   WEST(new MoveWest());
   private final MoveInDirection dirFunc;
   CardinalDirection(MoveInDirection dirFunc)  {
      if(dirFunc == null)  {
         throw  new NullPointerException("dirFunc");
      }
      this.dirFunc = dirFunc;
   }
   public void move(int steps)  {
      dirFunc.move(steps);
   }
}
interface MoveInDirection  {
   void move(int steps);
}
class MoveNorth implements MoveInDirection  {
   public void move(int steps)  {
      System.out.println("Moved " + steps + " steps north.");
   }
}
class MoveSouth implements MoveInDirection  {
   public void move(int steps)  {
      System.out.println("Moved " + steps + " steps south.");
   }
}
class MoveEast implements MoveInDirection  {
   public void move(int steps)  {
      System.out.println("Moved " + steps + " steps east.");
   }
}
class MoveWest implements MoveInDirection  {
   public void move(int steps)  {
      System.out.println("Moved " + steps + " steps west.");
   }
}

Output:

[C:\java_code\]java EnumFunctorXmpl
Moved 3 steps west.
Moved 2 steps north.
Moved 15 steps east.

I haven’t started with Java 8 yet, so I can’t write the Lamdas section yet :)

Using regular expressions to validate a numeric range

To be clear: When a simple if statement will suffice

if(num < -2055  ||  num > 2055)  {
   throw  new IllegalArgumentException("num (" + num + ") must be between -2055 and 2055");
}

using regular expressions for validating numeric ranges is not recommended.

In addition, since regular expressions analyze strings, numbers must first be translated to a string before they can be tested (an exception is when the number happens to already be a string, such as when getting user input from the console).

(To ensure the string is a number to begin with, you could use org.apache.commons.lang3.math.NumberUtils#isNumber(s))

Despite this, figuring out how to validate number ranges with regular expressions is interesting and instructive.

A one number range

Rule: A number must be exactly 15.

The simplest range there is. A regex to match this is

\b15\b

Word boundaries are necessary to avoid matching the 15 inside of 8215242.

A two number range

The rule: The number must be between 15 and 16. Three possible regexes:

\b(15|16)\b
\b1(5|6)\b
\b1[5-6]\b

A number range "mirrored" around zero

The rule: The number must be between -12 and 12.

Here is a regex for 0 through 12, positive-only:

   \b(\d|1[0-2])\b

Free-spaced:

   \b(         //The beginning of a word (or number), followed by either
      \d       //   Any digit 0 through 9
   |           //Or
      1[0-2]   //   A 1 followed by any digit between 0 and 2.
   )\b         //The end of a word

Making this work for both negative and positive is as simple as adding an optional dash at the start:

-?\b(\d|1[0-2])\b

(This assumes no inappropriate characters precede the dash.)

To forbid negative numbers, a negative lookbehind is necessary:

(?<!-)\b(\d|1[0-2])\b

Leaving the lookbehind out would cause the 11 in -11 to match. (The first example in this post should have this added.)

Note: \d versus [0-9]

In order to be compatible with all regex flavors, all \d-s should be changed to [0-9]. For example, .NET considers non ASCII numbers, such as those in different languages, as legal values for \d. Except for in the last example, for brevity, it’s left as \d.

(With thanks to TimPietzcker at stackoverflow)

Three digits, with all but the first digit equal to zero

Rule: Must be between 0 and 400.

A possible regex:

(?<!-)\b([1-3]?\d{1,2}|400)\b

Free spaced:

   (?<!-)          //Something not preceded by a dash
   \b(             //Word-start, followed by either
      [1-3]?       //   No digit, or the digit 1, 2, or 3
         \d{1,2}   //   Followed by one or two digits (between 0 and 9)
   |               //Or
      400          //   The number 400
   )\b             //Word-end

Another possibility that should never be used:

\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255|256|257|258|259|260|261|262|263|264|265|266|267|268|269|270|271|272|273|274|275|276|277|278|279|280|281|282|283|284|285|286|287|288|289|290|291|292|293|294|295|296|297|298|299|300|301|302|303|304|305|306|307|308|309|310|311|312|313|314|315|316|317|318|319|320|321|322|323|324|325|326|327|328|329|330|331|332|333|334|335|336|337|338|339|340|341|342|343|344|345|346|347|348|349|350|351|352|353|354|355|356|357|358|359|360|361|362|363|364|365|366|367|368|369|370|371|372|373|374|375|376|377|378|379|380|381|382|383|384|385|386|387|388|389|390|391|392|393|394|395|396|397|398|399|400)\b

Final example: Four digits, mirrored around zero, that does not end with zeros.

Rule: Must be between -2055 and 2055

This is from a question on stackoverflow.

Regex:

(-?\b(?:20(?:5[0-5]|[0-4][0-9])|1[0-9]{3}|[1-9][0-9]{0,2}|(?<!-)0+))\b

Free-spaced:

(             //Capture group for the entire number
   -?\b             //Optional dash, followed by a word (number) boundary
   (?:20            //Followed by "20", which is followed by one of 
         (?:5[0-5]        //50 through 55
      |                                         //or
         [0-4][0-9])      //00 through 49
      |                                         //or
         1[0-9]{3}        //a one followed by any three digits
      |                                         //or
         [1-9][0-9]{0,2}  //1-9 followed by 0 through 2 of any digit
      |                                         //or
         (?<!-)0+         //one-or-more zeros *not* preceded by a dash
   )                 //end "or" non-capture group
)\b            //End number capture group, followed by a word-bound

Here is a visual representation of this regex (Try it out yourself):


(With thanks to PlasmaPower and Casimir et Hippolyte on stackoverflow for the debugging assistance.)

Final note

Depending on what you are capturing, it is likely that all sub-groups should be made into non-capture groups. For example, this:

(-?\b(?:20(?:5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\b)

Instead of this:

-?\b(20(5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\b

Example Java implementation

      import  java.util.Scanner;
      import  java.util.regex.Matcher;
      import  java.util.regex.Pattern;
      import  org.apache.commons.lang.math.NumberUtils;
    /**
      <P>Confirm a user-input number is a valid number by reading a string an testing it is numeric before converting it to an it--this loops until a valid number is provided.</P>
   
      <P>{@code java UserInputNumInRangeWRegex}</P>
     **/
    public class UserInputNumInRangeWRegex  {
      public static final void main(String[] ignored)  {
   
         int num = -1;
         boolean isNum = false;
   
         int iRangeMax = 2055;
   
         //"": Dummy string, to reuse matcher
         Matcher mtchrNumNegThrPos = Pattern.compile("(-?\\b(?:20(?:5[0-5]|[0-4][0-9])|1[0-9]{3}|[1-9][0-9]{0,2}|(?<!-)0+))\\b").matcher("");
   
         do  {
            System.out.print("Enter a number between -" + iRangeMax + " and " + iRangeMax + ": ");
            String strInput = (new Scanner(System.in)).next();
            if(!NumberUtils.isNumber(strInput))  {
               System.out.println("Not a number. Try again.");
            }  else if(!mtchrNumNegThrPos.reset(strInput).matches())  {
               System.out.println("Not in range. Try again.");
            }  else  {
               //Safe to convert
               num = Integer.parseInt(strInput);
               isNum = true;
            }
         }  while(!isNum);
   
         System.out.println("Number: " + num);
      }
   }

Output

[C:\java_code\]java UserInputNumInRangeWRegex
Enter a number between -2055 and 2055: tuhet
Not a number. Try again.
Enter a number between -2055 and 2055: 283837483
Not in range. Try again.
Enter a number between -2055 and 2055: -200000
Not in range. Try again.
Enter a number between -2055 and 2055: -300
Number: -300