Process N lists simultaneously by doing permutations I'm trying to see if someone can come up with a better solution for this algorithm.

I have an input of three lists/queues and I have to group/sort the elements of xpn, tu, and efx — depending on the presence (or not) of UniqueKey. For instance, if UniqueKey exists, I have to try to form a list/tuple of xpn, tu, and efx (if they exists for that UniqueKey)...and so on.

This is the "main" class:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public final class Request {
  private Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; result;

  private Queue&lt;Node&gt; xpn;

  private Queue&lt;Node&gt; tu;

  private Queue&lt;Node&gt; efx;

  public Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; transform() {
    final int max = estimateSize();
    result = new LinkedHashMap&lt;&gt;(max / 3);
    for (int i = 0; i &lt; max; i++) {
      pollFrom(xpn).ifPresent(it -&gt; process(it, Category.XPN));
      pollFrom(tu).ifPresent(it -&gt; process(it, Category.TU));
      pollFrom(efx).ifPresent(it -&gt; process(it, Category.EFX));
    }
    return Collections.unmodifiableMap(result);
  }

  private int estimateSize() {
    final int[] sizes = { sizeOf(xpn), sizeOf(tu), sizeOf(efx) };
    int max = sizes[0];
    for (int i = 1; i &lt; sizes.length; i++) {
      if (sizes[i] &gt; max) { max = sizes[i]; }
    }
    return max;
  }

  private Optional&lt;Node&gt; pollFrom(final Queue&lt;Node&gt; collection) {
    return (null != collection) ? Optional.ofNullable(collection.poll()) : Optional.empty();
  }

  private void process(final Node thiz, final Category category) {
    if (result.containsKey(thiz.getUniqueKey())) {
      final List&lt;Tuple3&lt;Node, Node, Node&gt;&gt; values = result.get(thiz.getUniqueKey());
      final Optional&lt;Tuple3&lt;Node, Node, Node&gt;&gt; optional = values.stream()
        .filter(tuple3 -&gt; emptyBucket.apply(tuple3, category))
        .findFirst();
      if (optional.isPresent()) {
        final Tuple3&lt;Node, Node, Node&gt; tuple3 = optional.get();
        if (Category.XPN == category) {
          tuple3.update1(thiz);
        } else if (Category.TU == category) {
          tuple3.update2(thiz);
        } else {
          tuple3.update3(thiz);
        }
      } else {
        result.get(thiz.getUniqueKey()).add(tupleFrom(thiz, category));
      }
    } else {
      final List&lt;Tuple3&lt;Node, Node, Node&gt;&gt; list = new LinkedList&lt;&gt;();
      list.add(tupleFrom(thiz, category));
      result.put((null != thiz.getUniqueKey()) ? thiz.getUniqueKey() : IdGen.uuid(), list);
    }
  }

  private BiFunction&lt;Tuple3&lt;Node, Node, Node&gt;, Category, Boolean&gt; emptyBucket = (tuple, category) -&gt; {
    if (Category.XPN == category) {
      return null == tuple._1();
    } else if (Category.TU == category) {
      return null == tuple._2();
    } else { // EFX
      return null == tuple._3();
    }
  };

  private Tuple3&lt;Node, Node, Node&gt; tupleFrom(final Node thiz, final Category category) {
    final Tuple3&lt;Node, Node, Node&gt; empty = new Tuple3&lt;&gt;(null, null, null);
    switch (category) {
      case XPN:
        return empty.update1(thiz);
      case TU:
        return empty.update2(thiz);
      case EFX:
        return empty.update3(thiz);
    }
    return empty;
  }

  private int sizeOf(final Collection&lt;Node&gt; collection) {
    return (null != collection) ? collection.size() : 0;
  }

  private enum Category {
    XPN, // 1st element in tuple
    TU,  // 2nd element in tuple
    EFX  // 3rd element in tuple
  }
}


The content of Node and Tuple3 are irrelevant, but I can post it if that may help. I'm just trying to see if there is a better way of doing this. The reason you don't see a constructor is because that class is being constructed/deserialized via Jackson. I would really like to avoid having result at class level.

PS: Don't mind about the method/variable names.
Not sure why you're making it so hard on yourself. Why not just go through each of the 3 queues individually?

For the first queue, just go through it and make a new Node for each element. Add it to your result list.

For the other 2 queues, check if the index is already in the result list. If it is, add it to that node, otherwise create a new node.



EDIT it seems my point wasn't clear, so let me show you the difference:

public Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; transform2() {
    Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; result = new LinkedHashMap&lt;&gt;(estimateSize() / 3);

    for (Node node : xpn) {
        if (!result.containsKey(node.getUniqueKey())) {
            result.put(node.getUniqueKey(), new LinkedList&lt;&gt;());
        }
        result.get(node.getUniqueKey()).add(new Tuple3&lt;Node, Node, Node&gt;(node, null, null));
    }

    for (Node node : tu) {
        if (!result.containsKey(node.getUniqueKey())) {
            result.put(node.getUniqueKey(), new LinkedList&lt;&gt;());
        }
        List&lt;Tuple3&lt;Node, Node, Node&gt;&gt; tuples = result.get(node.getUniqueKey());
        boolean needNew = true;
        for (Tuple3&lt;Node, Node, Node&gt; tuple : tuples) {
            if (tuple._2() == null) {
                tuple.update2(node);
                needNew = false;
                break;
            }
        }
        if (needNew) {
            tuples.add(new Tuple3&lt;&gt;(null, node, null));
        }
    }

    for (Node node : efx) {
        if (!result.containsKey(node.getUniqueKey())) {
            result.put(node.getUniqueKey(), new LinkedList&lt;&gt;());
        }
        List&lt;Tuple3&lt;Node, Node, Node&gt;&gt; tuples = result.get(node.getUniqueKey());
        boolean needNew = true;
        for (Tuple3&lt;Node, Node, Node&gt; tuple : tuples) {
            if (tuple._3() == null) {
                tuple.update3(node);
                needNew = false;
                break;
            }
        }
        if (needNew) {
            tuples.add(new Tuple3&lt;&gt;(null, null, node));
        }
    }
    return result;
}


I personally find this a lot easier to understand than all your indirections and switches.

If you really don't want to have one big method you can also split it up like this:

public Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; transform() {
    Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; result = new LinkedHashMap&lt;&gt;(estimateSize() / 3);

    for (Node node : xpn) {
        Tuple3&lt;Node, Node, Node&gt; tuple = getTupleToModify(result, node.getUniqueKey(), (n -&gt; null == n._1()));
        tuple.update1(node);
    }

    for (Node node : tu) {
        Tuple3&lt;Node, Node, Node&gt; tuple = getTupleToModify(result, node.getUniqueKey(), (n -&gt; null == n._2()));
        tuple.update2(node);
    }

    for (Node node : efx) {
        Tuple3&lt;Node, Node, Node&gt; tuple = getTupleToModify(result, node.getUniqueKey(), (n -&gt; null == n._3()));
        tuple.update3(node);
    }
    return result;
}

private Tuple3&lt;Node, Node, Node&gt; getTupleToModify(
        Map&lt;String, List&lt;Tuple3&lt;Node, Node, Node&gt;&gt;&gt; currentTuples,
        String key, Function&lt;Tuple3&lt;Node, Node, Node&gt;,
        Boolean&gt; filter) {

    if (!currentTuples.containsKey(key)) {
        currentTuples.put(key, new LinkedList&lt;&gt;());
    }

    List&lt;Tuple3&lt;Node, Node, Node&gt;&gt; list = currentTuples.get(key);
    for (Tuple3&lt;Node, Node, Node&gt; tuple : list) {
        if (filter.apply(tuple)) {
            return tuple;
        }
    }
    Tuple3&lt;Node, Node, Node&gt; result = new Tuple3&lt;&gt;(null, null, null);
    list.add(result);
    return result;
}