Appendix-Collection-Topics
附录: 集合主题
本附录是一些比第十二章 集合中介绍的更高级的内容。
示例数据
这里创建一些样本数据用于集合示例。 以下数据将颜色名称与
// onjava/HTMLColors.java
// Sample data for collection examples
package onjava;
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
public class HTMLColors {
public static final Object[][] ARRAY = {
{ 0xF0F8FF, "AliceBlue" },
{ 0xFAEBD7, "AntiqueWhite" },
{ 0x7FFFD4, "Aquamarine" },
{ 0xF0FFFF, "Azure" },
{ 0xF5F5DC, "Beige" },
{ 0xFFE4C4, "Bisque" },
{ 0x000000, "Black" },
{ 0xFFEBCD, "BlanchedAlmond" },
{ 0x0000FF, "Blue" },
{ 0x8A2BE2, "BlueViolet" },
{ 0xA52A2A, "Brown" },
{ 0xDEB887, "BurlyWood" },
{ 0x5F9EA0, "CadetBlue" },
{ 0x7FFF00, "Chartreuse" },
{ 0xD2691E, "Chocolate" },
{ 0xFF7F50, "Coral" },
{ 0x6495ED, "CornflowerBlue" },
{ 0xFFF8DC, "Cornsilk" },
{ 0xDC143C, "Crimson" },
{ 0x00FFFF, "Cyan" },
{ 0x00008B, "DarkBlue" },
{ 0x008B8B, "DarkCyan" },
{ 0xB8860B, "DarkGoldenRod" },
{ 0xA9A9A9, "DarkGray" },
{ 0x006400, "DarkGreen" },
{ 0xBDB76B, "DarkKhaki" },
{ 0x8B008B, "DarkMagenta" },
{ 0x556B2F, "DarkOliveGreen" },
{ 0xFF8C00, "DarkOrange" },
{ 0x9932CC, "DarkOrchid" },
{ 0x8B0000, "DarkRed" },
{ 0xE9967A, "DarkSalmon" },
{ 0x8FBC8F, "DarkSeaGreen" },
{ 0x483D8B, "DarkSlateBlue" },
{ 0x2F4F4F, "DarkSlateGray" },
{ 0x00CED1, "DarkTurquoise" },
{ 0x9400D3, "DarkViolet" },
{ 0xFF1493, "DeepPink" },
{ 0x00BFFF, "DeepSkyBlue" },
{ 0x696969, "DimGray" },
{ 0x1E90FF, "DodgerBlue" },
{ 0xB22222, "FireBrick" },
{ 0xFFFAF0, "FloralWhite" },
{ 0x228B22, "ForestGreen" },
{ 0xDCDCDC, "Gainsboro" },
{ 0xF8F8FF, "GhostWhite" },
{ 0xFFD700, "Gold" },
{ 0xDAA520, "GoldenRod" },
{ 0x808080, "Gray" },
{ 0x008000, "Green" },
{ 0xADFF2F, "GreenYellow" },
{ 0xF0FFF0, "HoneyDew" },
{ 0xFF69B4, "HotPink" },
{ 0xCD5C5C, "IndianRed" },
{ 0x4B0082, "Indigo" },
{ 0xFFFFF0, "Ivory" },
{ 0xF0E68C, "Khaki" },
{ 0xE6E6FA, "Lavender" },
{ 0xFFF0F5, "LavenderBlush" },
{ 0x7CFC00, "LawnGreen" },
{ 0xFFFACD, "LemonChiffon" },
{ 0xADD8E6, "LightBlue" },
{ 0xF08080, "LightCoral" },
{ 0xE0FFFF, "LightCyan" },
{ 0xFAFAD2, "LightGoldenRodYellow" },
{ 0xD3D3D3, "LightGray" },
{ 0x90EE90, "LightGreen" },
{ 0xFFB6C1, "LightPink" },
{ 0xFFA07A, "LightSalmon" },
{ 0x20B2AA, "LightSeaGreen" },
{ 0x87CEFA, "LightSkyBlue" },
{ 0x778899, "LightSlateGray" },
{ 0xB0C4DE, "LightSteelBlue" },
{ 0xFFFFE0, "LightYellow" },
{ 0x00FF00, "Lime" },
{ 0x32CD32, "LimeGreen" },
{ 0xFAF0E6, "Linen" },
{ 0xFF00FF, "Magenta" },
{ 0x800000, "Maroon" },
{ 0x66CDAA, "MediumAquaMarine" },
{ 0x0000CD, "MediumBlue" },
{ 0xBA55D3, "MediumOrchid" },
{ 0x9370DB, "MediumPurple" },
{ 0x3CB371, "MediumSeaGreen" },
{ 0x7B68EE, "MediumSlateBlue" },
{ 0x00FA9A, "MediumSpringGreen" },
{ 0x48D1CC, "MediumTurquoise" },
{ 0xC71585, "MediumVioletRed" },
{ 0x191970, "MidnightBlue" },
{ 0xF5FFFA, "MintCream" },
{ 0xFFE4E1, "MistyRose" },
{ 0xFFE4B5, "Moccasin" },
{ 0xFFDEAD, "NavajoWhite" },
{ 0x000080, "Navy" },
{ 0xFDF5E6, "OldLace" },
{ 0x808000, "Olive" },
{ 0x6B8E23, "OliveDrab" },
{ 0xFFA500, "Orange" },
{ 0xFF4500, "OrangeRed" },
{ 0xDA70D6, "Orchid" },
{ 0xEEE8AA, "PaleGoldenRod" },
{ 0x98FB98, "PaleGreen" },
{ 0xAFEEEE, "PaleTurquoise" },
{ 0xDB7093, "PaleVioletRed" },
{ 0xFFEFD5, "PapayaWhip" },
{ 0xFFDAB9, "PeachPuff" },
{ 0xCD853F, "Peru" },
{ 0xFFC0CB, "Pink" },
{ 0xDDA0DD, "Plum" },
{ 0xB0E0E6, "PowderBlue" },
{ 0x800080, "Purple" },
{ 0xFF0000, "Red" },
{ 0xBC8F8F, "RosyBrown" },
{ 0x4169E1, "RoyalBlue" },
{ 0x8B4513, "SaddleBrown" },
{ 0xFA8072, "Salmon" },
{ 0xF4A460, "SandyBrown" },
{ 0x2E8B57, "SeaGreen" },
{ 0xFFF5EE, "SeaShell" },
{ 0xA0522D, "Sienna" },
{ 0xC0C0C0, "Silver" },
{ 0x87CEEB, "SkyBlue" },
{ 0x6A5ACD, "SlateBlue" },
{ 0x708090, "SlateGray" },
{ 0xFFFAFA, "Snow" },
{ 0x00FF7F, "SpringGreen" },
{ 0x4682B4, "SteelBlue" },
{ 0xD2B48C, "Tan" },
{ 0x008080, "Teal" },
{ 0xD8BFD8, "Thistle" },
{ 0xFF6347, "Tomato" },
{ 0x40E0D0, "Turquoise" },
{ 0xEE82EE, "Violet" },
{ 0xF5DEB3, "Wheat" },
{ 0xFFFFFF, "White" },
{ 0xF5F5F5, "WhiteSmoke" },
{ 0xFFFF00, "Yellow" },
{ 0x9ACD32, "YellowGreen" },
};
public static final Map<Integer,String> MAP =
Arrays.stream(ARRAY)
.collect(Collectors.toMap(
element -> (Integer)element[0],
element -> (String)element[1],
(v1, v2) -> { // Merge function
throw new IllegalStateException();
},
LinkedHashMap::new
));
// Inversion only works if values are unique:
public static <V, K> Map<V, K>
invert(Map<K, V> map) {
return map.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey,
(v1, v2) -> {
throw new IllegalStateException();
},
LinkedHashMap::new
));
}
public static final Map<String,Integer>
INVMAP = invert(MAP);
// Look up RGB value given a name:
public static Integer rgb(String colorName) {
return INVMAP.get(colorName);
}
public static final List<String> LIST =
Arrays.stream(ARRAY)
.map(item -> (String)item[1])
.collect(Collectors.toList());
public static final List<Integer> RGBLIST =
Arrays.stream(ARRAY)
.map(item -> (Integer)item[0])
.collect(Collectors.toList());
public static
void show(Map.Entry<Integer,String> e) {
System.out.format(
"0x%06X: %s%n", e.getKey(), e.getValue());
}
public static void
show(Map<Integer,String> m, int count) {
m.entrySet().stream()
.limit(count)
.forEach(e -> show(e));
}
public static void show(Map<Integer,String> m) {
show(m, m.size());
}
public static
void show(Collection<String> lst, int count) {
lst.stream()
.limit(count)
.forEach(System.out::println);
}
public static void show(Collection<String> lst) {
show(lst, lst.size());
}
public static
void showrgb(Collection<Integer> lst, int count) {
lst.stream()
.limit(count)
.forEach(n -> System.out.format("0x%06X%n", n));
}
public static void showrgb(Collection<Integer> lst) {
showrgb(lst, lst.size());
}
public static
void showInv(Map<String,Integer> m, int count) {
m.entrySet().stream()
.limit(count)
.forEach(e ->
System.out.format(
"%-20s 0x%06X%n", e.getKey(), e.getValue()));
}
public static void showInv(Map<String,Integer> m) {
showInv(m, m.size());
}
public static void border() {
System.out.println(
"******************************");
}
}
Collectors.toMap()
。 那个版本生成一个Collectors.toMap()
版本。这需要两个函数从每个流元素中提取键和值,就像简单版本的Collectors.toMap()
一样。 然后它需要一个合并函数(merge function
rgb()
方法是一个便捷函数(convenience functioninvert()
方法实现的,如果任何
我们还创建包含所有名称的
第一个 show()
方法接受一个
这里是一个基本的测试:
// collectiontopics/HTMLColorTest.java
import static onjava.HTMLColors.*;
public class HTMLColorTest {
static final int DISPLAY_SIZE = 20;
public static void main(String[] args) {
show(MAP, DISPLAY_SIZE);
border();
showInv(INVMAP, DISPLAY_SIZE);
border();
show(LIST, DISPLAY_SIZE);
border();
showrgb(RGBLIST, DISPLAY_SIZE);
}
}
/* Output:
0xF0F8FF: AliceBlue
0xFAEBD7: AntiqueWhite
0x7FFFD4: Aquamarine
0xF0FFFF: Azure
0xF5F5DC: Beige
0xFFE4C4: Bisque
0x000000: Black
0xFFEBCD: BlanchedAlmond
0x0000FF: Blue
0x8A2BE2: BlueViolet
0xA52A2A: Brown
0xDEB887: BurlyWood
0x5F9EA0: CadetBlue
0x7FFF00: Chartreuse
0xD2691E: Chocolate
0xFF7F50: Coral
0x6495ED: CornflowerBlue
0xFFF8DC: Cornsilk
0xDC143C: Crimson
0x00FFFF: Cyan
******************************
AliceBlue 0xF0F8FF
AntiqueWhite 0xFAEBD7
Aquamarine 0x7FFFD4
Azure 0xF0FFFF
Beige 0xF5F5DC
Bisque 0xFFE4C4
Black 0x000000
BlanchedAlmond 0xFFEBCD
Blue 0x0000FF
BlueViolet 0x8A2BE2
Brown 0xA52A2A
BurlyWood 0xDEB887
CadetBlue 0x5F9EA0
Chartreuse 0x7FFF00
Chocolate 0xD2691E
Coral 0xFF7F50
CornflowerBlue 0x6495ED
Cornsilk 0xFFF8DC
Crimson 0xDC143C
Cyan 0x00FFFF
******************************
AliceBlue
AntiqueWhite
Aquamarine
Azure
Beige
Bisque
Black
BlanchedAlmond
Blue
BlueViolet
Brown
BurlyWood
CadetBlue
Chartreuse
Chocolate
Coral
CornflowerBlue
Cornsilk
Crimson
Cyan
******************************
0xF0F8FF
0xFAEBD7
0x7FFFD4
0xF0FFFF
0xF5F5DC
0xFFE4C4
0x000000
0xFFEBCD
0x0000FF
0x8A2BE2
0xA52A2A
0xDEB887
0x5F9EA0
0x7FFF00
0xD2691E
0xFF7F50
0x6495ED
0xFFF8DC
0xDC143C
0x00FFFF
*/
可以看到,使用
List 行为
add()
用于插入元素get()
用于随机访问元素iterator()
获取序列上的一个Iterator stream()
生成元素的一个Stream
列表构造方法始终保留元素的添加顺序。
以下示例中的方法各自涵盖了一组不同的行为:每个basicTest()
iterMotion()
)遍历序列,使用iterManipulation()
)更改内容,查看testVisual()
)的效果,以及仅可用于
// collectiontopics/ListOps.java
// Things you can do with Lists
import java.util.*;
import onjava.HTMLColors;
public class ListOps {
// Create a short list for testing:
static final List<String> LIST =
HTMLColors.LIST.subList(0, 10);
private static boolean b;
private static String s;
private static int i;
private static Iterator<String> it;
private static ListIterator<String> lit;
public static void basicTest(List<String> a) {
a.add(1, "x"); // Add at location 1
a.add("x"); // Add at end
// Add a collection:
a.addAll(LIST);
// Add a collection starting at location 3:
a.addAll(3, LIST);
b = a.contains("1"); // Is it in there?
// Is the entire collection in there?
b = a.containsAll(LIST);
// Lists allow random access, which is cheap
// for ArrayList, expensive for LinkedList:
s = a.get(1); // Get (typed) object at location 1
i = a.indexOf("1"); // Tell index of object
b = a.isEmpty(); // Any elements inside?
it = a.iterator(); // Ordinary Iterator
lit = a.listIterator(); // ListIterator
lit = a.listIterator(3); // Start at location 3
i = a.lastIndexOf("1"); // Last match
a.remove(1); // Remove location 1
a.remove("3"); // Remove this object
a.set(1, "y"); // Set location 1 to "y"
// Keep everything that's in the argument
// (the intersection of the two sets):
a.retainAll(LIST);
// Remove everything that's in the argument:
a.removeAll(LIST);
i = a.size(); // How big is it?
a.clear(); // Remove all elements
}
public static void iterMotion(List<String> a) {
ListIterator<String> it = a.listIterator();
b = it.hasNext();
b = it.hasPrevious();
s = it.next();
i = it.nextIndex();
s = it.previous();
i = it.previousIndex();
}
public static void iterManipulation(List<String> a) {
ListIterator<String> it = a.listIterator();
it.add("47");
// Must move to an element after add():
it.next();
// Remove the element after the new one:
it.remove();
// Must move to an element after remove():
it.next();
// Change the element after the deleted one:
it.set("47");
}
public static void testVisual(List<String> a) {
System.out.println(a);
List<String> b = LIST;
System.out.println("b = " + b);
a.addAll(b);
a.addAll(b);
System.out.println(a);
// Insert, remove, and replace elements
// using a ListIterator:
ListIterator<String> x =
a.listIterator(a.size()/2);
x.add("one");
System.out.println(a);
System.out.println(x.next());
x.remove();
System.out.println(x.next());
x.set("47");
System.out.println(a);
// Traverse the list backwards:
x = a.listIterator(a.size());
while(x.hasPrevious())
System.out.print(x.previous() + " ");
System.out.println();
System.out.println("testVisual finished");
}
// There are some things that only LinkedLists can do:
public static void testLinkedList() {
LinkedList<String> ll = new LinkedList<>();
ll.addAll(LIST);
System.out.println(ll);
// Treat it like a stack, pushing:
ll.addFirst("one");
ll.addFirst("two");
System.out.println(ll);
// Like "peeking" at the top of a stack:
System.out.println(ll.getFirst());
// Like popping a stack:
System.out.println(ll.removeFirst());
System.out.println(ll.removeFirst());
// Treat it like a queue, pulling elements
// off the tail end:
System.out.println(ll.removeLast());
System.out.println(ll);
}
public static void main(String[] args) {
// Make and fill a new list each time:
basicTest(new LinkedList<>(LIST));
basicTest(new ArrayList<>(LIST));
iterMotion(new LinkedList<>(LIST));
iterMotion(new ArrayList<>(LIST));
iterManipulation(new LinkedList<>(LIST));
iterManipulation(new ArrayList<>(LIST));
testVisual(new LinkedList<>(LIST));
testLinkedList();
}
}
/* Output:
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
b = [AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet,
AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet,
AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet,
AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige, one,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet,
AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
Bisque
Black
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet,
AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige, one,
47, BlanchedAlmond, Blue, BlueViolet, AliceBlue,
AntiqueWhite, Aquamarine, Azure, Beige, Bisque, Black,
BlanchedAlmond, Blue, BlueViolet]
BlueViolet Blue BlanchedAlmond Black Bisque Beige Azure
Aquamarine AntiqueWhite AliceBlue BlueViolet Blue
BlanchedAlmond 47 one Beige Azure Aquamarine
AntiqueWhite AliceBlue BlueViolet Blue BlanchedAlmond
Black Bisque Beige Azure Aquamarine AntiqueWhite
AliceBlue
testVisual finished
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
[two, one, AliceBlue, AntiqueWhite, Aquamarine, Azure,
Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet]
two
two
one
BlueViolet
[AliceBlue, AntiqueWhite, Aquamarine, Azure, Beige,
Bisque, Black, BlanchedAlmond, Blue]
*/
在 basicTest()
和 iterMotion()
中,方法调用是为了展示正确的语法,尽管获取了返回值,但不会使用它。在某些情况下,根本不会去获取返回值。在使用这些方法之前,请查看
Set 行为
其它的
// collectiontopics/SetOrder.java
import java.util.*;
import onjava.HTMLColors;
public class SetOrder {
static String[] sets = {
"java.util.HashSet",
"java.util.TreeSet",
"java.util.concurrent.ConcurrentSkipListSet",
"java.util.LinkedHashSet",
"java.util.concurrent.CopyOnWriteArraySet",
};
static final List<String> RLIST =
new ArrayList<>(HTMLColors.LIST);
static {
Collections.reverse(RLIST);
}
public static void
main(String[] args) throws Exception {
for(String type: sets) {
System.out.format("[-> %s <-]%n",
type.substring(type.lastIndexOf('.') + 1));
@SuppressWarnings("unchecked")
Set<String> set = (Set<String>)
Class.forName(type).newInstance();
set.addAll(RLIST);
set.stream()
.limit(10)
.forEach(System.out::println);
}
}
}
/* Output:
[-> HashSet <-]
MediumOrchid
PaleGoldenRod
Sienna
LightSlateGray
DarkSeaGreen
Black
Gainsboro
Orange
LightCoral
DodgerBlue
[-> TreeSet <-]
AliceBlue
AntiqueWhite
Aquamarine
Azure
Beige
Bisque
Black
BlanchedAlmond
Blue
BlueViolet
[-> ConcurrentSkipListSet <-]
AliceBlue
AntiqueWhite
Aquamarine
Azure
Beige
Bisque
Black
BlanchedAlmond
Blue
BlueViolet
[-> LinkedHashSet <-]
YellowGreen
Yellow
WhiteSmoke
White
Wheat
Violet
Turquoise
Tomato
Thistle
Teal
[-> CopyOnWriteArraySet <-]
YellowGreen
Yellow
WhiteSmoke
White
Wheat
Violet
Turquoise
Tomato
Thistle
Teal
*/
这里需要使用Class.forName(type).newInstance()
。编译器并不能保证这是一次成功的操作。
Collections.reverse()
是通过修改参数来执行反向操作,而不是返回包含反向元素的新
在附录的最后,我们将了解在非
在Map 中使用函数式操作
与forEach()
也内置在map()
,flatMap()
,reduce()
或 filter()
时,该怎么办? 查看
可以通过 entrySet()
连接到这些方法,该方法会生成一个由stream()
和 parallelStream()
方法。只需要记住一件事,这里正在使用的是
// collectiontopics/FunctionalMap.java
// Functional operations on a Map
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
import static onjava.HTMLColors.*;
public class FunctionalMap {
public static void main(String[] args) {
MAP.entrySet().stream()
.map(Map.Entry::getValue)
.filter(v -> v.startsWith("Dark"))
.map(v -> v.replaceFirst("Dark", "Hot"))
.forEach(System.out::println);
}
}
/* Output:
HotBlue
HotCyan
HotGoldenRod
HotGray
HotGreen
HotKhaki
HotMagenta
HotOliveGreen
HotOrange
HotOrchid
HotRed
HotSalmon
HotSeaGreen
HotSlateBlue
HotSlateGray
HotTurquoise
HotViolet
*/
生成
选择Map 片段
由
// collectiontopics/NavMap.java
// NavigableMap produces pieces of a Map
import java.util.*;
import java.util.concurrent.*;
import static onjava.HTMLColors.*;
public class NavMap {
public static final
NavigableMap<Integer,String> COLORS =
new ConcurrentSkipListMap<>(MAP);
public static void main(String[] args) {
show(COLORS.firstEntry());
border();
show(COLORS.lastEntry());
border();
NavigableMap<Integer, String> toLime =
COLORS.headMap(rgb("Lime"), true);
show(toLime);
border();
show(COLORS.ceilingEntry(rgb("DeepSkyBlue") - 1));
border();
show(COLORS.floorEntry(rgb("DeepSkyBlue") - 1));
border();
show(toLime.descendingMap());
border();
show(COLORS.tailMap(rgb("MistyRose"), true));
border();
show(COLORS.subMap(
rgb("Orchid"), true,
rgb("DarkSalmon"), false));
}
}
/* Output:
0x000000: Black
******************************
0xFFFFFF: White
******************************
0x000000: Black
0x000080: Navy
0x00008B: DarkBlue
0x0000CD: MediumBlue
0x0000FF: Blue
0x006400: DarkGreen
0x008000: Green
0x008080: Teal
0x008B8B: DarkCyan
0x00BFFF: DeepSkyBlue
0x00CED1: DarkTurquoise
0x00FA9A: MediumSpringGreen
0x00FF00: Lime
******************************
0x00BFFF: DeepSkyBlue
******************************
0x008B8B: DarkCyan
******************************
0x00FF00: Lime
0x00FA9A: MediumSpringGreen
0x00CED1: DarkTurquoise
0x00BFFF: DeepSkyBlue
0x008B8B: DarkCyan
0x008080: Teal
0x008000: Green
0x006400: DarkGreen
0x0000FF: Blue
0x0000CD: MediumBlue
0x00008B: DarkBlue
0x000080: Navy
0x000000: Black
******************************
0xFFE4E1: MistyRose
0xFFEBCD: BlanchedAlmond
0xFFEFD5: PapayaWhip
0xFFF0F5: LavenderBlush
0xFFF5EE: SeaShell
0xFFF8DC: Cornsilk
0xFFFACD: LemonChiffon
0xFFFAF0: FloralWhite
0xFFFAFA: Snow
0xFFFF00: Yellow
0xFFFFE0: LightYellow
0xFFFFF0: Ivory
0xFFFFFF: White
******************************
0xDA70D6: Orchid
0xDAA520: GoldenRod
0xDB7093: PaleVioletRed
0xDC143C: Crimson
0xDCDCDC: Gainsboro
0xDDA0DD: Plum
0xDEB887: BurlyWood
0xE0FFFF: LightCyan
0xE6E6FA: Lavender
*/
在主方法中可以看到firstEntry()
和 lastEntry()
的概念。调用 headMap()
会生成一个headMap()
参数中所指向的一组元素,其中tailMap()
执行了类似的操作,只不过是从参数开始到subMap()
则允许生成
ceilingEntry()
从当前键值对向上搜索下一个键值对,floorEntry()
则是向下搜索。 descendingMap()
反转了
如果需要通过分割
填充集合
与fill()
的方法。 fill()
只复制整个集合中的单个对象引用。此外,它仅适用于addAll()
方法:
// collectiontopics/FillingLists.java
// Collections.fill() & Collections.nCopies()
import java.util.*;
class StringAddress {
private String s;
StringAddress(String s) { this.s = s; }
@Override
public String toString() {
return super.toString() + " " + s;
}
}
public class FillingLists {
public static void main(String[] args) {
List<StringAddress> list = new ArrayList<>(
Collections.nCopies(4,
new StringAddress("Hello")));
System.out.println(list);
Collections.fill(list,
new StringAddress("World!"));
System.out.println(list);
}
}
/* Output:
[StringAddress@15db9742 Hello, StringAddress@15db9742
Hello, StringAddress@15db9742 Hello,
StringAddress@15db9742 Hello]
[StringAddress@6d06d69c World!, StringAddress@6d06d69c
World!, StringAddress@6d06d69c World!,
StringAddress@6d06d69c World!]
*/
这个示例展示了两种使用对单个对象的引用来填充Collections.nCopies()
,创建一个
toString()
方法调用了 Object.toString()
,它先生成类名,后跟着对象的哈希码的无符号十六进制表示(哈希吗由 hashCode()
方法生成Collections.fill()
后也是如此。 fill()
方法的用处非常有限,它只能替换
使用Suppliers 填充集合
第二十章 泛型章节中介绍的
// collectiontopics/SuppliersCollectionTest.java
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import onjava.*;
class Government implements Supplier<String> {
static String[] foundation = (
"strange women lying in ponds " +
"distributing swords is no basis " +
"for a system of government").split(" ");
private int index;
@Override
public String get() {
return foundation[index++];
}
}
public class SuppliersCollectionTest {
public static void main(String[] args) {
// Suppliers class from the Generics chapter:
Set<String> set = Suppliers.create(
LinkedHashSet::new, new Government(), 15);
System.out.println(set);
List<String> list = Suppliers.create(
LinkedList::new, new Government(), 15);
System.out.println(list);
list = new ArrayList<>();
Suppliers.fill(list, new Government(), 15);
System.out.println(list);
// Or we can use Streams:
set = Arrays.stream(Government.foundation)
.collect(Collectors.toSet());
System.out.println(set);
list = Arrays.stream(Government.foundation)
.collect(Collectors.toList());
System.out.println(list);
list = Arrays.stream(Government.foundation)
.collect(Collectors
.toCollection(LinkedList::new));
System.out.println(list);
set = Arrays.stream(Government.foundation)
.collect(Collectors
.toCollection(LinkedHashSet::new));
System.out.println(set);
}
}
/* Output:
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
[ponds, no, a, in, swords, for, is, basis, strange,
system, government, distributing, of, women, lying]
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
[strange, women, lying, in, ponds, distributing,
swords, is, no, basis, for, a, system, of, government]
*/
但是请注意示例的第二部分:大多数情况下都可以使用
尽可能优先选择
Map Suppliers
使用get()
方法时,都必须生成一对对象(一个键和一个值
// onjava/Pair.java
package onjava;
public class Pair<K, V> {
public final K key;
public final V value;
public Pair(K k, V v) {
key = k;
value = v;
}
public K key() { return key; }
public V value() { return value; }
public static <K,V> Pair<K, V> make(K k, V v) {
return new Pair<K,V>(k, v);
}
}
make()
方法,以便为创建
// collectiontopics/StreamFillMaps.java
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import onjava.*;
class Letters
implements Supplier<Pair<Integer,String>> {
private int number = 1;
private char letter = 'A';
@Override
public Pair<Integer,String> get() {
return new Pair<>(number++, "" + letter++);
}
}
public class StreamFillMaps {
public static void main(String[] args) {
Map<Integer,String> m =
Stream.generate(new Letters())
.limit(11)
.collect(Collectors
.toMap(Pair::key, Pair::value));
System.out.println(m);
// Two separate Suppliers:
Rand.String rs = new Rand.String(3);
Count.Character cc = new Count.Character();
Map<Character,String> mcs = Stream.generate(
() -> Pair.make(cc.get(), rs.get()))
.limit(8)
.collect(Collectors
.toMap(Pair::key, Pair::value));
System.out.println(mcs);
// A key Supplier and a single value:
Map<Character,String> mcs2 = Stream.generate(
() -> Pair.make(cc.get(), "Val"))
.limit(8)
.collect(Collectors
.toMap(Pair::key, Pair::value));
System.out.println(mcs2);
}
}
/* Output:
{1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J,
11=K}
{b=btp, c=enp, d=ccu, e=xsz, f=gvg, g=mei, h=nne,
i=elo}
{p=Val, q=Val, j=Val, k=Val, l=Val, m=Val, n=Val,
o=Val}
*/
上面的示例中出现了一个模式,可以使用它来创建一个自动创建和填充
// onjava/FillMap.java
package onjava;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class FillMap {
public static <K, V> Map<K,V>
basic(Supplier<Pair<K,V>> pairGen, int size) {
return Stream.generate(pairGen)
.limit(size)
.collect(Collectors
.toMap(Pair::key, Pair::value));
}
public static <K, V> Map<K,V>
basic(Supplier<K> keyGen,
Supplier<V> valueGen, int size) {
return Stream.generate(
() -> Pair.make(keyGen.get(), valueGen.get()))
.limit(size)
.collect(Collectors
.toMap(Pair::key, Pair::value));
}
public static <K, V, M extends Map<K,V>>
M create(Supplier<K> keyGen,
Supplier<V> valueGen,
Supplier<M> mapSupplier, int size) {
return Stream.generate( () ->
Pair.make(keyGen.get(), valueGen.get()))
.limit(size)
.collect(Collectors
.toMap(Pair::key, Pair::value,
(k, v) -> k, mapSupplier));
}
}
create()
方法允许指定一个确切的
下面是一个测试:
// collectiontopics/FillMapTest.java
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import onjava.*;
public class FillMapTest {
public static void main(String[] args) {
Map<String,Integer> mcs = FillMap.basic(
new Rand.String(4), new Count.Integer(), 7);
System.out.println(mcs);
HashMap<String,Integer> hashm =
FillMap.create(new Rand.String(4),
new Count.Integer(), HashMap::new, 7);
System.out.println(hashm);
LinkedHashMap<String,Integer> linkm =
FillMap.create(new Rand.String(4),
new Count.Integer(), LinkedHashMap::new, 7);
System.out.println(linkm);
}
}
/* Output:
{npcc=1, ztdv=6, gvgm=3, btpe=0, einn=4, eelo=5,
uxsz=2}
{npcc=1, ztdv=6, gvgm=3, btpe=0, einn=4, eelo=5,
uxsz=2}
{btpe=0, npcc=1, uxsz=2, gvgm=3, einn=4, eelo=5,
ztdv=6}
*/
使用享元(Flyweight)自定义Collection 和Map
本节介绍如何创建自定义iterator()
和 size()
。最后一个示例是生成测试数据的另一种方法。生成的集合通常是只读的,并且所提供的方法最少。
该解决方案还演示了 享元 (Flyweight)设计模式。当普通解决方案需要太多对象时,或者当生成普通对象占用太多空间时,可以使用享元。享元设计模式将对象的一部分外部化(externalizes
下面是一个可以是任何大小的get()
和 size()
:
// onjava/CountingIntegerList.java
// List of any length, containing sample data
// {java onjava.CountingIntegerList}
package onjava;
import java.util.*;
public class CountingIntegerList
extends AbstractList<Integer> {
private int size;
public CountingIntegerList() { size = 0; }
public CountingIntegerList(int size) {
this.size = size < 0 ? 0 : size;
}
@Override
public Integer get(int index) {
return index;
}
@Override
public int size() { return size; }
public static void main(String[] args) {
List<Integer> cil =
new CountingIntegerList(30);
System.out.println(cil);
System.out.println(cil.get(500));
}
}
/* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
500
*/
只有当想要限制get()
也会产生任何值。
这个类是享元模式的一个简洁的例子。当需要的时候, get()
“计算”所需的值,因此没必要存储和初始化实际的底层
在大多数程序中,这里所保存的存储结构永远都不会改变。但是,它允许用非常大的List.get()
,而
可以使用享元设计模式来实现具有任何大小数据集的其他“初始化”自定义集合。下面是一个
// onjava/CountMap.java
// Unlimited-length Map containing sample data
// {java onjava.CountMap}
package onjava;
import java.util.*;
import java.util.stream.*;
public class CountMap
extends AbstractMap<Integer,String> {
private int size;
private static char[] chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static String value(int key) {
return
chars[key % chars.length] +
Integer.toString(key / chars.length);
}
public CountMap(int size) {
this.size = size < 0 ? 0 : size;
}
@Override
public String get(Object key) {
return value((Integer)key);
}
private static class Entry
implements Map.Entry<Integer,String> {
int index;
Entry(int index) { this.index = index; }
@Override
public boolean equals(Object o) {
return o instanceof Entry &&
Objects.equals(index, ((Entry)o).index);
}
@Override
public Integer getKey() { return index; }
@Override
public String getValue() {
return value(index);
}
@Override
public String setValue(String value) {
throw new UnsupportedOperationException();
}
@Override
public int hashCode() {
return Objects.hashCode(index);
}
}
@Override
public Set<Map.Entry<Integer,String>> entrySet() {
// LinkedHashSet retains initialization order:
return IntStream.range(0, size)
.mapToObj(Entry::new)
.collect(Collectors
.toCollection(LinkedHashSet::new));
}
public static void main(String[] args) {
final int size = 6;
CountMap cm = new CountMap(60);
System.out.println(cm);
System.out.println(cm.get(500));
cm.values().stream()
.limit(size)
.forEach(System.out::println);
System.out.println();
new Random(47).ints(size, 0, 1000)
.mapToObj(cm::get)
.forEach(System.out::println);
}
}
/* Output:
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0,
9=J0, 10=K0, 11=L0, 12=M0, 13=N0, 14=O0, 15=P0, 16=Q0,
17=R0, 18=S0, 19=T0, 20=U0, 21=V0, 22=W0, 23=X0, 24=Y0,
25=Z0, 26=A1, 27=B1, 28=C1, 29=D1, 30=E1, 31=F1, 32=G1,
33=H1, 34=I1, 35=J1, 36=K1, 37=L1, 38=M1, 39=N1, 40=O1,
41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 48=W1,
49=X1, 50=Y1, 51=Z1, 52=A2, 53=B2, 54=C2, 55=D2, 56=E2,
57=F2, 58=G2, 59=H2}
G19
A0
B0
C0
D0
E0
F0
Y9
J21
R26
D33
Z36
N16
*/
要创建一个只读的entrySet()
。私有的 value()
方法计算任何键的值,并在 get()
和 Entry.getValue()
中使用。可以忽略
这里是使用了entrySet()
时才会生成此对象。
现在创建一个更复杂的享元。这个示例中的数据集是世界各国及其首都的capitals()
方法生成一个国家和首都的names()
方法生成一个由国家名字组成的
// onjava/Countries.java
// "Flyweight" Maps and Lists of sample data
// {java onjava.Countries}
package onjava;
import java.util.*;
public class Countries {
public static final String[][] DATA = {
// Africa
{"ALGERIA","Algiers"},
{"ANGOLA","Luanda"},
{"BENIN","Porto-Novo"},
{"BOTSWANA","Gaberone"},
{"BURKINA FASO","Ouagadougou"},
{"BURUNDI","Bujumbura"},
{"CAMEROON","Yaounde"},
{"CAPE VERDE","Praia"},
{"CENTRAL AFRICAN REPUBLIC","Bangui"},
{"CHAD","N'djamena"},
{"COMOROS","Moroni"},
{"CONGO","Brazzaville"},
{"DJIBOUTI","Dijibouti"},
{"EGYPT","Cairo"},
{"EQUATORIAL GUINEA","Malabo"},
{"ERITREA","Asmara"},
{"ETHIOPIA","Addis Ababa"},
{"GABON","Libreville"},
{"THE GAMBIA","Banjul"},
{"GHANA","Accra"},
{"GUINEA","Conakry"},
{"BISSAU","Bissau"},
{"COTE D'IVOIR (IVORY COAST)","Yamoussoukro"},
{"KENYA","Nairobi"},
{"LESOTHO","Maseru"},
{"LIBERIA","Monrovia"},
{"LIBYA","Tripoli"},
{"MADAGASCAR","Antananarivo"},
{"MALAWI","Lilongwe"},
{"MALI","Bamako"},
{"MAURITANIA","Nouakchott"},
{"MAURITIUS","Port Louis"},
{"MOROCCO","Rabat"},
{"MOZAMBIQUE","Maputo"},
{"NAMIBIA","Windhoek"},
{"NIGER","Niamey"},
{"NIGERIA","Abuja"},
{"RWANDA","Kigali"},
{"SAO TOME E PRINCIPE","Sao Tome"},
{"SENEGAL","Dakar"},
{"SEYCHELLES","Victoria"},
{"SIERRA LEONE","Freetown"},
{"SOMALIA","Mogadishu"},
{"SOUTH AFRICA","Pretoria/Cape Town"},
{"SUDAN","Khartoum"},
{"SWAZILAND","Mbabane"},
{"TANZANIA","Dodoma"},
{"TOGO","Lome"},
{"TUNISIA","Tunis"},
{"UGANDA","Kampala"},
{"DEMOCRATIC REPUBLIC OF THE CONGO (ZAIRE)",
"Kinshasa"},
{"ZAMBIA","Lusaka"},
{"ZIMBABWE","Harare"},
// Asia
{"AFGHANISTAN","Kabul"},
{"BAHRAIN","Manama"},
{"BANGLADESH","Dhaka"},
{"BHUTAN","Thimphu"},
{"BRUNEI","Bandar Seri Begawan"},
{"CAMBODIA","Phnom Penh"},
{"CHINA","Beijing"},
{"CYPRUS","Nicosia"},
{"INDIA","New Delhi"},
{"INDONESIA","Jakarta"},
{"IRAN","Tehran"},
{"IRAQ","Baghdad"},
{"ISRAEL","Jerusalem"},
{"JAPAN","Tokyo"},
{"JORDAN","Amman"},
{"KUWAIT","Kuwait City"},
{"LAOS","Vientiane"},
{"LEBANON","Beirut"},
{"MALAYSIA","Kuala Lumpur"},
{"THE MALDIVES","Male"},
{"MONGOLIA","Ulan Bator"},
{"MYANMAR (BURMA)","Rangoon"},
{"NEPAL","Katmandu"},
{"NORTH KOREA","P'yongyang"},
{"OMAN","Muscat"},
{"PAKISTAN","Islamabad"},
{"PHILIPPINES","Manila"},
{"QATAR","Doha"},
{"SAUDI ARABIA","Riyadh"},
{"SINGAPORE","Singapore"},
{"SOUTH KOREA","Seoul"},
{"SRI LANKA","Colombo"},
{"SYRIA","Damascus"},
{"TAIWAN (REPUBLIC OF CHINA)","Taipei"},
{"THAILAND","Bangkok"},
{"TURKEY","Ankara"},
{"UNITED ARAB EMIRATES","Abu Dhabi"},
{"VIETNAM","Hanoi"},
{"YEMEN","Sana'a"},
// Australia and Oceania
{"AUSTRALIA","Canberra"},
{"FIJI","Suva"},
{"KIRIBATI","Bairiki"},
{"MARSHALL ISLANDS","Dalap-Uliga-Darrit"},
{"MICRONESIA","Palikir"},
{"NAURU","Yaren"},
{"NEW ZEALAND","Wellington"},
{"PALAU","Koror"},
{"PAPUA NEW GUINEA","Port Moresby"},
{"SOLOMON ISLANDS","Honaira"},
{"TONGA","Nuku'alofa"},
{"TUVALU","Fongafale"},
{"VANUATU","Port Vila"},
{"WESTERN SAMOA","Apia"},
// Eastern Europe and former USSR
{"ARMENIA","Yerevan"},
{"AZERBAIJAN","Baku"},
{"BELARUS (BYELORUSSIA)","Minsk"},
{"BULGARIA","Sofia"},
{"GEORGIA","Tbilisi"},
{"KAZAKSTAN","Almaty"},
{"KYRGYZSTAN","Alma-Ata"},
{"MOLDOVA","Chisinau"},
{"RUSSIA","Moscow"},
{"TAJIKISTAN","Dushanbe"},
{"TURKMENISTAN","Ashkabad"},
{"UKRAINE","Kyiv"},
{"UZBEKISTAN","Tashkent"},
// Europe
{"ALBANIA","Tirana"},
{"ANDORRA","Andorra la Vella"},
{"AUSTRIA","Vienna"},
{"BELGIUM","Brussels"},
{"BOSNIA-HERZEGOVINA","Sarajevo"},
{"CROATIA","Zagreb"},
{"CZECH REPUBLIC","Prague"},
{"DENMARK","Copenhagen"},
{"ESTONIA","Tallinn"},
{"FINLAND","Helsinki"},
{"FRANCE","Paris"},
{"GERMANY","Berlin"},
{"GREECE","Athens"},
{"HUNGARY","Budapest"},
{"ICELAND","Reykjavik"},
{"IRELAND","Dublin"},
{"ITALY","Rome"},
{"LATVIA","Riga"},
{"LIECHTENSTEIN","Vaduz"},
{"LITHUANIA","Vilnius"},
{"LUXEMBOURG","Luxembourg"},
{"MACEDONIA","Skopje"},
{"MALTA","Valletta"},
{"MONACO","Monaco"},
{"MONTENEGRO","Podgorica"},
{"THE NETHERLANDS","Amsterdam"},
{"NORWAY","Oslo"},
{"POLAND","Warsaw"},
{"PORTUGAL","Lisbon"},
{"ROMANIA","Bucharest"},
{"SAN MARINO","San Marino"},
{"SERBIA","Belgrade"},
{"SLOVAKIA","Bratislava"},
{"SLOVENIA","Ljuijana"},
{"SPAIN","Madrid"},
{"SWEDEN","Stockholm"},
{"SWITZERLAND","Berne"},
{"UNITED KINGDOM","London"},
{"VATICAN CITY","Vatican City"},
// North and Central America
{"ANTIGUA AND BARBUDA","Saint John's"},
{"BAHAMAS","Nassau"},
{"BARBADOS","Bridgetown"},
{"BELIZE","Belmopan"},
{"CANADA","Ottawa"},
{"COSTA RICA","San Jose"},
{"CUBA","Havana"},
{"DOMINICA","Roseau"},
{"DOMINICAN REPUBLIC","Santo Domingo"},
{"EL SALVADOR","San Salvador"},
{"GRENADA","Saint George's"},
{"GUATEMALA","Guatemala City"},
{"HAITI","Port-au-Prince"},
{"HONDURAS","Tegucigalpa"},
{"JAMAICA","Kingston"},
{"MEXICO","Mexico City"},
{"NICARAGUA","Managua"},
{"PANAMA","Panama City"},
{"ST. KITTS AND NEVIS","Basseterre"},
{"ST. LUCIA","Castries"},
{"ST. VINCENT AND THE GRENADINES","Kingstown"},
{"UNITED STATES OF AMERICA","Washington, D.C."},
// South America
{"ARGENTINA","Buenos Aires"},
{"BOLIVIA","Sucre (legal)/La Paz(administrative)"},
{"BRAZIL","Brasilia"},
{"CHILE","Santiago"},
{"COLOMBIA","Bogota"},
{"ECUADOR","Quito"},
{"GUYANA","Georgetown"},
{"PARAGUAY","Asuncion"},
{"PERU","Lima"},
{"SURINAME","Paramaribo"},
{"TRINIDAD AND TOBAGO","Port of Spain"},
{"URUGUAY","Montevideo"},
{"VENEZUELA","Caracas"},
};
// Use AbstractMap by implementing entrySet()
private static class FlyweightMap
extends AbstractMap<String,String> {
private static class Entry
implements Map.Entry<String,String> {
int index;
Entry(int index) { this.index = index; }
@Override
public boolean equals(Object o) {
return o instanceof FlyweightMap &&
Objects.equals(DATA[index][0], o);
}
@Override
public int hashCode() {
return Objects.hashCode(DATA[index][0]);
}
@Override
public String getKey() { return DATA[index][0]; }
@Override
public String getValue() {
return DATA[index][1];
}
@Override
public String setValue(String value) {
throw new UnsupportedOperationException();
}
}
// Implement size() & iterator() for AbstractSet:
static class EntrySet
extends AbstractSet<Map.Entry<String,String>> {
private int size;
EntrySet(int size) {
if(size < 0)
this.size = 0;
// Can't be any bigger than the array:
else if(size > DATA.length)
this.size = DATA.length;
else
this.size = size;
}
@Override
public int size() { return size; }
private class Iter
implements Iterator<Map.Entry<String,String>> {
// Only one Entry object per Iterator:
private Entry entry = new Entry(-1);
@Override
public boolean hasNext() {
return entry.index < size - 1;
}
@Override
public Map.Entry<String,String> next() {
entry.index++;
return entry;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@Override
public
Iterator<Map.Entry<String,String>> iterator() {
return new Iter();
}
}
private static
Set<Map.Entry<String,String>> entries =
new EntrySet(DATA.length);
@Override
public Set<Map.Entry<String,String>> entrySet() {
return entries;
}
}
// Create a partial map of 'size' countries:
static Map<String,String> select(final int size) {
return new FlyweightMap() {
@Override
public Set<Map.Entry<String,String>> entrySet() {
return new EntrySet(size);
}
};
}
static Map<String,String> map = new FlyweightMap();
public static Map<String,String> capitals() {
return map; // The entire map
}
public static Map<String,String> capitals(int size) {
return select(size); // A partial map
}
static List<String> names =
new ArrayList<>(map.keySet());
// All the names:
public static List<String> names() { return names; }
// A partial list:
public static List<String> names(int size) {
return new ArrayList<>(select(size).keySet());
}
public static void main(String[] args) {
System.out.println(capitals(10));
System.out.println(names(10));
System.out.println(new HashMap<>(capitals(3)));
System.out.println(
new LinkedHashMap<>(capitals(3)));
System.out.println(new TreeMap<>(capitals(3)));
System.out.println(new Hashtable<>(capitals(3)));
System.out.println(new HashSet<>(names(6)));
System.out.println(new LinkedHashSet<>(names(6)));
System.out.println(new TreeSet<>(names(6)));
System.out.println(new ArrayList<>(names(6)));
System.out.println(new LinkedList<>(names(6)));
System.out.println(capitals().get("BRAZIL"));
}
}
/* Output:
{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo,
BOTSWANA=Gaberone, BURKINA FASO=Ouagadougou,
BURUNDI=Bujumbura, CAMEROON=Yaounde, CAPE VERDE=Praia,
CENTRAL AFRICAN REPUBLIC=Bangui, CHAD=N'djamena}
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN
REPUBLIC, CHAD]
{BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers}
{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo}
{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo}
{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo}
[BENIN, BOTSWANA, ANGOLA, BURKINA FASO, ALGERIA,
BURUNDI]
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI]
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI]
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI]
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI]
Brasilia
*/
二维数组entrySet()
方法,该方法需要一个自定义getKey()
或 getValue()
时,它使用索引返回相应的
享元的另一部分在next()
时,next()
返回
select()
方法生成一个包含所需大小的capitals()
和 names()
方法。
集合功能
下面这个表格展示了可以对
方法名 | 描述 |
---|---|
boolean add(T) | 确保集合包含该泛型类型 |
boolean addAll(Collection<? extends T>) | 添加参数集合中的所有元素。只要有元素被成功添加则返回 |
void clear() | 删除集合中的所有元素 |
boolean contains(T) | 如果目标集合包含该泛型类型 |
boolean containsAll(Collection<?>) | 如果目标集合包含参数集合中的所有元素,则返回 |
boolean isEmpty() | 如果集合为空,则返回 |
Iterator<T> iterator() Spliterator<T> spliterator() | 返回一个迭代器来遍历集合中的元素。 |
boolean remove(Object) | 如果目标集合包含该参数,则在集合中删除该参数,如果成功删除则返回 |
boolean removeAll(Collection<?>) | 删除目标集合中,参数集合所包含的全部元素。如果有元素被成功删除则返回 |
boolean removeIf(Predicate<? super E>) | 删除此集合中,满足给定断言(predicate)的所有元素 |
Stream<E> stream() Stream<E> parallelStream() | 返回由该 |
int size() | 返回集合中所包含元素的个数 |
Object[] toArrat() | 返回包含该集合所有元素的一个数组 |
<T> T[] toArray(T[] a) | 返回包含该集合所有元素的一个数组。结果的运行时类型是参数数组而不是普通的 |
这里没有提供用于随机访问元素的
下面这个示例演示了
// collectiontopics/CollectionMethods.java
// Things you can do with all Collections
import java.util.*;
import static onjava.HTMLColors.*;
public class CollectionMethods {
public static void main(String[] args) {
Collection<String> c =
new ArrayList<>(LIST.subList(0, 4));
c.add("ten");
c.add("eleven");
show(c);
border();
// Make an array from the List:
Object[] array = c.toArray();
// Make a String array from the List:
String[] str = c.toArray(new String[0]);
// Find max and min elements; this means
// different things depending on the way
// the Comparable interface is implemented:
System.out.println(
"Collections.max(c) = " + Collections.max(c));
System.out.println(
"Collections.min(c) = " + Collections.min(c));
border();
// Add a Collection to another Collection
Collection<String> c2 =
new ArrayList<>(LIST.subList(10, 14));
c.addAll(c2);
show(c);
border();
c.remove(LIST.get(0));
show(c);
border();
// Remove all components that are
// in the argument collection:
c.removeAll(c2);
show(c);
border();
c.addAll(c2);
show(c);
border();
// Is an element in this Collection?
String val = LIST.get(3);
System.out.println(
"c.contains(" + val + ") = " + c.contains(val));
// Is a Collection in this Collection?
System.out.println(
"c.containsAll(c2) = " + c.containsAll(c2));
Collection<String> c3 =
((List<String>)c).subList(3, 5);
// Keep all the elements that are in both
// c2 and c3 (an intersection of sets):
c2.retainAll(c3);
show(c2);
// Throw away all the elements
// in c2 that also appear in c3:
c2.removeAll(c3);
System.out.println(
"c2.isEmpty() = " + c2.isEmpty());
border();
// Functional operation:
c = new ArrayList<>(LIST);
c.removeIf(s -> !s.startsWith("P"));
c.removeIf(s -> s.startsWith("Pale"));
// Stream operation:
c.stream().forEach(System.out::println);
c.clear(); // Remove all elements
System.out.println("after c.clear():" + c);
}
}
/* Output:
AliceBlue
AntiqueWhite
Aquamarine
Azure
ten
eleven
******************************
Collections.max(c) = ten
Collections.min(c) = AliceBlue
******************************
AliceBlue
AntiqueWhite
Aquamarine
Azure
ten
eleven
Brown
BurlyWood
CadetBlue
Chartreuse
******************************
AntiqueWhite
Aquamarine
Azure
ten
eleven
Brown
BurlyWood
CadetBlue
Chartreuse
******************************
AntiqueWhite
Aquamarine
Azure
ten
eleven
******************************
AntiqueWhite
Aquamarine
Azure
ten
eleven
Brown
BurlyWood
CadetBlue
Chartreuse
******************************
c.contains(Azure) = true
c.containsAll(c2) = true
c2.isEmpty() = true
******************************
PapayaWhip
PeachPuff
Peru
Pink
Plum
PowderBlue
Purple
after c.clear():[]
*/
为了只演示
可选操作
在
这是一种非常不寻常的定义接口的方式。正如我们所知,接口是一种合约(contract
其实没那么糟糕。如果操作是可选的,编译器仍然能够限制你仅调用该接口中的方法。它不像动态语言那样,可以为任何对象调用任何方法,并在运行时查找特定的调用是否可行。2此外,大多数将
为什么要将方法定义为“可选”的?因为这样做可以防止设计中的接口爆炸。集合库的其他设计往往会产生令人困惑的过多接口来描述主题的每个变体。这甚至使得不可能捕获到接口中的所有特殊情况,因为总有人能发明一个新的接口
-
UnsupportedOperationException 必须是一个罕见的事件。也就是说,对于大多数类,所有操作都应该起作用,并且只有在特殊情况下才应该不支持某项操作。这在 Java 集合库中是正确的,因为99% 的时间使用到的类 —— ArrayList , LinkedList ,HashSet 和 HashMap ,以及其他具体实现,都支持所有操作。该设计确实为创建一个新的Collection 提供了一个“后门”,可以不为 Collection 接口中的所有方法都提供有意义的定义,这些定义仍然适合现有的类库。 -
当不支持某个操作时,
UnsupportedOperationException 应该出现在实现阶段,而不是在将产品发送给客户之后。毕竟,这个异常表示编程错误:错误地使用了一个具体实现。
值得注意的是,不支持的操作只能在运行时检测到,因此这代表动态类型检查。如果你来自像
不支持的操作
不支持的操作的常见来源是由固定大小的数据结构所支持的集合。使用 Arrays.asList()
方法将数组转换为
// collectiontopics/Unsupported.java
// Unsupported operations in Java collections
import java.util.*;
public class Unsupported {
static void
check(String description, Runnable tst) {
try {
tst.run();
} catch(Exception e) {
System.out.println(description + "(): " + e);
}
}
static void test(String msg, List<String> list) {
System.out.println("--- " + msg + " ---");
Collection<String> c = list;
Collection<String> subList = list.subList(1,8);
// Copy of the sublist:
Collection<String> c2 = new ArrayList<>(subList);
check("retainAll", () -> c.retainAll(c2));
check("removeAll", () -> c.removeAll(c2));
check("clear", () -> c.clear());
check("add", () -> c.add("X"));
check("addAll", () -> c.addAll(c2));
check("remove", () -> c.remove("C"));
// The List.set() method modifies the value but
// doesn't change the size of the data structure:
check("List.set", () -> list.set(0, "X"));
}
public static void main(String[] args) {
List<String> list = Arrays.asList(
"A B C D E F G H I J K L".split(" "));
test("Modifiable Copy", new ArrayList<>(list));
test("Arrays.asList()", list);
test("unmodifiableList()",
Collections.unmodifiableList(
new ArrayList<>(list)));
}
}
/* Output:
--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException
*/
因为 Arrays.asList()
生成的
请注意,始终可以将 Arrays.asList()
的结果作为一个参数传递给任何addAll()
方法或静态的 Collections.addAll()
方法)来创建一个允许使用所有方法的常规集合,在主方法中第一次调用 test()
时显示了这种情况。这种调用产生了一个新的可调整大小的底层数据结构。
test()
中的最后一个 check()
用于测试set()
方法。这里Arrays.asList()
返回的对象和 Collections.unmodifiableList()
返回的对象之间变换。 Arrays.asList()
返回固定大小的Collections.unmodifiableList()
生成无法更改的Arrays.asList()
返回的unmodifiableList()
的结果不应该以任何方式修改。如果使用接口来描述,则需要两个额外的接口,一个具有可用的 set()
方法,而另一个没有。
如果一个方法将一个集合作为它的参数,那么它的文档应该说明必须实现哪些可选方法。
Set 和存储顺序
第十二章 集合章节中的
约束 | |
---|---|
Set(interface) | 添加到equals() 方法以建立对象唯一性。 |
HashSet* | 注重快速查找元素的集合,其中元素必须定义 hashCode() 和 equals() 方法。 |
TreeSet | 由树支持的有序 |
LinkedHashSet | 具有hashCode() 和 equals() 方法。 |
定义 hashCode()
方法在附录equals()
方法,但只有当把类放在hashCode()
(当然这很有可能,因为equals()
时应始终覆盖 hashCode()
。
下面的示例演示了成功使用具有特定
// collectiontopics/TypesForSets.java
// Methods necessary to put your own type in a Set
import java.util.*;
import java.util.function.*;
import java.util.Objects;
class SetType {
protected int i;
SetType(int n) { i = n; }
@Override
public boolean equals(Object o) {
return o instanceof SetType &&
Objects.equals(i, ((SetType)o).i);
}
@Override
public String toString() {
return Integer.toString(i);
}
}
class HashType extends SetType {
HashType(int n) { super(n); }
@Override
public int hashCode() {
return Objects.hashCode(i);
}
}
class TreeType extends SetType
implements Comparable<TreeType> {
TreeType(int n) { super(n); }
@Override
public int compareTo(TreeType arg) {
return Integer.compare(arg.i, i);
// Equivalent to:
// return arg.i < i ? -1 : (arg.i == i ? 0 : 1);
}
}
public class TypesForSets {
static <T> void
fill(Set<T> set, Function<Integer, T> type) {
for(int i = 10; i >= 5; i--) // Descending
set.add(type.apply(i));
for(int i = 0; i < 5; i++) // Ascending
set.add(type.apply(i));
}
static <T> void
test(Set<T> set, Function<Integer, T> type) {
fill(set, type);
fill(set, type); // Try to add duplicates
fill(set, type);
System.out.println(set);
}
public static void main(String[] args) {
test(new HashSet<>(), HashType::new);
test(new LinkedHashSet<>(), HashType::new);
test(new TreeSet<>(), TreeType::new);
// Things that don't work:
test(new HashSet<>(), SetType::new);
test(new HashSet<>(), TreeType::new);
test(new LinkedHashSet<>(), SetType::new);
test(new LinkedHashSet<>(), TreeType::new);
try {
test(new TreeSet<>(), SetType::new);
} catch(Exception e) {
System.out.println(e.getMessage());
}
try {
test(new TreeSet<>(), HashType::new);
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
/* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[10, 9, 8, 7, 6, 5, 0, 1, 2, 3, 4]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[1, 6, 8, 6, 2, 7, 8, 9, 4, 10, 7, 5, 1, 3, 4, 9, 9,
10, 5, 3, 2, 0, 4, 1, 2, 0, 8, 3, 0, 10, 6, 5, 7]
[3, 1, 4, 8, 7, 6, 9, 5, 3, 0, 10, 5, 5, 10, 7, 8, 8,
9, 1, 4, 10, 2, 6, 9, 1, 6, 0, 3, 2, 0, 7, 2, 4]
[10, 9, 8, 7, 6, 5, 0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5,
0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5, 0, 1, 2, 3, 4]
[10, 9, 8, 7, 6, 5, 0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5,
0, 1, 2, 3, 4, 10, 9, 8, 7, 6, 5, 0, 1, 2, 3, 4]
SetType cannot be cast to java.lang.Comparable
HashType cannot be cast to java.lang.Comparable
*/
为了证明特定toString()
方法打印它。由于存储在equals()
,因此该方法也放在基类中。基于 int i
来判断元素是否相等。
hashCode()
方法,该方法对于
要在任何类型的有序集合中使用对象,由compareTo()
中,请注意我没有使用“简单明了”的形式: return i-i2
。虽然这是一个常见的编程错误,但只有当i-j
将溢出并返回一个负值,这不是我们所需要的。
通常希望 compareTo()
方法生成与 equals()
方法一致的自然顺序。如果 equals()
对于特定比较产生compareTo()
应该为该比较返回结果 零,并且如果 equals()
为比较产生compareTo()
应该为该比较产生非零结果。
在fill()
和 test()
都是使用泛型定义的,以防止代码重复。为了验证test()
在测试集上调用 fill()
三次,尝试引入重复的对象。 fill()
方法的参数可以接收任意一个
请注意, fill()
方法按降序添加前五个元素,按升序添加后五个元素,以此来指出生成的存储顺序。输出显示compareTo()
的实现方式,所以元素按降序排列
特定的hashCode()
方法的hashCode()
是合法的,所以即使它是不正确的,这也是合法的行为。确保此类程序正确性的唯一可靠方法是将单元测试合并到构建系统中。
如果尝试在
SortedSet
Comparator comparator()
:生成用于此Set 的 Comparator 或 null 来用于自然排序。 Object first()
:返回第一个元素。Object last()
:返回最后一个元素。SortedSet subSet(fromElement,toElement)
:使用fromElement (包含)和toElement (不包括)中的元素生成此Set 的一个视图。 SortedSet headSet(toElement)
:使用顺序在toElement 之前的元素生成此 Set 的一个视图。 SortedSet tailSet(fromElement)
:使用顺序在fromElement 之后(包含 fromElement )的元素生成此Set 的一个视图。
下面是一个简单的演示:
// collectiontopics/SortedSetDemo.java
import java.util.*;
import static java.util.stream.Collectors.*;
public class SortedSetDemo {
public static void main(String[] args) {
SortedSet<String> sortedSet =
Arrays.stream(
"one two three four five six seven eight"
.split(" "))
.collect(toCollection(TreeSet::new));
System.out.println(sortedSet);
String low = sortedSet.first();
String high = sortedSet.last();
System.out.println(low);
System.out.println(high);
Iterator<String> it = sortedSet.iterator();
for(int i = 0; i <= 6; i++) {
if(i == 3) low = it.next();
if(i == 6) high = it.next();
else it.next();
}
System.out.println(low);
System.out.println(high);
System.out.println(sortedSet.subSet(low, high));
System.out.println(sortedSet.headSet(high));
System.out.println(sortedSet.tailSet(low));
}
}
/* Output:
[eight, five, four, one, seven, six, three, two]
eight
two
one
two
[one, seven, six, three]
[eight, five, four, one, seven, six, three]
[one, seven, six, three, two]
*/
注意,
队列
有许多
// collectiontopics/QueueBehavior.java
// Compares basic behavior
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
public class QueueBehavior {
static Stream<String> strings() {
return Arrays.stream(
("one two three four five six seven " +
"eight nine ten").split(" "));
}
static void test(int id, Queue<String> queue) {
System.out.print(id + ": ");
strings().map(queue::offer).count();
while(queue.peek() != null)
System.out.print(queue.remove() + " ");
System.out.println();
}
public static void main(String[] args) {
int count = 10;
test(1, new LinkedList<>());
test(2, new PriorityQueue<>());
test(3, new ArrayBlockingQueue<>(count));
test(4, new ConcurrentLinkedQueue<>());
test(5, new LinkedBlockingQueue<>());
test(6, new PriorityBlockingQueue<>());
test(7, new ArrayDeque<>());
test(8, new ConcurrentLinkedDeque<>());
test(9, new LinkedBlockingDeque<>());
test(10, new LinkedTransferQueue<>());
test(11, new SynchronousQueue<>());
}
}
/* Output:
1: one two three four five six seven eight nine ten
2: eight five four nine one seven six ten three two
3: one two three four five six seven eight nine ten
4: one two three four five six seven eight nine ten
5: one two three four five six seven eight nine ten
6: eight five four nine one seven six ten three two
7: one two three four five six seven eight nine ten
8: one two three four five six seven eight nine ten
9: one two three four five six seven eight nine ten
10: one two three four five six seven eight nine ten
11:
*/
优先级队列
考虑一个待办事项列表,其中每个对象包含一个
// collectiontopics/ToDoList.java
// A more complex use of PriorityQueue
import java.util.*;
class ToDoItem implements Comparable<ToDoItem> {
private char primary;
private int secondary;
private String item;
ToDoItem(String td, char pri, int sec) {
primary = pri;
secondary = sec;
item = td;
}
@Override
public int compareTo(ToDoItem arg) {
if(primary > arg.primary)
return +1;
if(primary == arg.primary)
if(secondary > arg.secondary)
return +1;
else if(secondary == arg.secondary)
return 0;
return -1;
}
@Override
public String toString() {
return Character.toString(primary) +
secondary + ": " + item;
}
}
class ToDoList {
public static void main(String[] args) {
PriorityQueue<ToDoItem> toDo =
new PriorityQueue<>();
toDo.add(new ToDoItem("Empty trash", 'C', 4));
toDo.add(new ToDoItem("Feed dog", 'A', 2));
toDo.add(new ToDoItem("Feed bird", 'B', 7));
toDo.add(new ToDoItem("Mow lawn", 'C', 3));
toDo.add(new ToDoItem("Water lawn", 'A', 1));
toDo.add(new ToDoItem("Feed cat", 'B', 1));
while(!toDo.isEmpty())
System.out.println(toDo.remove());
}
}
/* Output:
A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash
*/
这展示了通过优先级队列自动排序待办事项。
双端队列
Deque (双端队列)就像一个队列,但是可以从任一端添加和删除元素。
// collectiontopics/SimpleDeques.java
// Very basic test of Deques
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
class CountString implements Supplier<String> {
private int n = 0;
CountString() {}
CountString(int start) { n = start; }
@Override
public String get() {
return Integer.toString(n++);
}
}
public class SimpleDeques {
static void test(Deque<String> deque) {
CountString s1 = new CountString(),
s2 = new CountString(20);
for(int n = 0; n < 8; n++) {
deque.offerFirst(s1.get());
deque.offerLast(s2.get()); // Same as offer()
}
System.out.println(deque);
String result = "";
while(deque.size() > 0) {
System.out.print(deque.peekFirst() + " ");
result += deque.pollFirst() + " ";
System.out.print(deque.peekLast() + " ");
result += deque.pollLast() + " ";
}
System.out.println("\n" + result);
}
public static void main(String[] args) {
int count = 10;
System.out.println("LinkedList");
test(new LinkedList<>());
System.out.println("ArrayDeque");
test(new ArrayDeque<>());
System.out.println("LinkedBlockingDeque");
test(new LinkedBlockingDeque<>(count));
System.out.println("ConcurrentLinkedDeque");
test(new ConcurrentLinkedDeque<>());
}
}
/* Output:
LinkedList
[7, 6, 5, 4, 3, 2, 1, 0, 20, 21, 22, 23, 24, 25, 26,
27]
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
ArrayDeque
[7, 6, 5, 4, 3, 2, 1, 0, 20, 21, 22, 23, 24, 25, 26,
27]
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
LinkedBlockingDeque
[4, 3, 2, 1, 0, 20, 21, 22, 23, 24]
4 24 3 23 2 22 1 21 0 20
4 24 3 23 2 22 1 21 0 20
ConcurrentLinkedDeque
[7, 6, 5, 4, 3, 2, 1, 0, 20, 21, 22, 23, 24, 25, 26,
27]
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
7 27 6 26 5 25 4 24 3 23 2 22 1 21 0 20
*/
我只使用了
理解Map
正如在第十二章 集合章节中所了解到的,Map(也称为 关联数组 )维护键值关联(对
为了更深入地了解
// collectiontopics/AssociativeArray.java
// Associates keys with values
public class AssociativeArray<K, V> {
private Object[][] pairs;
private int index;
public AssociativeArray(int length) {
pairs = new Object[length][2];
}
public void put(K key, V value) {
if(index >= pairs.length)
throw new ArrayIndexOutOfBoundsException();
pairs[index++] = new Object[]{ key, value };
}
@SuppressWarnings("unchecked")
public V get(K key) {
for(int i = 0; i < index; i++)
if(key.equals(pairs[i][0]))
return (V)pairs[i][1];
return null; // Did not find key
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for(int i = 0; i < index; i++) {
result.append(pairs[i][0].toString());
result.append(" : ");
result.append(pairs[i][1].toString());
if(i < index - 1)
result.append("\n");
}
return result.toString();
}
public static void main(String[] args) {
AssociativeArray<String,String> map =
new AssociativeArray<>(6);
map.put("sky", "blue");
map.put("grass", "green");
map.put("ocean", "dancing");
map.put("tree", "tall");
map.put("earth", "brown");
map.put("sun", "warm");
try {
map.put("extra", "object"); // Past the end
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Too many objects!");
}
System.out.println(map);
System.out.println(map.get("ocean"));
}
}
/* Output:
Too many objects!
sky : blue
grass : green
ocean : dancing
tree : tall
earth : brown
sun : warm
dancing
*/
关联数组中的基本方法是 put()
和 get()
,但为了便于显示,重写了 toString()
方法以打印键值对。为了显示它的工作原理,主方法加载一个带有字符串对的get()
方法。
要使用 get()
方法,可以传入要查找的get()
方法使用可能是效率最低的方法来定位值:从数组的头部开始并使用 equals()
来比较键。但这里是侧重于简单,而不是效率。
这个版本很有启发性,但它不是很有效,而且它只有一个固定的大小,这是不灵活的。幸运的是,
性能
性能是get()
中使用线性方法搜索一个键时会非常慢。这就是hashCode()
是根类hashCode()
并使用它来快速搜索键。这就使得性能有了显著的提升。3
以下是基本的
描述 | |
---|---|
HashMap* | 基于哈希表的实现 |
LinkedHashMap | 与 |
TreeMap | 基于红黑树的实现。当查看键或键值对时,它们按排序顺序(由subMap() 方法的 |
WeakHashMap | 一种具有 弱键(weak keys) 的 |
ConcurrentHashMap | 不使用同步锁定的线程安全 |
IdentityHashMap | 使用 == 而不是 equals() 来比较键。仅用于解决特殊问题,不适用于一般用途。 |
散列是在
equals()
方法。如果键用于散列映射,则它还必须具有正确的 hashCode()
方法。如果键在
以下示例使用先前定义的
// collectiontopics/MapOps.java
// Things you can do with Maps
import java.util.concurrent.*;
import java.util.*;
import onjava.*;
public class MapOps {
public static
void printKeys(Map<Integer,String> map) {
System.out.print("Size = " + map.size() + ", ");
System.out.print("Keys: ");
// Produce a Set of the keys:
System.out.println(map.keySet());
}
public static
void test(Map<Integer,String> map) {
System.out.println(
map.getClass().getSimpleName());
map.putAll(new CountMap(25));
// Map has 'Set' behavior for keys:
map.putAll(new CountMap(25));
printKeys(map);
// Producing a Collection of the values:
System.out.print("Values: ");
System.out.println(map.values());
System.out.println(map);
System.out.println("map.containsKey(11): " +
map.containsKey(11));
System.out.println(
"map.get(11): " + map.get(11));
System.out.println("map.containsValue(\"F0\"): "
+ map.containsValue("F0"));
Integer key = map.keySet().iterator().next();
System.out.println("First key in map: " + key);
map.remove(key);
printKeys(map);
map.clear();
System.out.println(
"map.isEmpty(): " + map.isEmpty());
map.putAll(new CountMap(25));
// Operations on the Set change the Map:
map.keySet().removeAll(map.keySet());
System.out.println(
"map.isEmpty(): " + map.isEmpty());
}
public static void main(String[] args) {
test(new HashMap<>());
test(new TreeMap<>());
test(new LinkedHashMap<>());
test(new IdentityHashMap<>());
test(new ConcurrentHashMap<>());
test(new WeakHashMap<>());
}
}
/* Output: (First 11 Lines)
HashMap
Size = 25, Keys: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
Values: [A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0,
L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0]
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0,
9=J0, 10=K0, 11=L0, 12=M0, 13=N0, 14=O0, 15=P0, 16=Q0,
17=R0, 18=S0, 19=T0, 20=U0, 21=V0, 22=W0, 23=X0, 24=Y0}
map.containsKey(11): true
map.get(11): L0
map.containsValue("F0"): true
First key in map: 0
Size = 24, Keys: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
map.isEmpty(): true
map.isEmpty(): true
...
*/
printKeys()
方法演示了如何生成keySet()
方法生成一个由values()
方法的结果会生成一个包含
程序的其余部分提供了每个
SortedMap
使用
Comparator comparator()
:生成用于此Map 的比较器, null 表示自然排序。 T firstKey()
:返回第一个键。T lastKey()
:返回最后一个键。SortedMap subMap(fromKey,toKey)
:生成此Map 的视图,其中键从 fromKey (包括) ,到toKey (不包括) 。SortedMap headMap(toKey)
:使用小于toKey 的键生成此 Map 的视图。 SortedMap tailMap(fromKey)
:使用大于或等于fromKey 的键生成此 Map 的视图。
这是一个类似于
// collectiontopics/SortedMapDemo.java
// What you can do with a TreeMap
import java.util.*;
import onjava.*;
public class SortedMapDemo {
public static void main(String[] args) {
TreeMap<Integer,String> sortedMap =
new TreeMap<>(new CountMap(10));
System.out.println(sortedMap);
Integer low = sortedMap.firstKey();
Integer high = sortedMap.lastKey();
System.out.println(low);
System.out.println(high);
Iterator<Integer> it =
sortedMap.keySet().iterator();
for(int i = 0; i <= 6; i++) {
if(i == 3) low = it.next();
if(i == 6) high = it.next();
else it.next();
}
System.out.println(low);
System.out.println(high);
System.out.println(sortedMap.subMap(low, high));
System.out.println(sortedMap.headMap(high));
System.out.println(sortedMap.tailMap(low));
}
}
/* Output:
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0,
9=J0}
0
9
3
7
{3=D0, 4=E0, 5=F0, 6=G0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0}
{3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0}
*/
这里,键值对按照键的排序顺序进行排序。因为
LinkedHashMap
System.out.println()
可以遍历它,因此可以看到遍历的结果
// collectiontopics/LinkedHashMapDemo.java
// What you can do with a LinkedHashMap
import java.util.*;
import onjava.*;
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<Integer,String> linkedMap =
new LinkedHashMap<>(new CountMap(9));
System.out.println(linkedMap);
// Least-recently-used order:
linkedMap =
new LinkedHashMap<>(16, 0.75f, true);
linkedMap.putAll(new CountMap(9));
System.out.println(linkedMap);
for(int i = 0; i < 6; i++)
linkedMap.get(i);
System.out.println(linkedMap);
linkedMap.get(0);
System.out.println(linkedMap);
}
}
/* Output:
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0}
{6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}
*/
这些键值对确实是按照插入顺序进行遍历,即使对于
集合工具类
集合有许多独立的实用工具程序,在addAll()
, reverseOrder()
和 binarySearch()
。以下是其他内容(同步和不可修改的实用工具程序将在后面的章节中介绍
方法 | 描述 |
---|---|
checkedCollection(Collection<T> c, Class<T> type) checkedList(List<T> list, Class<T> type) checkedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) checkedSet(Set<T> s, Class<T> type) checkedSortedMap(SortedMap<K, V> m, Class<K> keyType, Class<V> valueType) checkedSortedSet(SortedSet<T> s, Class<T> type) |
生成 这些方法的使用在第九章 多态章节的“动态类型安全”标题下进行了展示。 |
max(Collection) min(Collection) |
使用 |
max(Collection, Comparator) min(Collection, Comparator) |
使用 |
indexOfSubList(List source, List target) | 返回 |
lastIndexOfSubList(List source, List target) | 返回 |
replaceAll(List<T> list, T oldVal, T newVal) | 用 |
reverse(List) | 反转列表 |
reverseOrder() reverseOrder(Comparator<T>) |
返回一个 |
rotate(List, int distance) | 将所有元素向前移动 |
shuffle(List) shuffle(List, Random) |
随机置换指定列表(即打乱顺序 |
sort(List<T>) sort(List<T>, Comparator<? super T> c) |
第一个版本使用元素的自然顺序排序该 |
copy(List<? super T> dest, List<? extends T> src) | 将 |
swap(List, int i, int j) | 交换 |
fill(List<? super T>, T x) | 用 |
nCopies(int n, T x) | 返回大小为 |
disjoint(Collection, Collection) | 如果两个集合没有共同元素,则返回 |
frequency(Collection, Object x) | 返回 |
emptyList() emptyMap() emptySet() |
返回不可变的空 |
singleton(T x) singletonList(T x) singletonMap(K key, V value) |
生成一个不可变的 |
list(Enumeration<T> e) | 生成一个 |
enumeration(Collection<T>) | 为参数集合生成一个旧式的 |
请注意, min()
和 max()
使用binarySearch()
之前,将会对sort()
排序
下面是一个示例,展示了上表中大多数实用工具程序的基本用法:
// collectiontopics/Utilities.java
// Simple demonstrations of the Collections utilities
import java.util.*;
public class Utilities {
static List<String> list = Arrays.asList(
"one Two three Four five six one".split(" "));
public static void main(String[] args) {
System.out.println(list);
System.out.println("'list' disjoint (Four)?: " +
Collections.disjoint(list,
Collections.singletonList("Four")));
System.out.println(
"max: " + Collections.max(list));
System.out.println(
"min: " + Collections.min(list));
System.out.println(
"max w/ comparator: " + Collections.max(list,
String.CASE_INSENSITIVE_ORDER));
System.out.println(
"min w/ comparator: " + Collections.min(list,
String.CASE_INSENSITIVE_ORDER));
List<String> sublist =
Arrays.asList("Four five six".split(" "));
System.out.println("indexOfSubList: " +
Collections.indexOfSubList(list, sublist));
System.out.println("lastIndexOfSubList: " +
Collections.lastIndexOfSubList(list, sublist));
Collections.replaceAll(list, "one", "Yo");
System.out.println("replaceAll: " + list);
Collections.reverse(list);
System.out.println("reverse: " + list);
Collections.rotate(list, 3);
System.out.println("rotate: " + list);
List<String> source =
Arrays.asList("in the matrix".split(" "));
Collections.copy(list, source);
System.out.println("copy: " + list);
Collections.swap(list, 0, list.size() - 1);
System.out.println("swap: " + list);
Collections.shuffle(list, new Random(47));
System.out.println("shuffled: " + list);
Collections.fill(list, "pop");
System.out.println("fill: " + list);
System.out.println("frequency of 'pop': " +
Collections.frequency(list, "pop"));
List<String> dups =
Collections.nCopies(3, "snap");
System.out.println("dups: " + dups);
System.out.println("'list' disjoint 'dups'?: " +
Collections.disjoint(list, dups));
// Getting an old-style Enumeration:
Enumeration<String> e =
Collections.enumeration(dups);
Vector<String> v = new Vector<>();
while(e.hasMoreElements())
v.addElement(e.nextElement());
// Converting an old-style Vector
// to a List via an Enumeration:
ArrayList<String> arrayList =
Collections.list(v.elements());
System.out.println("arrayList: " + arrayList);
}
}
/* Output:
[one, Two, three, Four, five, six, one]
'list' disjoint (Four)?: false
max: three
min: Four
max w/ comparator: Two
min w/ comparator: five
indexOfSubList: 3
lastIndexOfSubList: 3
replaceAll: [Yo, Two, three, Four, five, six, Yo]
reverse: [Yo, six, five, Four, three, Two, Yo]
rotate: [three, Two, Yo, Yo, six, five, Four]
copy: [in, the, matrix, Yo, six, five, Four]
swap: [Four, the, matrix, Yo, six, five, in]
shuffled: [six, matrix, the, Four, Yo, five, in]
fill: [pop, pop, pop, pop, pop, pop, pop]
frequency of 'pop': 7
dups: [snap, snap, snap]
'list' disjoint 'dups'?: true
arrayList: [snap, snap, snap]
*/
输出解释了每种实用方法的行为。请注意由于大小写的缘故,普通版本的 min()
和 max()
与带有
排序和搜索列表
用于执行排序和搜索
// collectiontopics/ListSortSearch.java
// Sorting/searching Lists with Collections utilities
import java.util.*;
public class ListSortSearch {
public static void main(String[] args) {
List<String> list =
new ArrayList<>(Utilities.list);
list.addAll(Utilities.list);
System.out.println(list);
Collections.shuffle(list, new Random(47));
System.out.println("Shuffled: " + list);
// Use ListIterator to trim off last elements:
ListIterator<String> it = list.listIterator(10);
while(it.hasNext()) {
it.next();
it.remove();
}
System.out.println("Trimmed: " + list);
Collections.sort(list);
System.out.println("Sorted: " + list);
String key = list.get(7);
int index = Collections.binarySearch(list, key);
System.out.println(
"Location of " + key + " is " + index +
", list.get(" + index + ") = " +
list.get(index));
Collections.sort(list,
String.CASE_INSENSITIVE_ORDER);
System.out.println(
"Case-insensitive sorted: " + list);
key = list.get(7);
index = Collections.binarySearch(list, key,
String.CASE_INSENSITIVE_ORDER);
System.out.println(
"Location of " + key + " is " + index +
", list.get(" + index + ") = " +
list.get(index));
}
}
/* Output:
[one, Two, three, Four, five, six, one, one, Two,
three, Four, five, six, one]
Shuffled: [Four, five, one, one, Two, six, six, three,
three, five, Four, Two, one, one]
Trimmed: [Four, five, one, one, Two, six, six, three,
three, five]
Sorted: [Four, Two, five, five, one, one, six, six,
three, three]
Location of six is 7, list.get(7) = six
Case-insensitive sorted: [five, five, Four, one, one,
six, six, three, three, Two]
Location of three is 7, list.get(7) = three
*/
就像使用数组进行搜索和排序一样,如果使用binarySearch()
。
该程序还演示了shuffle()
方法,该方法随机打乱了
创建不可修改的Collection 或Map
通常,创建
// collectiontopics/ReadOnly.java
// Using the Collections.unmodifiable methods
import java.util.*;
import onjava.*;
public class ReadOnly {
static Collection<String> data =
new ArrayList<>(Countries.names(6));
public static void main(String[] args) {
Collection<String> c =
Collections.unmodifiableCollection(
new ArrayList<>(data));
System.out.println(c); // Reading is OK
//- c.add("one"); // Can't change it
List<String> a = Collections.unmodifiableList(
new ArrayList<>(data));
ListIterator<String> lit = a.listIterator();
System.out.println(lit.next()); // Reading is OK
//- lit.add("one"); // Can't change it
Set<String> s = Collections.unmodifiableSet(
new HashSet<>(data));
System.out.println(s); // Reading is OK
//- s.add("one"); // Can't change it
// For a SortedSet:
Set<String> ss =
Collections.unmodifiableSortedSet(
new TreeSet<>(data));
Map<String,String> m =
Collections.unmodifiableMap(
new HashMap<>(Countries.capitals(6)));
System.out.println(m); // Reading is OK
//- m.put("Ralph", "Howdy!");
// For a SortedMap:
Map<String,String> sm =
Collections.unmodifiableSortedMap(
new TreeMap<>(Countries.capitals(6)));
}
}
/* Output:
[ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI]
ALGERIA
[BENIN, BOTSWANA, ANGOLA, BURKINA FASO, ALGERIA,
BURUNDI]
{BENIN=Porto-Novo, BOTSWANA=Gaberone, ANGOLA=Luanda,
BURKINA FASO=Ouagadougou, ALGERIA=Algiers,
BURUNDI=Bujumbura}
*/
为特定类型调用 “unmodifiable” 方法不会导致编译时检查,但是一旦发生转换,对修改特定集合内容的任何方法调用都将产生
在每种情况下,在将集合设置为只读之前,必须使用有意义的数据填充集合。填充完成后,最好的方法是用 “unmodifiable” 方法调用生成的引用替换现有引用。这样,一旦使得内容无法修改,那么就不会冒有意外更改内容的风险。另一方面,此工具还允许将可修改的集合保留为类中的私有集合,并从方法调用处返回对该集合的只读引用。所以,你可以在类内修改它,但其他人只能读它。
同步Collection 或Map
// collectiontopics/Synchronization.java
// Using the Collections.synchronized methods
import java.util.*;
public class Synchronization {
public static void main(String[] args) {
Collection<String> c =
Collections.synchronizedCollection(
new ArrayList<>());
List<String> list = Collections
.synchronizedList(new ArrayList<>());
Set<String> s = Collections
.synchronizedSet(new HashSet<>());
Set<String> ss = Collections
.synchronizedSortedSet(new TreeSet<>());
Map<String,String> m = Collections
.synchronizedMap(new HashMap<>());
Map<String,String> sm = Collections
.synchronizedSortedMap(new TreeMap<>());
}
}
最好立即通过适当的 “synchronized” 方法传递新集合,如上所示。这样,就不会意外地暴露出非同步版本。
Fail Fast
size()
之后集合的大小会缩小…有许多灾难情景。
通过创建迭代器并向迭代器指向的集合中添加元素,可以很容易地看到操作中的
// collectiontopics/FailFast.java
// Demonstrates the "fail-fast" behavior
import java.util.*;
public class FailFast {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
c.add("An object");
try {
String s = it.next();
} catch(ConcurrentModificationException e) {
System.out.println(e);
}
}
}
/* Output:
java.util.ConcurrentModificationException
*/
异常来自于在从集合中获得迭代器之后,又尝试在集合中添加元素。程序的两个部分可能会修改同一个集合,这种可能性的存在会产生不确定状态,因此异常会通知你更改代码。在这种情况下,应先将所有元素添加到集合,然后再获取迭代器。
ConcurrentHashMap ,
持有引用
如果一个对象是 可达的(reachable
使用
可以通过使用
按照
使用
// collectiontopics/References.java
// Demonstrates Reference objects
import java.lang.ref.*;
import java.util.*;
class VeryBig {
private static final int SIZE = 10000;
private long[] la = new long[SIZE];
private String ident;
VeryBig(String id) { ident = id; }
@Override
public String toString() { return ident; }
@Override
protected void finalize() {
System.out.println("Finalizing " + ident);
}
}
public class References {
private static ReferenceQueue<VeryBig> rq =
new ReferenceQueue<>();
public static void checkQueue() {
Reference<? extends VeryBig> inq = rq.poll();
if(inq != null)
System.out.println("In queue: " + inq.get());
}
public static void main(String[] args) {
int size = 10;
// Or, choose size via the command line:
if(args.length > 0)
size = Integer.valueOf(args[0]);
LinkedList<SoftReference<VeryBig>> sa =
new LinkedList<>();
for(int i = 0; i < size; i++) {
sa.add(new SoftReference<>(
new VeryBig("Soft " + i), rq));
System.out.println(
"Just created: " + sa.getLast());
checkQueue();
}
LinkedList<WeakReference<VeryBig>> wa =
new LinkedList<>();
for(int i = 0; i < size; i++) {
wa.add(new WeakReference<>(
new VeryBig("Weak " + i), rq));
System.out.println(
"Just created: " + wa.getLast());
checkQueue();
}
SoftReference<VeryBig> s =
new SoftReference<>(new VeryBig("Soft"));
WeakReference<VeryBig> w =
new WeakReference<>(new VeryBig("Weak"));
System.gc();
LinkedList<PhantomReference<VeryBig>> pa =
new LinkedList<>();
for(int i = 0; i < size; i++) {
pa.add(new PhantomReference<>(
new VeryBig("Phantom " + i), rq));
System.out.println(
"Just created: " + pa.getLast());
checkQueue();
}
}
}
/* Output: (First and Last 10 Lines)
Just created: java.lang.ref.SoftReference@15db9742
Just created: java.lang.ref.SoftReference@6d06d69c
Just created: java.lang.ref.SoftReference@7852e922
Just created: java.lang.ref.SoftReference@4e25154f
Just created: java.lang.ref.SoftReference@70dea4e
Just created: java.lang.ref.SoftReference@5c647e05
Just created: java.lang.ref.SoftReference@33909752
Just created: java.lang.ref.SoftReference@55f96302
Just created: java.lang.ref.SoftReference@3d4eac69
Just created: java.lang.ref.SoftReference@42a57993
...________...________...________...________...
Just created: java.lang.ref.PhantomReference@45ee12a7
In queue: null
Just created: java.lang.ref.PhantomReference@330bedb4
In queue: null
Just created: java.lang.ref.PhantomReference@2503dbd3
In queue: null
Just created: java.lang.ref.PhantomReference@4b67cf4d
In queue: null
Just created: java.lang.ref.PhantomReference@7ea987ac
In queue: null
*/
当运行此程序(将输出重定向到文本文件以查看页面中的输出)时,将会看到对象是被垃圾收集了的,虽然仍然可以通过get()
来获取实际的对象引用
WeakHashMap
集合类库中有一个特殊的
由于这是一种节省存储空间的技术,因此
// collectiontopics/CanonicalMapping.java
// Demonstrates WeakHashMap
import java.util.*;
class Element {
private String ident;
Element(String id) { ident = id; }
@Override
public String toString() { return ident; }
@Override
public int hashCode() {
return Objects.hashCode(ident);
}
@Override
public boolean equals(Object r) {
return r instanceof Element &&
Objects.equals(ident, ((Element)r).ident);
}
@Override
protected void finalize() {
System.out.println("Finalizing " +
getClass().getSimpleName() + " " + ident);
}
}
class Key extends Element {
Key(String id) { super(id); }
}
class Value extends Element {
Value(String id) { super(id); }
}
public class CanonicalMapping {
public static void main(String[] args) {
int size = 1000;
// Or, choose size via the command line:
if(args.length > 0)
size = Integer.valueOf(args[0]);
Key[] keys = new Key[size];
WeakHashMap<Key,Value> map =
new WeakHashMap<>();
for(int i = 0; i < size; i++) {
Key k = new Key(Integer.toString(i));
Value v = new Value(Integer.toString(i));
if(i % 3 == 0)
keys[i] = k; // Save as "real" references
map.put(k, v);
}
System.gc();
}
}
hashCode()
和 equals()
,因为它将被用作散列数据结构中的键。 hashCode()
的内容在附录:理解
运行程序,你会看到垃圾收集器每三个键跳过一次。对该键的普通引用也被放置在
Java 1.0 / 1.1 的集合类
不幸的是,许多代码是使用
Vector 和Enumeration
迭代器的boolean hasMoreElements()
返回 true
, Object nextElement()
返回此
此外,可以使用 Collections.enumeration()
方法为任何
// collectiontopics/Enumerations.java
// Java 1.0/1.1 Vector and Enumeration
import java.util.*;
import onjava.*;
public class Enumerations {
public static void main(String[] args) {
Vector<String> v =
new Vector<>(Countries.names(10));
Enumeration<String> e = v.elements();
while(e.hasMoreElements())
System.out.print(e.nextElement() + ", ");
// Produce an Enumeration from a Collection:
e = Collections.enumeration(new ArrayList<>());
}
}
/* Output:
ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO,
BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN
REPUBLIC, CHAD,
*/
要生成elements()
,然后可以使用它来执行向前迭代。
最后一行创建一个enumeration()
来将
Hashtable
正如你在本附录中的性能比较中所看到的,基本的
Stack
之前使用
这是
// collectiontopics/Stacks.java
// Demonstration of Stack Class
import java.util.*;
enum Month { JANUARY, FEBRUARY, MARCH, APRIL,
MAY, JUNE, JULY, AUGUST, SEPTEMBER,
OCTOBER, NOVEMBER }
public class Stacks {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
for(Month m : Month.values())
stack.push(m.toString());
System.out.println("stack = " + stack);
// Treating a stack as a Vector:
stack.addElement("The last line");
System.out.println(
"element 5 = " + stack.elementAt(5));
System.out.println("popping elements:");
while(!stack.empty())
System.out.print(stack.pop() + " ");
// Using a LinkedList as a Stack:
LinkedList<String> lstack = new LinkedList<>();
for(Month m : Month.values())
lstack.addFirst(m.toString());
System.out.println("lstack = " + lstack);
while(!lstack.isEmpty())
System.out.print(lstack.removeFirst() + " ");
// Using the Stack class from
// the Collections Chapter:
onjava.Stack<String> stack2 =
new onjava.Stack<>();
for(Month m : Month.values())
stack2.push(m.toString());
System.out.println("stack2 = " + stack2);
while(!stack2.isEmpty())
System.out.print(stack2.pop() + " ");
}
}
/* Output:
stack = [JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE,
JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER]
element 5 = JUNE
popping elements:
The last line NOVEMBER OCTOBER SEPTEMBER AUGUST JULY
JUNE MAY APRIL MARCH FEBRUARY JANUARY lstack =
[NOVEMBER, OCTOBER, SEPTEMBER, AUGUST, JULY, JUNE, MAY,
APRIL, MARCH, FEBRUARY, JANUARY]
NOVEMBER OCTOBER SEPTEMBER AUGUST JULY JUNE MAY APRIL
MARCH FEBRUARY JANUARY stack2 = [NOVEMBER, OCTOBER,
SEPTEMBER, AUGUST, JULY, JUNE, MAY, APRIL, MARCH,
FEBRUARY, JANUARY]
NOVEMBER OCTOBER SEPTEMBER AUGUST JULY JUNE MAY APRIL
MARCH FEBRUARY JANUARY
*/
push()
压入到栈中,然后使用 pop()
从栈顶部取出。为了说明一点,将elementAt()
。
如前所述,在需要栈行为时使用
BitSet
此外,
当添加更多元素时,普通集合会扩展,
// collectiontopics/Bits.java
// Demonstration of BitSet
import java.util.*;
public class Bits {
public static void printBitSet(BitSet b) {
System.out.println("bits: " + b);
StringBuilder bbits = new StringBuilder();
for(int j = 0; j < b.size() ; j++)
bbits.append(b.get(j) ? "1" : "0");
System.out.println("bit pattern: " + bbits);
}
public static void main(String[] args) {
Random rand = new Random(47);
// Take the LSB of nextInt():
byte bt = (byte)rand.nextInt();
BitSet bb = new BitSet();
for(int i = 7; i >= 0; i--)
if(((1 << i) & bt) != 0)
bb.set(i);
else
bb.clear(i);
System.out.println("byte value: " + bt);
printBitSet(bb);
short st = (short)rand.nextInt();
BitSet bs = new BitSet();
for(int i = 15; i >= 0; i--)
if(((1 << i) & st) != 0)
bs.set(i);
else
bs.clear(i);
System.out.println("short value: " + st);
printBitSet(bs);
int it = rand.nextInt();
BitSet bi = new BitSet();
for(int i = 31; i >= 0; i--)
if(((1 << i) & it) != 0)
bi.set(i);
else
bi.clear(i);
System.out.println("int value: " + it);
printBitSet(bi);
// Test bitsets >= 64 bits:
BitSet b127 = new BitSet();
b127.set(127);
System.out.println("set bit 127: " + b127);
BitSet b255 = new BitSet(65);
b255.set(255);
System.out.println("set bit 255: " + b255);
BitSet b1023 = new BitSet(512);
b1023.set(1023);
b1023.set(1024);
System.out.println("set bit 1023: " + b1023);
}
}
/* Output:
byte value: -107
bits: {0, 2, 4, 7}
bit pattern: 101010010000000000000000000000000000000000
0000000000000000000000
short value: 1302
bits: {1, 2, 4, 8, 10}
bit pattern: 011010001010000000000000000000000000000000
0000000000000000000000
int value: -2014573909
bits: {0, 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24,
25, 26, 31}
bit pattern: 110101010101000000110111111000010000000000
0000000000000000000000
set bit 127: {127}
set bit 255: {255}
set bit 1023: {1023, 1024}
*/
随机数生成器用于创建随机
对于可以命名的固定标志集, EnumSet (参见第二十二章:枚举章节)通常比
本章小结
集合可以说是编程语言中最常用的工具。有些语言(例如
正如在第十二章:集合章节中看到的那样,可以使用集合执行许多非常有用的操作,而不需要太多努力。但是,在某些时候,为了正确地使用它们而不得不更多地了解集合,特别是,必须充分了解散列操作以编写自己的 hashCode()
方法(并且必须知道何时需要
集合库的设计很困难(大多数库设计问题都是如此
-
java.util 中的 Map 使用 Map 的 getKey()
和getValue()
执行批量复制,因此这是有效的。如果自定义Map 只是复制整个 Map.Entry ,那么这种方法就会出现问题。 ↩︎ -
虽然当我用这种方式描述它的时候听起来很奇怪而且好像没什么用处,但在第十九章 类型信息章节中已经看到过,这种动态行为也可以非常强大有用。 ↩︎
-
如果这些加速仍然无法满足性能需求,则可以通过编写自己的
Map 并将其自定义为特定类型来进一步加速表查找,以避免因向 对象 转换而导致的延迟。为了达到更高的性能水平,速度爱好者可以使用 Donald Knuth 的《计算机程序设计艺术(第3 卷) :排序与查找》 (第二版) ,将溢出桶列表(overflow bucket lists)替换为具有两个额外优势的阵列:它们可以针对磁盘存储进行优化,并且它们可以节省大部分创建和回收个别记录(individual records)的时间。 ↩︎