package unit; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; /** * hazelcast-0001: QueueContainer.compareAndRemove() — ArrayList.contains() O(Q×D) * hazelcast-0002: QueueContainer.contains() — inner queue scan O(D×Q) * * Standalone unit test — no JUnit required. * Compile: javac -d defects/hazelcast/unit defects/hazelcast/unit/HazelcastQueueAlgorithm.java * Run: java -cp defects/hazelcast/unit unit.HazelcastQueueAlgorithm * * QueueItem is modelled as a plain String (the serialized object). * Collections mirror the production types: LinkedList (queue), ArrayList (dataList). */ public class HazelcastQueueAlgorithm { static long slowOps; static long fastOps; // ----------------------------------------------------------------------- // hazelcast-0001: compareAndRemove // ----------------------------------------------------------------------- /** * SLOW: mirrors QueueContainer.compareAndRemove() — ArrayList dataList.contains() * is called inside the outer loop over every queue item. */ static Map slowCompareAndRemove(Queue itemQueue, Collection dataList, boolean retain) { slowOps = 0; Map map = new LinkedHashMap<>(); int id = 0; for (String item : itemQueue) { id++; slowOps++; // outer loop step boolean found = false; for (String d : dataList) { // ArrayList linear scan — O(D) slowOps++; if (d.equals(item)) { found = true; break; } } if ((retain && !found) || (!retain && found)) { map.put(id, item); } } return map; } /** * FAST: patched compareAndRemove — pre-build HashSet before outer loop. */ static Map fastCompareAndRemove(Queue itemQueue, Collection dataList, boolean retain) { fastOps = 0; Set dataSet = new HashSet<>(dataList); // O(D) one-time fastOps += dataList.size(); Map map = new LinkedHashMap<>(); int id = 0; for (String item : itemQueue) { id++; fastOps++; // O(1) hash lookup boolean found = dataSet.contains(item); if ((retain && !found) || (!retain && found)) { map.put(id, item); } } return map; } // ----------------------------------------------------------------------- // hazelcast-0002: contains (containsAll) // ----------------------------------------------------------------------- /** * SLOW: mirrors QueueContainer.contains() — scans entire queue for each query item. */ static boolean slowContains(Queue itemQueue, Collection dataSet) { slowOps = 0; for (String data : dataSet) { // outer loop over D queries slowOps++; boolean found = false; for (String item : itemQueue) { // inner scan O(Q) slowOps++; if (item.equals(data)) { found = true; break; } } if (!found) return false; } return true; } /** * FAST: patched contains — build HashSet from queue once, then O(1) per query. */ static boolean fastContains(Queue itemQueue, Collection dataSet) { fastOps = 0; Set queueData = new HashSet<>(itemQueue.size() * 2); for (String item : itemQueue) { fastOps++; // O(Q) build queueData.add(item); } for (String data : dataSet) { fastOps++; // O(D) lookups, each O(1) if (!queueData.contains(data)) return false; } return true; } // ----------------------------------------------------------------------- // Test helpers // ----------------------------------------------------------------------- static Queue buildQueue(int q) { Queue queue = new LinkedList<>(); for (int i = 0; i < q; i++) queue.add("item-" + i); return queue; } /** dataList: D items, half overlap with queue, half are unique. */ static List buildDataList(int q, int d) { List list = new ArrayList<>(d); for (int i = 0; i < d / 2; i++) list.add("item-" + i); // overlaps for (int i = 0; i < d - d / 2; i++) list.add("remove-" + i); // not in queue return list; } /** dataSet for containsAll: all items present so worst-case full scan. */ static List buildAllPresentList(int q, int d) { List list = new ArrayList<>(d); for (int i = 0; i < d; i++) list.add("item-" + i); return list; } // ----------------------------------------------------------------------- // hazelcast-0001 test // ----------------------------------------------------------------------- static void testCompareAndRemove(int q, int d, int expectedNx) { Queue queue = buildQueue(q); List dataList = buildDataList(q, d); Map slowResult = slowCompareAndRemove(new LinkedList<>(queue), dataList, false); long slow = slowOps; Map fastResult = fastCompareAndRemove(new LinkedList<>(queue), dataList, false); long fast = fastOps; boolean match = slowResult.equals(fastResult); boolean ratio = slow > fast * expectedNx; System.out.printf("[0001] compareAndRemove q=%-5d d=%-4d slow=%8d fast=%6d ratio=%5.1fx match=%b PASS=%b%n", q, d, slow, fast, (double) slow / fast, match, match && ratio); if (!match || !ratio) { throw new AssertionError( "FAIL compareAndRemove q=" + q + " d=" + d + " match=" + match + " slowOps=" + slow + " fastOps=" + fast + " needed ratio>" + expectedNx); } } // ----------------------------------------------------------------------- // hazelcast-0002 test // ----------------------------------------------------------------------- static void testContains(int q, int d, int expectedNx) { Queue queue = buildQueue(q); List queryList = buildAllPresentList(q, d); boolean slowResult = slowContains(new LinkedList<>(queue), queryList); long slow = slowOps; boolean fastResult = fastContains(new LinkedList<>(queue), queryList); long fast = fastOps; boolean match = slowResult == fastResult; boolean ratio = slow > fast * expectedNx; System.out.printf("[0002] contains q=%-5d d=%-4d slow=%8d fast=%6d ratio=%5.1fx match=%b PASS=%b%n", q, d, slow, fast, (double) slow / fast, match, match && ratio); if (!match || !ratio) { throw new AssertionError( "FAIL contains q=" + q + " d=" + d + " match=" + match + " slowOps=" + slow + " fastOps=" + fast + " needed ratio>" + expectedNx); } } // ----------------------------------------------------------------------- // main // ----------------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== hazelcast-0001: QueueContainer.compareAndRemove O(Q*D) vs O(Q+D) ==="); testCompareAndRemove(200, 100, 5); testCompareAndRemove(1000, 500, 20); testCompareAndRemove(2000, 1000, 50); System.out.println("=== hazelcast-0002: QueueContainer.contains O(D*Q) vs O(Q+D) ==="); testContains(200, 100, 5); testContains(1000, 500, 20); testContains(2000, 1000, 50); System.out.println("6/6 PASS"); } }