附录:集合主题 翻译更新至 填充集合

This commit is contained in:
1326670425
2019-07-19 18:31:09 +08:00
parent 6368f9e5c4
commit d5d45fcb50

View File

@@ -362,23 +362,453 @@ Cyan
可以看到,使用 **LinkedHashMap** 确实能够保留 **HTMLColors.ARRAY** 的顺序。
<!-- List Behavior -->
## List表现
## List行为
**Lists** 是存储和检索对象(次于数组)的最基本方法。基本列表操作包括:
- **add()** 用于插入元素
- **get()** 用于随机访问元素
- **iterator()** 获取序列上的一个 **Iterator**
- **stream()** 生成元素的一个 **Stream**
列表构造方法始终保留元素的添加顺序。
以下示例中的方法各自涵盖了一组不同的行为:每个 **List** 可以执行的操作( **basicTest()** ),使用 **Iterator** **iterMotion()** )遍历序列,使用 **Iterator** **iterManipulation()** )更改内容,查看 **List** 操作( **testVisual()** )的效果,以及仅可用于 **LinkedLists** 的操作:
```java
// 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()**方法调用是为了展示正确的语法尽管获取了返回值但不会使用它。在某些情况下根本不会去获取返回值。在使用这些方法之前请查看JDK文档中这些方法的完整用法。
<!-- Set Behavior -->
## Set表现
## Set行为
**Set** 的主要用处是测试成员身份,不过也可以将其用作删除重复元素的工具。如果不关心元素顺序或并发性, **HashSet** 总是最好的选择,因为它是专门为了快速查找而设计的(这里使用了在[附录理解equals和hashCode方法]()章节中探讨的散列函数)。
其它的 **Set** 实现产生不同的排序行为:
```java
// 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
*/
```
这里需要使用 **@SuppressWarnings(“unchecked”)** ,因为这里将一个 **String** (可能是任何东西)传递给了 **Class.forName(type).newInstance()** 。编译器并不能保证这是一次成功的操作。
**RLIST****HTMLColors.LIST** 的反转版本。因为 **Collections.reverse()** 是通过修改参数来执行反向操作,而不是返回包含反向元素的新 **List** ,所以该调用在 **static** 块内执行。 **RLIST** 可以防止我们意外地认为 **Set** 对其结果进行了排序。
**HashSet** 的输出结果似乎没有可辨别的顺序,因为它是基于散列函数的。 **TreeSet****ConcurrentSkipListSet** 都对它们的元素进行了排序,它们都实现了 **SortedSet** 接口来标识这个特点。因为实现该接口的 **Set** 按顺序排列,所以该接口还有一些其他的可用操作。 **LinkedHashSet****CopyOnWriteArraySet** 尽管没有用于标识的接口,但它们还是保留了元素的插入顺序。
**ConcurrentSkipListSet****CopyOnWriteArraySet** 是线程安全的。
在附录的最后,我们将了解在非 **HashSet** 实现的 **Set** 上添加额外排序的性能成本,以及不同实现中的任何其他功能的成本。
<!-- Using Functional Operations with any Map -->
## 在Map中使用函数式操作
**Collection** 接口一样,**forEach()** 也内置在 **Map** 接口中。但是如果想要执行任何其他的基本功能操作,比如 **map()** **flatMap()** **reduce()** 或 **filter()** 时,该怎么办? 查看 **Map** 接口发现并没有这些。
可以通过 **entrySet()** 连接到这些方法,该方法会生成一个由 **Map.Entry** 对象组成的 **Set** 。这个 **Set** 包含 **stream()****parallelStream()** 方法。只需要记住一件事,这里正在使用的是 **Map.Entry** 对象:
```java
// 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
*/
```
生成 **Stream** 后,所有的基本功能方法,甚至更多就都可以使用了。
<!-- Selecting Parts of a Map -->
## 选择Map的部分
## 选择Map片段
**TreeMap****ConcurrentSkipListMap** 实现的 **NavigableMap** 接口解决了需要选择Map片段的问题。下面是一个示例使用了 **HTMLColors**
```java
// 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
*/
```
**main()** 方法中可以看到 **NavigableMap** 的各种功能。 因为 **NavigableMap** 具有键顺序,所以它使用了 **firstEntry()****lastEntry()** 的概念。调用 **headMap()** 会生成一个 **NavigableMap** ,其中包含了从 **Map** 的开头到 **headMap()** 参数中所指向的一组元素,其中 **boolean** 值指示结果中是否包含该参数。调用 **tailMap()** 执行了类似的操作,只不过是从参数开始到 **Map** 的末尾。 **subMap()** 则允许生成 **Map** 中间的一部分。
**ceilingEntry()** 从当前键值对向上搜索下一个键值对,**floorEntry()** 则是向下搜索。 **descendingMap()** 反转了 **NavigableMap** 的顺序。
如果需要通过分割 **Map** 来简化所正在解决的问题,则 **NavigableMap** 可以做到。具有类似的功能的其它集合实现也可以用来帮助解决问题。
<!-- Filling Collections -->
## 集合的fill方法
## 填充集合
<!-- Custom Collection and Map using Flyweight -->