8198484: URLClassPath should use an ArrayDeque instead of a Stack

Reviewed-by: alanb, mchung, plevart, psandoz
This commit is contained in:
Martin Buchholz 2018-02-08 17:25:57 -08:00
parent 541978b4a2
commit 80e322bbcc

View file

@ -46,6 +46,7 @@ import java.security.Permission;
import java.security.PrivilegedActionException; import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -57,7 +58,6 @@ import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -101,10 +101,10 @@ public class URLClassPath {
} }
/* The original search path of URLs. */ /* The original search path of URLs. */
private final List<URL> path; private final ArrayList<URL> path;
/* The stack of unopened URLs */ /* The deque of unopened URLs */
private final Stack<URL> unopenedUrls = new Stack<>(); private final ArrayDeque<URL> unopenedUrls;
/* The resulting search path of Loaders */ /* The resulting search path of Loaders */
private final ArrayList<Loader> loaders = new ArrayList<>(); private final ArrayList<Loader> loaders = new ArrayList<>();
@ -138,12 +138,15 @@ public class URLClassPath {
public URLClassPath(URL[] urls, public URLClassPath(URL[] urls,
URLStreamHandlerFactory factory, URLStreamHandlerFactory factory,
AccessControlContext acc) { AccessControlContext acc) {
List<URL> path = new ArrayList<>(urls.length); ArrayList<URL> path = new ArrayList<>(urls.length);
ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(urls.length);
for (URL url : urls) { for (URL url : urls) {
path.add(url); path.add(url);
unopenedUrls.add(url);
} }
this.path = path; this.path = path;
push(urls); this.unopenedUrls = unopenedUrls;
if (factory != null) { if (factory != null) {
jarHandler = factory.createURLStreamHandler("jar"); jarHandler = factory.createURLStreamHandler("jar");
} else { } else {
@ -169,7 +172,7 @@ public class URLClassPath {
* @apiNote Used to create the application class path. * @apiNote Used to create the application class path.
*/ */
URLClassPath(String cp, boolean skipEmptyElements) { URLClassPath(String cp, boolean skipEmptyElements) {
List<URL> path = new ArrayList<>(); ArrayList<URL> path = new ArrayList<>();
if (cp != null) { if (cp != null) {
// map each element of class path to a file URL // map each element of class path to a file URL
int off = 0; int off = 0;
@ -189,13 +192,16 @@ public class URLClassPath {
URL url = toFileURL(element); URL url = toFileURL(element);
if (url != null) path.add(url); if (url != null) path.add(url);
} }
// push the URLs
for (int i = path.size() - 1; i >= 0; --i) {
unopenedUrls.push(path.get(i));
}
} }
// can't use ArrayDeque#addAll or new ArrayDeque(Collection);
// it's too early in the bootstrap to trigger use of lambdas
int size = path.size();
ArrayDeque<URL> unopenedUrls = new ArrayDeque<>(size);
for (int i = 0; i < size; i++)
unopenedUrls.add(path.get(i));
this.unopenedUrls = unopenedUrls;
this.path = path; this.path = path;
this.jarHandler = null; this.jarHandler = null;
this.acc = null; this.acc = null;
@ -225,14 +231,13 @@ public class URLClassPath {
* URLs, then invoking this method has no effect. * URLs, then invoking this method has no effect.
*/ */
public synchronized void addURL(URL url) { public synchronized void addURL(URL url) {
if (closed) if (closed || url == null)
return; return;
synchronized (unopenedUrls) { synchronized (unopenedUrls) {
if (url == null || path.contains(url)) if (! path.contains(url)) {
return; unopenedUrls.addLast(url);
path.add(url);
unopenedUrls.add(0, url); }
path.add(url);
} }
} }
@ -413,17 +418,14 @@ public class URLClassPath {
if (closed) { if (closed) {
return null; return null;
} }
// Expand URL search path until the request can be satisfied // Expand URL search path until the request can be satisfied
// or the URL stack is empty. // or unopenedUrls is exhausted.
while (loaders.size() < index + 1) { while (loaders.size() < index + 1) {
// Pop the next URL from the URL stack final URL url;
URL url;
synchronized (unopenedUrls) { synchronized (unopenedUrls) {
if (unopenedUrls.empty()) { url = unopenedUrls.pollFirst();
if (url == null)
return null; return null;
} else {
url = unopenedUrls.pop();
}
} }
// Skip this URL if it already has a Loader. (Loader // Skip this URL if it already has a Loader. (Loader
// may be null in the case where URL has not been opened // may be null in the case where URL has not been opened
@ -437,7 +439,7 @@ public class URLClassPath {
try { try {
loader = getLoader(url); loader = getLoader(url);
// If the loader defines a local class path then add the // If the loader defines a local class path then add the
// URLs to the list of URLs to be opened. // URLs as the next URLs to be opened.
URL[] urls = loader.getClassPath(); URL[] urls = loader.getClassPath();
if (urls != null) { if (urls != null) {
push(urls); push(urls);
@ -502,12 +504,12 @@ public class URLClassPath {
} }
/* /*
* Pushes the specified URLs onto the list of unopened URLs. * Pushes the specified URLs onto the head of unopened URLs.
*/ */
private void push(URL[] urls) { private void push(URL[] urls) {
synchronized (unopenedUrls) { synchronized (unopenedUrls) {
for (int i = urls.length - 1; i >= 0; --i) { for (int i = urls.length - 1; i >= 0; --i) {
unopenedUrls.push(urls[i]); unopenedUrls.addFirst(urls[i]);
} }
} }
} }