From 823744c0b27f670c745e4f09e8c32e343c1794e3 Mon Sep 17 00:00:00 2001 From: Junggi Kim Date: Thu, 10 Sep 2026 17:59:59 +0900 Subject: [PATCH] Use a map to look up layer index child nodes LayersIndex.Node kept its children in a list and scanned that list linearly to find the child for each path segment. Building the index is therefore O(entries x siblings), a cost that is dominated by the largest flat directory in the jar: BOOT-INF/lib/ for the dependencies, and any bundled resource directory such as a front-end build output. Keep the children in a LinkedHashMap keyed by the segment name so that lookups are constant time. Insertion order is preserved, so the order in which buildIndex() walks the tree is unchanged. For a jar with 32,529 entries, 426 dependencies and a 12,000 file static resource directory, this reduces the number of string comparisons from 73.7M to 222K and the time spent building the index from 197ms to 12ms. For a jar with no large flat directory (20,529 entries, 426 dependencies) the gain is much smaller: 1.58M comparisons to 162K, and 12.5ms to 9.6ms. The generated layers.idx is byte for byte identical in both cases. See gh-51654 Signed-off-by: Junggi Kim --- .../boot/loader/tools/LayersIndex.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/loader/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayersIndex.java b/loader/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayersIndex.java index f2e6bd770cf..7926f5bf4c4 100644 --- a/loader/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayersIndex.java +++ b/loader/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/LayersIndex.java @@ -21,11 +21,12 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import org.springframework.util.LinkedMultiValueMap; @@ -47,6 +48,7 @@ import org.springframework.util.MultiValueMap; * @author Madhura Bhave * @author Andy Wilkinson * @author Phillip Webb + * @author Junggi Kim * @since 2.3.0 */ public class LayersIndex { @@ -116,28 +118,21 @@ public class LayersIndex { private final Set layers; - private final List children = new ArrayList<>(); + private final Map children = new LinkedHashMap<>(); Node() { - this.name = ""; - this.layers = new HashSet<>(); + this(""); } - Node(String name, Layer layer) { + Node(String name) { this.name = name; - this.layers = new HashSet<>(Collections.singleton(layer)); + this.layers = new HashSet<>(); } Node updateOrAddNode(String segment, boolean isDirectory, Layer layer) { String name = segment + (isDirectory ? "/" : ""); - for (Node child : this.children) { - if (name.equals(child.name)) { - child.layers.add(layer); - return child; - } - } - Node child = new Node(name, layer); - this.children.add(child); + Node child = this.children.computeIfAbsent(name, Node::new); + child.layers.add(layer); return child; } @@ -147,7 +142,7 @@ public class LayersIndex { index.add(this.layers.iterator().next(), name); } else { - for (Node child : this.children) { + for (Node child : this.children.values()) { child.buildIndex(name, index); } }