java-topology/defects/hazelcast/unit/HazelcastQueueAlgorithm.java

216 lines
8.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Integer, String> slowCompareAndRemove(Queue<String> itemQueue,
Collection<String> dataList,
boolean retain) {
slowOps = 0;
Map<Integer, String> 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<Data> before outer loop.
*/
static Map<Integer, String> fastCompareAndRemove(Queue<String> itemQueue,
Collection<String> dataList,
boolean retain) {
fastOps = 0;
Set<String> dataSet = new HashSet<>(dataList); // O(D) one-time
fastOps += dataList.size();
Map<Integer, String> 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<String> itemQueue, Collection<String> 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<String> itemQueue, Collection<String> dataSet) {
fastOps = 0;
Set<String> 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<String> buildQueue(int q) {
Queue<String> 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<String> buildDataList(int q, int d) {
List<String> 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<String> buildAllPresentList(int q, int d) {
List<String> 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<String> queue = buildQueue(q);
List<String> dataList = buildDataList(q, d);
Map<Integer, String> slowResult = slowCompareAndRemove(new LinkedList<>(queue), dataList, false);
long slow = slowOps;
Map<Integer, String> 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<String> queue = buildQueue(q);
List<String> 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");
}
}