package unit; import java.util.*; /** * kafka-0006: DefaultTaskManager lockedTasks ArrayList → HashSet * * Demonstrates that ArrayList.contains() inside a task-scheduling loop is O(T×L) * while HashSet.contains() is O(T). * * Compile: javac -d . KafkaStreamsDefaultTaskManagerTest.java * Run: java unit.KafkaStreamsDefaultTaskManagerTest */ public class KafkaStreamsDefaultTaskManagerTest { // Simulated TaskId (Integer wrapper for simplicity) static class TaskId { final int id; TaskId(int id) { this.id = id; } @Override public boolean equals(Object o) { return o instanceof TaskId && ((TaskId) o).id == this.id; } @Override public int hashCode() { return Integer.hashCode(id); } @Override public String toString() { return "Task-" + id; } } // --- SLOW: ArrayList lockedTasks (defective, mirrors DefaultTaskManager line 62) --- static class DefectiveTaskManager { private final List lockedTasks = new ArrayList<>(); private final List activeTasks; DefectiveTaskManager(List activeTasks) { this.activeTasks = activeTasks; } void lockTasks(Set ids) { lockedTasks.addAll(ids); } // Returns count of assignable tasks — mirrors assignNextTask() loop body int countAssignable() { int count = 0; for (TaskId task : activeTasks) { if (!lockedTasks.contains(task)) { // O(L) ArrayList scan count++; } } return count; } } // --- FAST: HashSet lockedTasks (fixed) --- static class FixedTaskManager { private final Set lockedTasks = new HashSet<>(); private final List activeTasks; FixedTaskManager(List activeTasks) { this.activeTasks = activeTasks; } void lockTasks(Set ids) { lockedTasks.addAll(ids); } int countAssignable() { int count = 0; for (TaskId task : activeTasks) { if (!lockedTasks.contains(task)) { // O(1) HashSet lookup count++; } } return count; } } static long bench(Runnable r, int iters) { // warmup for (int i = 0; i < 3; i++) r.run(); long t0 = System.nanoTime(); for (int i = 0; i < iters; i++) r.run(); return System.nanoTime() - t0; } public static void main(String[] args) { System.out.println("kafka-0006: DefaultTaskManager lockedTasks ArrayList → HashSet"); System.out.println("=".repeat(65)); int[] sizes = {100, 500, 1000}; int iters = 500; boolean allPass = true; for (int n : sizes) { // Build n active tasks, lock half List active = new ArrayList<>(); Set toLock = new HashSet<>(); for (int i = 0; i < n; i++) { TaskId t = new TaskId(i); active.add(t); if (i % 2 == 0) toLock.add(t); } DefectiveTaskManager slow = new DefectiveTaskManager(active); slow.lockTasks(toLoad(toLoad(toLock))); FixedTaskManager fast = new FixedTaskManager(active); fast.lockTasks(toLoad(toLock)); // Correctness check int slowResult = slow.countAssignable(); int fastResult = fast.countAssignable(); boolean correct = (slowResult == fastResult) && (slowResult == n / 2); if (!correct) allPass = false; long slowNs = bench(slow::countAssignable, iters); long fastNs = bench(fast::countAssignable, iters); double ratio = (double) slowNs / fastNs; System.out.printf("N=%-5d slow=%7.2f ms fast=%7.2f ms ratio=%.1fx assignable=%d %s%n", n, slowNs / 1_000_000.0 / iters, fastNs / 1_000_000.0 / iters, ratio, slowResult, correct ? "PASS" : "FAIL(result mismatch)"); } System.out.println("=".repeat(65)); System.out.println(allPass ? "ALL PASS" : "SOME FAILED"); if (!allPass) System.exit(1); } // Helper: copy set into set (simulate addAll contract) static Set toLoad(Set s) { return s; } }