diff --git a/java/src/main/java/com/optimizeme/CollectionUtils.java b/java/src/main/java/com/optimizeme/CollectionUtils.java index b1e28be..b96657b 100644 --- a/java/src/main/java/com/optimizeme/CollectionUtils.java +++ b/java/src/main/java/com/optimizeme/CollectionUtils.java @@ -8,21 +8,91 @@ public class CollectionUtils { public static > List mergeSorted(List a, List b) { - List result = new ArrayList<>(a.size() + b.size()); - int i = 0, j = 0; - while (i < a.size() && j < b.size()) { - if (a.get(i).compareTo(b.get(j)) <= 0) { + int na = a.size(); + int nb = b.size(); + List result = new ArrayList<>(na + nb); + + // Fast path for random-access lists: avoid duplicate get() calls by caching current elements + if (a instanceof java.util.RandomAccess && b instanceof java.util.RandomAccess) { + if (na == 0) { + if (nb > 0) result.addAll(b); + return result; + } + if (nb == 0) { + if (na > 0) result.addAll(a); + return result; + } + int i = 0, j = 0; + T aval = a.get(0); + T bval = b.get(0); + while (i < na && j < nb) { + if (aval.compareTo(bval) <= 0) { + result.add(aval); + i++; + if (i < na) { + aval = a.get(i); + } + } else { + result.add(bval); + j++; + if (j < nb) { + bval = b.get(j); + } + } + } + while (i < na) { result.add(a.get(i++)); - } else { + } + while (j < nb) { result.add(b.get(j++)); } + return result; + } + + // Generic path for non-random-access lists: use iterators to get O(n) traversal + java.util.Iterator ita = a.iterator(); + java.util.Iterator itb = b.iterator(); + + if (!ita.hasNext()) { + while (itb.hasNext()) { + result.add(itb.next()); + } + return result; } - while (i < a.size()) { - result.add(a.get(i++)); + if (!itb.hasNext()) { + while (ita.hasNext()) { + result.add(ita.next()); + } + return result; } - while (j < b.size()) { - result.add(b.get(j++)); + + T aval = ita.next(); + T bval = itb.next(); + + while (true) { + if (aval.compareTo(bval) <= 0) { + result.add(aval); + if (ita.hasNext()) { + aval = ita.next(); + } else { + // drain remaining from b (including current bval) + result.add(bval); + while (itb.hasNext()) result.add(itb.next()); + break; + } + } else { + result.add(bval); + if (itb.hasNext()) { + bval = itb.next(); + } else { + // drain remaining from a (including current aval) + result.add(aval); + while (ita.hasNext()) result.add(ita.next()); + break; + } + } } + return result; }