package unit; import java.util.*; /** * pulsar-0002: JavaInstanceRunnable.setupConfig() — List.contains() in config key validation loop. * * allFields is a List from BeanPropertiesReader.getBeanProperties(). * For each config key, .contains() is O(F). With K keys, total is O(K * F). * Fix: convert allFields to HashSet before the loop. */ public class PulsarJavaInstanceRunnableTest { // Slow path: List allFields with .contains() per key static Object[] slowValidateConfig(List allFields, Set configKeys) { long ops = 0; List invalidKeys = new ArrayList<>(); for (String s : configKeys) { // allFields.contains(s): O(F) linear scan boolean found = false; for (String f : allFields) { ops++; if (f.equals(s)) { found = true; break; } } if (!found) { invalidKeys.add(s); } } return new Object[]{invalidKeys, ops}; } // Fast path: HashSet for O(1) lookup static Object[] fastValidateConfig(List allFields, Set configKeys) { Set fieldSet = new HashSet<>(allFields); long ops = 0; List invalidKeys = new ArrayList<>(); for (String s : configKeys) { ops++; // O(1) HashSet.contains if (!fieldSet.contains(s)) { invalidKeys.add(s); } } return new Object[]{invalidKeys, ops}; } public static void main(String[] args) { int passed = 0; int total = 0; // Test 1: correctness — no invalid keys { total++; List allFields = Arrays.asList("host", "port", "timeout", "retries", "user"); Set configKeys = new HashSet<>(Arrays.asList("host", "port", "timeout")); @SuppressWarnings("unchecked") List slowInvalid = (List) slowValidateConfig(allFields, configKeys)[0]; @SuppressWarnings("unchecked") List fastInvalid = (List) fastValidateConfig(allFields, configKeys)[0]; assert slowInvalid.isEmpty() : "No invalid keys expected, slow found: " + slowInvalid; assert fastInvalid.isEmpty() : "No invalid keys expected, fast found: " + fastInvalid; System.out.println(" Test 1 PASS: no invalid keys"); passed++; } // Test 2: correctness — some invalid keys { total++; List allFields = Arrays.asList("host", "port", "timeout"); Set configKeys = new HashSet<>(Arrays.asList("host", "port", "badKey", "anotherBad")); @SuppressWarnings("unchecked") List slowInvalid = (List) slowValidateConfig(allFields, configKeys)[0]; @SuppressWarnings("unchecked") List fastInvalid = (List) fastValidateConfig(allFields, configKeys)[0]; Set slowSet = new HashSet<>(slowInvalid); Set fastSet = new HashSet<>(fastInvalid); assert slowSet.equals(fastSet) : "Slow and fast must find same invalid keys. slow=" + slowSet + " fast=" + fastSet; assert slowSet.contains("badKey") : "badKey must be invalid"; assert slowSet.contains("anotherBad") : "anotherBad must be invalid"; assert !slowSet.contains("host") : "host is valid, must not be invalid"; System.out.println(" Test 2 PASS: invalid keys=" + slowSet); passed++; } // Test 3: op count — slow O(K*F) vs fast O(K) { total++; int F = 150; // fields in config class int K = 100; // config keys List allFields = new ArrayList<>(); for (int i = 0; i < F; i++) allFields.add("field" + i); // Config keys: half valid, half invalid Set configKeys = new HashSet<>(); for (int i = 0; i < K / 2; i++) configKeys.add("field" + i); // valid for (int i = 0; i < K / 2; i++) configKeys.add("unknown-key-" + i); // invalid Object[] slowOut = slowValidateConfig(allFields, configKeys); Object[] fastOut = fastValidateConfig(allFields, configKeys); @SuppressWarnings("unchecked") Set slowInvalidSet = new HashSet<>((List) slowOut[0]); @SuppressWarnings("unchecked") Set fastInvalidSet = new HashSet<>((List) fastOut[0]); assert slowInvalidSet.equals(fastInvalidSet) : "Slow and fast must agree on invalid keys"; long slowOps = (Long) slowOut[1]; long fastOps = (Long) fastOut[1]; assert slowOps > fastOps : "Slow must do more ops: slowOps=" + slowOps + " fastOps=" + fastOps; assert fastOps == K : "Fast must make exactly K=" + K + " ops, got " + fastOps; long speedup = slowOps / fastOps; System.out.println(" Test 3 PASS: slowOps=" + slowOps + " fastOps=" + fastOps + " speedup=" + speedup + "x (F=" + F + " K=" + K + ")"); passed++; } // Test 4: worst-case O(K*F) — all config keys invalid (scan all F every time) { total++; int F = 100; int K = 80; List allFields = new ArrayList<>(); for (int i = 0; i < F; i++) allFields.add("field" + i); Set configKeys = new HashSet<>(); for (int i = 0; i < K; i++) configKeys.add("invalid-key-" + i); long expectedSlowOps = (long) K * F; // every key scans all F fields long actualSlowOps = 0; for (String key : configKeys) { for (String f : allFields) { actualSlowOps++; if (f.equals(key)) break; } } assert actualSlowOps == expectedSlowOps : "Expected " + expectedSlowOps + " ops (K*F), got " + actualSlowOps; long fastOps = K; long speedup = actualSlowOps / fastOps; assert speedup == F : "Speedup should equal F=" + F + ", got " + speedup; System.out.println(" Test 4 PASS: O(K*F)=" + actualSlowOps + " vs O(K)=" + fastOps + " speedup=" + speedup + "x"); passed++; } System.out.println(passed + "/" + total + " PASS"); if (passed != total) System.exit(1); } }