Skip to content

Commit 589060d

Browse files
committed
Avoid LinkedList performance issues through use of ArrayDeque
Closes gh-25652
1 parent 60fa704 commit 589060d

File tree

3 files changed

+34
-28
lines changed

3 files changed

+34
-28
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package org.springframework.beans.factory.parsing;
1818

19-
import java.util.LinkedList;
19+
import java.util.ArrayDeque;
2020

2121
import org.springframework.lang.Nullable;
2222

2323
/**
24-
* Simple {@link LinkedList}-based structure for tracking the logical position during
24+
* Simple {@link ArrayDeque}-based structure for tracking the logical position during
2525
* a parsing process. {@link Entry entries} are added to the LinkedList at
2626
* each point during the parse phase in a reader-specific manner.
2727
*
@@ -30,6 +30,7 @@
3030
* error messages.
3131
*
3232
* @author Rob Harrop
33+
* @author Juergen Hoeller
3334
* @since 2.0
3435
*/
3536
public final class ParseState {
@@ -40,45 +41,44 @@ public final class ParseState {
4041
private static final char TAB = '\t';
4142

4243
/**
43-
* Internal {@link LinkedList} storage.
44+
* Internal {@link ArrayDeque} storage.
4445
*/
45-
private final LinkedList<Entry> state;
46+
private final ArrayDeque<Entry> state;
4647

4748

4849
/**
49-
* Create a new {@code ParseState} with an empty {@link LinkedList}.
50+
* Create a new {@code ParseState} with an empty {@link ArrayDeque}.
5051
*/
5152
public ParseState() {
52-
this.state = new LinkedList<>();
53+
this.state = new ArrayDeque<>();
5354
}
5455

5556
/**
56-
* Create a new {@code ParseState} whose {@link LinkedList} is a {@link Object#clone clone}
57+
* Create a new {@code ParseState} whose {@link ArrayDeque} is a {@link Object#clone clone}
5758
* of that of the passed in {@code ParseState}.
5859
*/
59-
@SuppressWarnings("unchecked")
6060
private ParseState(ParseState other) {
61-
this.state = (LinkedList<Entry>) other.state.clone();
61+
this.state = other.state.clone();
6262
}
6363

6464

6565
/**
66-
* Add a new {@link Entry} to the {@link LinkedList}.
66+
* Add a new {@link Entry} to the {@link ArrayDeque}.
6767
*/
6868
public void push(Entry entry) {
6969
this.state.push(entry);
7070
}
7171

7272
/**
73-
* Remove an {@link Entry} from the {@link LinkedList}.
73+
* Remove an {@link Entry} from the {@link ArrayDeque}.
7474
*/
7575
public void pop() {
7676
this.state.pop();
7777
}
7878

7979
/**
80-
* Return the {@link Entry} currently at the top of the {@link LinkedList} or
81-
* {@code null} if the {@link LinkedList} is empty.
80+
* Return the {@link Entry} currently at the top of the {@link ArrayDeque} or
81+
* {@code null} if the {@link ArrayDeque} is empty.
8282
*/
8383
@Nullable
8484
public Entry peek() {
@@ -100,15 +100,17 @@ public ParseState snapshot() {
100100
@Override
101101
public String toString() {
102102
StringBuilder sb = new StringBuilder();
103-
for (int x = 0; x < this.state.size(); x++) {
104-
if (x > 0) {
103+
int i = 0;
104+
for (ParseState.Entry entry : this.state) {
105+
if (i > 0) {
105106
sb.append('\n');
106-
for (int y = 0; y < x; y++) {
107+
for (int j = 0; j < i; j++) {
107108
sb.append(TAB);
108109
}
109110
sb.append("-> ");
110111
}
111-
sb.append(this.state.get(x));
112+
sb.append(entry);
113+
i++;
112114
}
113115
return sb.toString();
114116
}
@@ -118,7 +120,6 @@ public String toString() {
118120
* Marker interface for entries into the {@link ParseState}.
119121
*/
120122
public interface Entry {
121-
122123
}
123124

124125
}

spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,8 +20,9 @@
2020
import java.io.InputStream;
2121
import java.io.OutputStream;
2222
import java.security.MessageDigest;
23+
import java.util.ArrayDeque;
24+
import java.util.Deque;
2325
import java.util.Iterator;
24-
import java.util.LinkedList;
2526

2627
import org.springframework.lang.Nullable;
2728

@@ -31,7 +32,7 @@
3132
* its sibling {@link ResizableByteArrayOutputStream}.
3233
*
3334
* <p>Unlike {@link java.io.ByteArrayOutputStream}, this implementation is backed
34-
* by a {@link java.util.LinkedList} of {@code byte[]} instead of 1 constantly
35+
* by an {@link java.util.ArrayDeque} of {@code byte[]} instead of 1 constantly
3536
* resizing {@code byte[]}. It does not copy buffers when it gets expanded.
3637
*
3738
* <p>The initial buffer is only created when the stream is first written.
@@ -50,7 +51,7 @@ public class FastByteArrayOutputStream extends OutputStream {
5051

5152

5253
// The buffers used to store the content bytes
53-
private final LinkedList<byte[]> buffers = new LinkedList<>();
54+
private final Deque<byte[]> buffers = new ArrayDeque<>();
5455

5556
// The size, in bytes, to use when allocating the first byte[]
5657
private final int initialBlockSize;
@@ -289,7 +290,7 @@ else if (size() == targetCapacity && this.buffers.getFirst().length == targetCap
289290
}
290291

291292
/**
292-
* Create a new buffer and store it in the LinkedList
293+
* Create a new buffer and store it in the ArrayDeque.
293294
* <p>Adds a new buffer that can store at least {@code minCapacity} bytes.
294295
*/
295296
private void addBuffer(int minCapacity) {

spring-core/src/main/java/org/springframework/util/StringUtils.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818

1919
import java.io.ByteArrayOutputStream;
2020
import java.nio.charset.Charset;
21+
import java.util.ArrayDeque;
2122
import java.util.ArrayList;
2223
import java.util.Arrays;
2324
import java.util.Collection;
2425
import java.util.Collections;
26+
import java.util.Deque;
2527
import java.util.Enumeration;
2628
import java.util.Iterator;
2729
import java.util.LinkedHashSet;
28-
import java.util.LinkedList;
2930
import java.util.List;
3031
import java.util.Locale;
3132
import java.util.Properties;
@@ -655,6 +656,9 @@ public static String applyRelativePath(String path, String relativePath) {
655656
* inner simple dots.
656657
* <p>The result is convenient for path comparison. For other uses,
657658
* notice that Windows separators ("\") are replaced by simple slashes.
659+
* <p><strong>NOTE</strong> that {@code cleanPath} should not be depended
660+
* upon in a security context. Other mechanisms should be used to prevent
661+
* path-traversal issues.
658662
* @param path the original path
659663
* @return the normalized path
660664
*/
@@ -690,7 +694,7 @@ public static String cleanPath(String path) {
690694
}
691695

692696
String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
693-
LinkedList<String> pathElements = new LinkedList<>();
697+
Deque<String> pathElements = new ArrayDeque<>();
694698
int tops = 0;
695699

696700
for (int i = pathArray.length - 1; i >= 0; i--) {
@@ -709,7 +713,7 @@ else if (TOP_PATH.equals(element)) {
709713
}
710714
else {
711715
// Normal path element found.
712-
pathElements.add(0, element);
716+
pathElements.addFirst(element);
713717
}
714718
}
715719
}
@@ -720,11 +724,11 @@ else if (TOP_PATH.equals(element)) {
720724
}
721725
// Remaining top paths need to be retained.
722726
for (int i = 0; i < tops; i++) {
723-
pathElements.add(0, TOP_PATH);
727+
pathElements.addFirst(TOP_PATH);
724728
}
725729
// If nothing else left, at least explicitly point to current path.
726730
if (pathElements.size() == 1 && pathElements.getLast().isEmpty() && !prefix.endsWith(FOLDER_SEPARATOR)) {
727-
pathElements.add(0, CURRENT_PATH);
731+
pathElements.addFirst(CURRENT_PATH);
728732
}
729733

730734
return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);

0 commit comments

Comments
 (0)