Skip to content

Commit dbef0ec

Browse files
Ian GravesStuart Marks
Ian Graves
authored and
Stuart Marks
committed
6323374: (coll) Optimize Collections.unmodifiable* and synchronized*
Reviewed-by: redestad, smarks, darcy
1 parent ee09bad commit dbef0ec

File tree

2 files changed

+146
-1
lines changed

2 files changed

+146
-1
lines changed

src/java.base/share/classes/java/util/Collections.java

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1008,12 +1008,17 @@ public static int lastIndexOfSubList(List<?> source, List<?> target) {
10081008
* The returned collection will be serializable if the specified collection
10091009
* is serializable.
10101010
*
1011+
* @implNote This method may return its argument if the argument is already unmodifiable.
10111012
* @param <T> the class of the objects in the collection
10121013
* @param c the collection for which an unmodifiable view is to be
10131014
* returned.
10141015
* @return an unmodifiable view of the specified collection.
10151016
*/
1017+
@SuppressWarnings("unchecked")
10161018
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
1019+
if (c.getClass() == UnmodifiableCollection.class) {
1020+
return (Collection<T>) c;
1021+
}
10171022
return new UnmodifiableCollection<>(c);
10181023
}
10191024

@@ -1116,11 +1121,17 @@ public Stream<E> parallelStream() {
11161121
* The returned set will be serializable if the specified set
11171122
* is serializable.
11181123
*
1124+
* @implNote This method may return its argument if the argument is already unmodifiable.
11191125
* @param <T> the class of the objects in the set
11201126
* @param s the set for which an unmodifiable view is to be returned.
11211127
* @return an unmodifiable view of the specified set.
11221128
*/
1129+
@SuppressWarnings("unchecked")
11231130
public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
1131+
// Not checking for subclasses because of heap pollution and information leakage.
1132+
if (s.getClass() == UnmodifiableSet.class) {
1133+
return (Set<T>) s;
1134+
}
11241135
return new UnmodifiableSet<>(s);
11251136
}
11261137

@@ -1148,12 +1159,17 @@ static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
11481159
* The returned sorted set will be serializable if the specified sorted set
11491160
* is serializable.
11501161
*
1162+
* @implNote This method may return its argument if the argument is already unmodifiable.
11511163
* @param <T> the class of the objects in the set
11521164
* @param s the sorted set for which an unmodifiable view is to be
11531165
* returned.
11541166
* @return an unmodifiable view of the specified sorted set.
11551167
*/
11561168
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s) {
1169+
// Not checking for subclasses because of heap pollution and information leakage.
1170+
if (s.getClass() == UnmodifiableSortedSet.class) {
1171+
return s;
1172+
}
11571173
return new UnmodifiableSortedSet<>(s);
11581174
}
11591175

@@ -1197,13 +1213,17 @@ public SortedSet<E> tailSet(E fromElement) {
11971213
* The returned navigable set will be serializable if the specified
11981214
* navigable set is serializable.
11991215
*
1216+
* @implNote This method may return its argument if the argument is already unmodifiable.
12001217
* @param <T> the class of the objects in the set
12011218
* @param s the navigable set for which an unmodifiable view is to be
12021219
* returned
12031220
* @return an unmodifiable view of the specified navigable set
12041221
* @since 1.8
12051222
*/
12061223
public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s) {
1224+
if (s.getClass() == UnmodifiableNavigableSet.class) {
1225+
return s;
1226+
}
12071227
return new UnmodifiableNavigableSet<>(s);
12081228
}
12091229

@@ -1289,11 +1309,17 @@ public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
12891309
* is serializable. Similarly, the returned list will implement
12901310
* {@link RandomAccess} if the specified list does.
12911311
*
1312+
* @implNote This method may return its argument if the argument is already unmodifiable.
12921313
* @param <T> the class of the objects in the list
12931314
* @param list the list for which an unmodifiable view is to be returned.
12941315
* @return an unmodifiable view of the specified list.
12951316
*/
1317+
@SuppressWarnings("unchecked")
12961318
public static <T> List<T> unmodifiableList(List<? extends T> list) {
1319+
if (list.getClass() == UnmodifiableList.class || list.getClass() == UnmodifiableRandomAccessList.class) {
1320+
return (List<T>) list;
1321+
}
1322+
12971323
return (list instanceof RandomAccess ?
12981324
new UnmodifiableRandomAccessList<>(list) :
12991325
new UnmodifiableList<>(list));
@@ -1438,12 +1464,18 @@ private Object writeReplace() {
14381464
* The returned map will be serializable if the specified map
14391465
* is serializable.
14401466
*
1467+
* @implNote This method may return its argument if the argument is already unmodifiable.
14411468
* @param <K> the class of the map keys
14421469
* @param <V> the class of the map values
14431470
* @param m the map for which an unmodifiable view is to be returned.
14441471
* @return an unmodifiable view of the specified map.
14451472
*/
1473+
@SuppressWarnings("unchecked")
14461474
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
1475+
// Not checking for subclasses because of heap pollution and information leakage.
1476+
if (m.getClass() == UnmodifiableMap.class) {
1477+
return (Map<K,V>) m;
1478+
}
14471479
return new UnmodifiableMap<>(m);
14481480
}
14491481

@@ -1795,13 +1827,19 @@ public boolean equals(Object o) {
17951827
* The returned sorted map will be serializable if the specified sorted map
17961828
* is serializable.
17971829
*
1830+
* @implNote This method may return its argument if the argument is already unmodifiable.
17981831
* @param <K> the class of the map keys
17991832
* @param <V> the class of the map values
18001833
* @param m the sorted map for which an unmodifiable view is to be
18011834
* returned.
18021835
* @return an unmodifiable view of the specified sorted map.
18031836
*/
1837+
@SuppressWarnings("unchecked")
18041838
public static <K,V> SortedMap<K,V> unmodifiableSortedMap(SortedMap<K, ? extends V> m) {
1839+
// Not checking for subclasses because of heap pollution and information leakage.
1840+
if (m.getClass() == UnmodifiableSortedMap.class) {
1841+
return (SortedMap<K,V>) m;
1842+
}
18051843
return new UnmodifiableSortedMap<>(m);
18061844
}
18071845

@@ -1840,14 +1878,19 @@ public SortedMap<K,V> tailMap(K fromKey)
18401878
* The returned navigable map will be serializable if the specified
18411879
* navigable map is serializable.
18421880
*
1881+
* @implNote This method may return its argument if the argument is already unmodifiable.
18431882
* @param <K> the class of the map keys
18441883
* @param <V> the class of the map values
18451884
* @param m the navigable map for which an unmodifiable view is to be
18461885
* returned
18471886
* @return an unmodifiable view of the specified navigable map
18481887
* @since 1.8
18491888
*/
1889+
@SuppressWarnings("unchecked")
18501890
public static <K,V> NavigableMap<K,V> unmodifiableNavigableMap(NavigableMap<K, ? extends V> m) {
1891+
if (m.getClass() == UnmodifiableNavigableMap.class) {
1892+
return (NavigableMap<K,V>) m;
1893+
}
18511894
return new UnmodifiableNavigableMap<>(m);
18521895
}
18531896

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 6323374
27+
* @run testng WrappedUnmodifiableCollections
28+
*/
29+
30+
import java.util.*;
31+
import java.util.function.Function;
32+
import org.testng.annotations.Test;
33+
import static org.testng.Assert.*;
34+
35+
36+
@Test
37+
public class WrappedUnmodifiableCollections {
38+
39+
private static <T,E extends T> void testWrapping(T collection, Function<T,E> wrapper) {
40+
var collection1 = wrapper.apply(collection);
41+
var collection2 = wrapper.apply(collection1);
42+
assertNotSame(collection, collection2);
43+
assertSame(collection1, collection2);
44+
}
45+
46+
public void testUnmodifiableListsDontWrap() {
47+
List<List<?>> lists = List.of(List.of(), List.of(1,2,3), List.of(1),
48+
List.of(1,2,3,4,5,6),
49+
List.of(1,2,3).subList(0,1),
50+
new LinkedList<>(List.of(1,2,3)),
51+
new ArrayList<>(List.of(1,2,3)));
52+
53+
for(List<?> list : lists) {
54+
testWrapping(list, Collections::unmodifiableList);
55+
}
56+
}
57+
58+
public void testUnmodifiableCollectionsDontWrap() {
59+
Collection<?> list = List.of();
60+
testWrapping(list, Collections::unmodifiableCollection);
61+
}
62+
63+
public void testUnmodifiableSetsDontWrap() {
64+
65+
List<Set<?>> sets = List.of(new TreeSet<>(),
66+
Set.of(1, 2),
67+
Set.of(1,2,3,4,5,6));
68+
69+
for (Set<?> set : sets) {
70+
testWrapping(set, Collections::unmodifiableSet);
71+
}
72+
73+
TreeSet<?> treeSet = new TreeSet<>();
74+
75+
//Collections.UnmodifiableSortedSet
76+
testWrapping((SortedSet<?>) treeSet, Collections::unmodifiableSortedSet);
77+
78+
//Collections.UnmodifiableNavigableSet
79+
testWrapping((NavigableSet<?>) treeSet, Collections::unmodifiableNavigableSet);
80+
81+
}
82+
83+
public void testUnmodifiableMapsDontWrap() {
84+
TreeMap<?,?> treeMap = new TreeMap<>();
85+
86+
List<Map<?,?>> maps = List.of(treeMap,
87+
Map.of(1,1),
88+
Map.of(1, 1, 2, 2, 3, 3, 4, 4));
89+
90+
for (Map<?,?> map : maps) {
91+
testWrapping(map, Collections::unmodifiableMap);
92+
}
93+
94+
//Collections.UnModifiableSortedMap
95+
testWrapping((SortedMap<?,?>) treeMap, Collections::unmodifiableSortedMap);
96+
97+
//Collections.UnModifiableNavigableMap
98+
testWrapping((NavigableMap<?,?>) treeMap, Collections::unmodifiableNavigableMap);
99+
100+
}
101+
102+
}

0 commit comments

Comments
 (0)