216 lines
8.3 KiB
Java
216 lines
8.3 KiB
Java
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");
|
||
}
|
||
}
|