Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 128f768

Browse files
perf: replace String[]/parseDouble Dijkstra PQ entries with typed DijkstraEntry
The Dijkstra priority queue stored entries as String[]{vertex, String.valueOf(dist)} and recovered distances via Double.parseDouble() on every poll and comparison. This caused: 1. Extra String allocation per enqueue (String.valueOf on double) 2. O(L) parseDouble on every PQ comparison instead of O(1) Double.compare 3. Unnecessary GC pressure from short-lived String[] + String objects Now uses a typed DijkstraEntry with primitive double field — eliminates all string conversions and reduces comparison cost to a single double compare. Affects all path operations: K-shortest, constrained, avoidance routing.
1 parent 36cf0bc commit 128f768

1 file changed

Lines changed: 36 additions & 7 deletions

File tree

Gvisual/src/gvisual/GraphPathExplorer.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -633,24 +633,53 @@ public String generateReport(String source, String target) {
633633

634634
// ── Internal helpers ──────────────────────────────────────────
635635

636+
/** Lightweight pair for the Dijkstra priority queue — avoids the
637+
* String.valueOf / Double.parseDouble round-trip that the old
638+
* String[] approach incurred on every enqueue and dequeue. */
639+
private static final class DijkstraEntry implements Comparable<DijkstraEntry> {
640+
final String vertex;
641+
final double dist;
642+
DijkstraEntry(String vertex, double dist) {
643+
this.vertex = vertex;
644+
this.dist = dist;
645+
}
646+
@Override public int compareTo(DijkstraEntry o) {
647+
return Double.compare(this.dist, o.dist);
648+
}
649+
}
650+
636651
/**
637652
* Dijkstra shortest path with optional node/Edge exclusions.
653+
*
654+
* <p><b>Performance note:</b> Previously stored priority-queue entries
655+
* as {@code String[]{vertex, String.valueOf(dist)}} and recovered the
656+
* distance via {@code Double.parseDouble} on every poll/comparison.
657+
* This caused two performance problems:</p>
658+
* <ol>
659+
* <li>Every enqueue allocated a String[] plus a String from
660+
* {@code String.valueOf(double)} — significant GC pressure
661+
* on graphs with many relaxation steps.</li>
662+
* <li>Every priority-queue comparison invoked
663+
* {@code Double.parseDouble} — an O(L) parse per comparison
664+
* instead of a single {@code Double.compare}.</li>
665+
* </ol>
666+
* <p>Now uses a typed {@link DijkstraEntry} record with a primitive
667+
* {@code double} field, eliminating all string conversions.</p>
638668
*/
639669
private Path dijkstra(String source, String target,
640670
Set<String> excludeNodes,
641671
Set<String> excludeEdges) {
642672
Map<String, Double> dist = new HashMap<>();
643673
Map<String, String> prev = new HashMap<>();
644-
PriorityQueue<String[]> pq = new PriorityQueue<>(
645-
Comparator.comparingDouble(a -> Double.parseDouble(a[1])));
674+
PriorityQueue<DijkstraEntry> pq = new PriorityQueue<>();
646675

647676
dist.put(source, 0.0);
648-
pq.add(new String[]{source, "0"});
677+
pq.add(new DijkstraEntry(source, 0.0));
649678

650679
while (!pq.isEmpty()) {
651-
String[] entry = pq.poll();
652-
String u = entry[0];
653-
double d = Double.parseDouble(entry[1]);
680+
DijkstraEntry entry = pq.poll();
681+
String u = entry.vertex;
682+
double d = entry.dist;
654683

655684
if (d > dist.getOrDefault(u, Double.MAX_VALUE)) continue;
656685
if (u.equals(target)) break;
@@ -667,7 +696,7 @@ private Path dijkstra(String source, String target,
667696
if (newDist < dist.getOrDefault(v, Double.MAX_VALUE)) {
668697
dist.put(v, newDist);
669698
prev.put(v, u);
670-
pq.add(new String[]{v, String.valueOf(newDist)});
699+
pq.add(new DijkstraEntry(v, newDist));
671700
}
672701
}
673702
}

0 commit comments

Comments
 (0)