mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-24 06:53:27 +08:00
@@ -369,7 +369,7 @@
|
||||
* [在Map中使用函数式操作](docs/book/Appendix-Collection-Topics.md#在Map中使用函数式操作)
|
||||
* [选择Map片段](docs/book/Appendix-Collection-Topics.md#选择Map片段)
|
||||
* [填充集合](docs/book/Appendix-Collection-Topics.md#填充集合)
|
||||
* [使用Flyweight自定义集合和Map](docs/book/Appendix-Collection-Topics.md#使用Flyweight自定义集合和Map)
|
||||
* [使用享元(Flyweight)自定义Collection和Map](docs/book/Appendix-Collection-Topics.md#使用享元(Flyweight)自定义Collection和Map)
|
||||
* [集合功能](docs/book/Appendix-Collection-Topics.md#集合功能)
|
||||
* [可选操作](docs/book/Appendix-Collection-Topics.md#可选操作)
|
||||
* [Set和存储顺序](docs/book/Appendix-Collection-Topics.md#Set和存储顺序)
|
||||
|
||||
@@ -810,10 +810,792 @@ public class NavMap {
|
||||
<!-- Filling Collections -->
|
||||
## 填充集合
|
||||
|
||||
与 **Arrays** 一样,这里有一个名为 **Collections** 的伴随类(companion class),包含了一些 **static** 的实用方法,其中包括一个名为 **fill()** 的方法。 **fill()** 只复制整个集合中的单个对象引用。此外,它仅适用于 **List** 对象,但结果列表可以传递给构造方法或 **addAll()** 方法:
|
||||
|
||||
```java
|
||||
// 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!]
|
||||
*/
|
||||
```
|
||||
|
||||
这个示例展示了两种使用对单个对象的引用来填充 **Collection** 的方法。 第一个: **Collections.nCopies()** ,创建一个 **List**,并传递给 **ArrayList** 的构造方法,进而填充了 **ArrayList** 。
|
||||
|
||||
**StringAddress** 中的 **toString()** 方法调用了 **Object.toString()** ,它先生成类名,后跟着对象的哈希码的无符号十六进制表示(哈希吗由 **hashCode()** 方法生成)。 输出显示所有的引用都指向同一个对象。调用第二个方法 **Collections.fill()** 后也是如此。 **fill()** 方法的用处非常有限,它只能替换 **List** 中已有的元素,而且不会添加新元素,
|
||||
|
||||
### 使用 Suppliers 填充集合
|
||||
|
||||
[第二十章 泛型]()章节中介绍的 **onjava.Suppliers** 类为填充集合提供了通用解决方案。 这是一个使用 **Suppliers** 初始化几种不同类型的 **Collection** 的示例:
|
||||
|
||||
```java
|
||||
// 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]
|
||||
*/
|
||||
```
|
||||
|
||||
**LinkedHashSet** 中的的元素按插入顺序排列,因为它维护一个链表来保存该顺序。
|
||||
|
||||
但是请注意示例的第二部分:大多数情况下都可以使用 **Stream** 来创建和填充 **Collection** 。在本例中的 **Stream** 版本不需要声明 **Supplier** 所想要创建的元素数量;,它直接吸收了 **Stream** 中的所有元素。
|
||||
|
||||
尽可能优先选择 **Stream** 来解决问题。
|
||||
|
||||
### Map Suppliers
|
||||
|
||||
使用 **Supplier** 来填充 **Map** 时需要一个 **Pair** 类,因为每次调用一个 **Supplier** 的 **get()** 方法时,都必须生成一对对象(一个键和一个值):
|
||||
|
||||
```java
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pair** 是一个只读的 *数据传输对象* (Data Transfer Object)或 *信使* (Messenger)。 这与[第二十章 泛型]()章节中的 **Tuple2** 基本相同,但名字更适合 **Map** 初始化。我还添加了静态的 **make()** 方法,以便为创建 **Pair** 对象提供一个更简洁的名字。
|
||||
|
||||
Java 8 的 **Stream** 提供了填充 **Map** 的便捷方法:
|
||||
|
||||
```java
|
||||
// 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}
|
||||
*/
|
||||
```
|
||||
|
||||
上面的示例中出现了一个模式,可以使用它来创建一个自动创建和填充 **Map** 的工具:
|
||||
|
||||
```java
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
basic() 方法生成一个默认的 **Map** ,而 **create()** 方法允许指定一个确切的 **Map** 类型,并返回那个确切的类型。
|
||||
|
||||
下面是一个测试:
|
||||
|
||||
```java
|
||||
// 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}
|
||||
*/
|
||||
```
|
||||
|
||||
<!-- Custom Collection and Map using Flyweight -->
|
||||
## 使用Flyweight自定义集合和Map
|
||||
## 使用享元(Flyweight)自定义Collection和Map
|
||||
|
||||
本节介绍如何创建自定义 **Collection** 和 **Map** 实现。每个 **java.util** 中的集合都有自己的 **Abstract** 类,它提供了该集合的部分实现,因此只需要实现必要的方法来生成所需的集合。你将看到通过继承 **java.util.Abstract** 类来创建自定义 **Map** 和 **Collection** 是多么简单。例如,要创建一个只读的 **Set** ,则可以从 **AbstractSet** 继承并实现 **iterator()** 和 **size()** 。最后一个示例是生成测试数据的另一种方法。生成的集合通常是只读的,并且所提供的方法最少。
|
||||
|
||||
该解决方案还演示了 *享元* (Flyweight)设计模式。当普通解决方案需要太多对象时,或者当生成普通对象占用太多空间时,可以使用享元。享元设计模式将对象的一部分外部化(externalizes)。相比于把对象的所有内容都包含在对象中,这样做使得对象的部分或者全部可以在更有效的外部表中查找,或通过一些节省空间的其他计算生成。
|
||||
|
||||
下面是一个可以是任何大小的 **List** ,并且(有效地)使用 **Integer** 数据进行预初始化。要从 **AbstractList** 创建只读 **List** ,必须实现 **get()** 和 **size()**:
|
||||
|
||||
```java
|
||||
// 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
|
||||
*/
|
||||
```
|
||||
|
||||
只有当想要限制 **List** 的长度时, **size** 值才是重要的,就像在 **main()** 中那样。即使在这种情况下, **get()** 也会产生任何值。
|
||||
|
||||
这个类是享元模式的一个简洁的例子。当需要的时候, **get()** “计算”所需的值,因此没必要存储和初始化实际的底层 **List** 结构。
|
||||
|
||||
在大多数程序中,这里所保存的存储结构永远都不会改变。但是,它允许用非常大的 **index** 来调用 **List.get()** ,而 **List** 并不需要填充到这么大。此外,还可以在程序中大量使用 **CountingIntegerLists** 而无需担心存储问题。实际上,享元的一个好处是它允许使用更好的抽象而不用担心资源。
|
||||
|
||||
可以使用享元设计模式来实现具有任何大小数据集的其他“初始化”自定义集合。下面是一个 **Map** ,它为每一个 **Integer** 键产生唯一的值:
|
||||
|
||||
```java
|
||||
// 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
|
||||
*/
|
||||
```
|
||||
|
||||
要创建一个只读的 **Map** ,则从 **AbstractMap** 继承并实现 **entrySet()** 。私有的 **value()** 方法计算任何键的值,并在 **get()** 和 **Entry.getValue()** 中使用。可以忽略 **CountMap** 的大小。
|
||||
|
||||
这里是使用了 **LinkedHashSet** 而不是创建自定义 **Set** 类,因此并未完全实现享元。只有在调用 **entrySet()** 时才会生成此对象。
|
||||
|
||||
现在创建一个更复杂的享元。这个示例中的数据集是世界各国及其首都的 **Map** 。 **capitals()** 方法生成一个国家和首都的 **Map** 。 **names()** 方法生成一个由国家名字组成的 **List** 。 当给定了表示所需大小的 **int** 参数时,两种方法都生成对应大小的列表片段:
|
||||
|
||||
```java
|
||||
// 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
|
||||
*/
|
||||
```
|
||||
|
||||
二维数组 **String DATA** 是 **public** 的,因此可以在别处使用。 **FlyweightMap** 必须实现 **entrySet()** 方法,该方法需要一个自定义 **Set** 实现和一个自定义 **Map.Entry** 类。这是实现享元的另一种方法:每个 **Map.Entry** 对象存储它自身的索引,而不是实际的键和值。当调用 **getKey()** 或 **getValue()** 时,它使用索引返回相应的 **DATA** 元素。 **EntrySet** 确保它的 **size** 不大于 **DATA** 。
|
||||
|
||||
享元的另一部分在 **EntrySet.Iterator** 中实现。相比于为 **DATA** 中的每个数据对创建一个 **Map.Entry** 对象,这里每个迭代器只有一个 **Map.Entry** 对象。 **Entry** 对象作为数据的窗口,它只包含 **String** 静态数组的索引。每次为迭代器调用 **next()** 时,**Entry** 中的索引都会递增,因此它会指向下一个数据对,然后从 **next()** 返回 **Iterators** 的单个 **Entry** 对象。
|
||||
|
||||
**select()** 方法生成一个包含所需大小的 **EntrySet** 的 **FlyweightMap** ,这用于在 **main()** 中演示的重载的 **capitals()** 和 **names()** 方法。
|
||||
|
||||
<!-- Collection Functionality -->
|
||||
## 集合功能
|
||||
|
||||
Reference in New Issue
Block a user