title: Generate unique pairs from collection items in JAVA + StackOverflowError
body: <p>I need to find a fast and efficient way to create unique pairs out of array items if there is more than two of them.</p>
  
  <p>My first question is that this code I came up with is throwing sometimes <strong>java.lang.StackOverflowError</strong>, why? I know it is going to deep with the recursive calls in the helper, but how to fix it?</p>
  
  <p>My second question is how to make the code more efficient. I don't need to use array - it could be some other collection type.</p>
  
  <p>This is what I have come up with:</p>
  
  <pre><code>import java.util.HashMap;
  import java.util.concurrent.ThreadLocalRandom;
  
  /**
   * Generates unique pairs from items in array. Each item cannot occur more than
   * once as key nor value.
   *
   * @author lkallas
   */
  public class UniquePairs {
  
      private static final String[] NAMES
              = new String[]{"Aaron", "Barney", "Charlie", "Desiré", "Edward"};
  
      private static final HashMap&lt;String, String&gt; PAIRS = new HashMap&lt;&gt;();
  
      public static void main(String[] args) {
  
          // Check if there is more than one item in array.
          if (NAMES.length &gt; 1) {
              // Find pairs
              for (String name : NAMES) {
                  if (!PAIRS.containsKey(name)) {
                      PAIRS.put(name, helper(name));
                  }
              }
              // Show results.
              PAIRS.entrySet().stream().forEach((pair) -&gt; {
                  System.out.println(pair.getKey() + " - " + pair.getValue());
              });
          } else {
              System.out.println(NAMES[0]);
  
          }
      }
  
      /**
       * Helper for finding partner.
       *
       * @param key Name that need partner.
       * @return Unique partner.
       */
      private static String helper(String key) {
  
          // Get random partner from array.
          String partner = NAMES[getRandomInt(0, NAMES.length - 1)];
  
          // Cannot pair up a name with itself. Also partner cannot occur more than once.
          if (key.equals(partner) || PAIRS.containsValue(partner)) {
              partner = helper(key);
          }
          return partner;
      }
  
      /**
       * Random integer in the given range.
       *
       * @param min Minimum value of the random integer.
       * @param max Maximum value of the random integer.
       * @return Random integer in given range.
       */
      private static int getRandomInt(int min, int max) {
  
          return ThreadLocalRandom.current().nextInt(min, max + 1);
      }
  
  }
  </code></pre>
  
  <p><strong>EDIT:</strong>
  Using</p>
  
  <pre><code>return ThreadLocalRandom.current().nextInt(min, max + 1);
  </code></pre>
  
  <p>instead of</p>
  
  <pre><code>return new Random().nextInt((max - min) + 1) + min;
  </code></pre>
  
  <p><strong>EDIT 2:</strong></p>
  
  <p>Created special class for this kind of operation. Feel free to use it if necessary.</p>
  
  <pre><code>import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ThreadLocalRandom;
  
  /**
   *
   * @author lkallas
   */
  public class MatchMaker {
  
      private final Map&lt;Object, Object&gt; PAIRS;
      private List&lt;? extends Object&gt; items;
  
      public MatchMaker() {
          this.PAIRS = new HashMap&lt;&gt;();
      }
  
      /**
       * Pairs items uniquely and randomly so that keys nor values are repeated.
       * For proper pairing of Objects it is recommended to provide your own
       * implementation of &lt;code&gt;equals()&lt;/code&gt; method. Also bear in mind that
       * you should also override &lt;code&gt;hashCode()&lt;/code&gt; if there's any chance of
       * your objects being used in a hash table.
       *
       * @param input List with objects that are paired with each other.
       * @return Map with generated pairs.
       * @throws IllegalArgumentException When input List is empty or contains
       * only one item.
       */
      public Map&lt;?, ?&gt; getPairs(List&lt;? extends Object&gt; input)
              throws IllegalArgumentException {
  
          if (input.size() &gt; 1) {
  
              items = input;
  
              for (int i = 0; i &lt; input.size() - 1; i++) {
                  Object k = input.get(i);
                  PAIRS.put(k, getPartner(k));
              }
  
              Object k = items.get(items.size() - 1);
              if (PAIRS.containsValue(k)) {
                  PAIRS.put(k, getPartner(k));
              } else {
                  Object k1 = items.get(getRandomInt(0, items.size() - 1));
                  PAIRS.put(k, PAIRS.get(k1));
                  PAIRS.put(k1, k);
              }
          } else {
              throw new IllegalArgumentException("Can't pair one or less items.");
          }
          return PAIRS;
      }
  
      /**
       * Helper for finding a random partner.
       *
       * @param key Object that needs partner.
       * @return Unique partner that is not used by other keys.
       */
      private Object getPartner(Object key) {
  
          // Get random partner from array.
          Object partner = items.get(getRandomInt(0, items.size() - 1));
  
          // Cannot pair up a key with itself. Also partner cannot occur more than once.
          if (key.equals(partner) || PAIRS.containsValue(partner)) {
              partner = getPartner(key);
          }
          return partner;
      }
  
      /**
       * Random integer in the given range.
       *
       * @param min Minimum value of the random integer.
       * @param max Maximum value of the random integer.
       * @return Random integer in given range.
       */
      private static int getRandomInt(int min, int max) {
  
          return ThreadLocalRandom.current().nextInt(min, max + 1);
      }
  }
  </code></pre>