From d7c94d511b269e1e71070070da67a9d43234a1c1 Mon Sep 17 00:00:00 2001 From: 1326670425 <1326670425@qq.com> Date: Sat, 20 Jul 2019 17:06:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=99=84=E5=BD=95=EF=BC=9A=E9=9B=86?= =?UTF-8?q?=E5=90=88=E4=B8=BB=E9=A2=98=20=E7=BF=BB=E8=AF=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=87=B3=20=E9=9B=86=E5=90=88=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/Appendix-Collection-Topics.md | 784 +++++++++++++++++++++++- 1 file changed, 783 insertions(+), 1 deletion(-) diff --git a/docs/book/Appendix-Collection-Topics.md b/docs/book/Appendix-Collection-Topics.md index 8123c28..37d2f54 100644 --- a/docs/book/Appendix-Collection-Topics.md +++ b/docs/book/Appendix-Collection-Topics.md @@ -810,10 +810,792 @@ public class NavMap { ## 填充集合 +与 **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 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 { + 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 set = Suppliers.create( + LinkedHashSet::new, new Government(), 15); + System.out.println(set); + List 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 { + 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 Pair make(K k, V v) { + return new Pair(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> { + private int number = 1; + private char letter = 'A'; + @Override + public Pair get() { + return new Pair<>(number++, "" + letter++); + } +} + +public class StreamFillMaps { + public static void main(String[] args) { + Map 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 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 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 Map + basic(Supplier> pairGen, int size) { + return Stream.generate(pairGen) + .limit(size) + .collect(Collectors + .toMap(Pair::key, Pair::value)); + } + public static Map + basic(Supplier keyGen, + Supplier valueGen, int size) { + return Stream.generate( + () -> Pair.make(keyGen.get(), valueGen.get())) + .limit(size) + .collect(Collectors + .toMap(Pair::key, Pair::value)); + } + public static > + M create(Supplier keyGen, + Supplier valueGen, + Supplier 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 mcs = FillMap.basic( + new Rand.String(4), new Count.Integer(), 7); + System.out.println(mcs); + HashMap hashm = + FillMap.create(new Rand.String(4), + new Count.Integer(), HashMap::new, 7); + System.out.println(hashm); + LinkedHashMap 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自定义集合和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 { + 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 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 { + 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 { + 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> 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 { + private static class Entry + implements Map.Entry { + 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> { + 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> { + // 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 next() { + entry.index++; + return entry; + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + @Override + public + Iterator> iterator() { + return new Iter(); + } + } + private static + Set> entries = + new EntrySet(DATA.length); + @Override + public Set> entrySet() { + return entries; + } + } + // Create a partial map of 'size' countries: + static Map select(final int size) { + return new FlyweightMap() { + @Override + public Set> entrySet() { + return new EntrySet(size); + } + }; + } + static Map map = new FlyweightMap(); + public static Map capitals() { + return map; // The entire map + } + public static Map capitals(int size) { + return select(size); // A partial map + } + static List names = + new ArrayList<>(map.keySet()); + // All the names: + public static List names() { return names; } + // A partial list: + public static List 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()** 方法。 ## 集合功能 From 97270c6dd60cc2ada7ae424043c8d83e871cddb5 Mon Sep 17 00:00:00 2001 From: 1326670425 <1326670425@qq.com> Date: Sun, 21 Jul 2019 14:57:24 +0800 Subject: [PATCH 2/2] Update SUMMARY.md --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index 346fce9..1a57fa6 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -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和存储顺序)