日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

think-in-java(17)容器深入研究

發(fā)布時(shí)間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 think-in-java(17)容器深入研究 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

注意: 17章接著 11章繼續(xù)分析 java容器,?think-in-java(11)

【17.1】完整的容器分類方法


【容器分類網(wǎng)絡(luò)解說】

1)接口:虛線框,沒有實(shí)線入邊(沒有實(shí)體類繼承關(guān)系,只有接口繼承關(guān)系);

2)抽象類:虛線框,有實(shí)現(xiàn)入邊(有實(shí)體類繼承關(guān)系);SortedSet 除外,SortedSet 明顯是一個(gè)接口, TreeSet 實(shí)現(xiàn)了?NavigableSet 接口,而?NavigableSet 接口繼承了?SortedSet ?接口(來自java 8 版本);

3)實(shí)體類:其他的都是繼承關(guān)系;

4)常用實(shí)體類: 加粗實(shí)線框;

5)Collections 和 Arrays 是工具類;


java 5 添加了新特性:

1)Queue接口(隊(duì)列可以通過 LinkedList來實(shí)現(xiàn)了,當(dāng)然包括 Stack棧數(shù)據(jù)結(jié)構(gòu))機(jī)器實(shí)現(xiàn) PriorityQueue 和 BlockingQueue阻塞隊(duì)列(21章 java 多線程編程介紹);

2)ConcurrentMap 接口 和 實(shí)現(xiàn)類 CurrentHashMap,也在21章;

3)CopyOnWriteArrayList 和 CopyOnWriteArraySet, 也是多線程的內(nèi)容;

4)EnumSet 和 EnumMap,為使用 enum 而設(shè)計(jì)的 Set 和 Map的特殊實(shí)現(xiàn);

5)Collections類中添加了多個(gè) 便利的工具方法;


【17.2】填充容器

1)用單個(gè)對象引用來填充 Collection:通過 Collections.nCopies() 方法 添加, 而通過 Collections.fill() 來替換添加;

class StringAddress {private String s;public StringAddress(String s) {this.s = s;}public String toString() {return super.toString() + " " + s;} }public class FillingLists {public static void main(String[] args) {/* 構(gòu)建4個(gè)StringAddress對象,并封裝到List ,操作的都是同一個(gè)對象 */List<StringAddress> list = new ArrayList<StringAddress>(Collections.nCopies(4, new StringAddress("Hello")));System.out.println(list);/* 把 新對象StringAddress引用 填充到list中 ,操作的都是同一個(gè)對象 *//* Collections.fill() 方法只能替換 list中已經(jīng)存在的元素,不能添加新元素 */Collections.fill(list, new StringAddress("World!"));System.out.println(list);} }

// 打印結(jié)果(List使用的都是同一個(gè)對象引用): [chapter17.StringAddress@15db9742 Hello, chapter17.StringAddress@15db9742 Hello, chapter17.StringAddress@15db9742 Hello, chapter17.StringAddress@15db9742 Hello] [chapter17.StringAddress@6d06d69c World!, chapter17.StringAddress@6d06d69c World!, chapter17.StringAddress@6d06d69c World!, chapter17.StringAddress@6d06d69c World!]

【17.2.1】一種 Generator 解決方案

1)所有Collection的子容器都有接受另一個(gè) Collection 對象的構(gòu)造器,用所接受的 Collection對象中的元素來填充新容器;


【荔枝】通過適配器模式模擬 將 Collection容器對象作為輸入?yún)?shù)傳入 另一個(gè) Collection的構(gòu)造方法

class Government implements Generator<String> {String[] foundation = ("strange women lying in ponds "+ "distributing swords is no basis for a system of " + "government").split(" ");private int index;public String next() {return foundation[index++];} }public class CollectionDataTest {public static void main(String[] args) {/* ArrayList容器作為參數(shù)傳入 LinkedHashSet 構(gòu)造器(這個(gè)適配器代碼很經(jīng)典的) */Set<String> set = new LinkedHashSet<String>(new CollectionData<String>(new Government(), 15));// Using the convenience method:set.addAll(CollectionData.list(new Government(), 15));System.out.println(set);} } /*

public class CollectionData<T> extends ArrayList<T> {// Generator類public CollectionData(Generator<T> gen, int quantity) {for (int i = 0; i < quantity; i++)add(gen.next());}// A generic convenience method:public static <T> CollectionData<T> list(Generator<T> gen, int quantity) {return new CollectionData<T>(gen, quantity);} }

// 打印結(jié)果: [strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government]

public interface Generator<T> { T next(); } ///:~

【元素的輸出順序與 插入順序相同】 因?yàn)?LinkedHashSet 保持了插入順序的鏈表列表;


【CollectionData 應(yīng)用的荔枝】

public class CollectionDataGeneration {public static void main(String[] args) {/* Convenience method */System.out.println(new ArrayList<String>(CollectionData.list(new RandomGenerator.String(9), 10)));System.out.println(new HashSet<Integer>(new CollectionData<Integer>(new RandomGenerator.Integer(), 10)));} } // 打印結(jié)果 [YNzbrnyGc, FOWZnTcQr, GseGZMmJM, RoEsuEcUO, neOEdLsmw, HLGEahKcx, rEqUCBbkI, naMesbtWH, kjUrUkZPg, wsqPzDyCy] [2017, 8037, 871, 7882, 6090, 4779, 299, 573, 4367, 3455]

【17.2.2】Map 生成器

【荔枝】將 Generator適配到 Map的構(gòu)造器中;

// 鍵值對類 public class Pair<K, V> {public final K key;public final V value;public Pair(K k, V v) {key = k;value = v;} }

public class MapData<K, V> extends LinkedHashMap<K, V> {// A single Pair Generator:public MapData(Generator<Pair<K, V>> gen, int quantity) { // 使用一個(gè) generator 構(gòu)建 mapfor (int i = 0; i < quantity; i++) {Pair<K, V> p = gen.next();put(p.key, p.value);}}// Two separate Generators:public MapData(Generator<K> genK, Generator<V> genV, int quantity) { // 使用兩個(gè) generator 構(gòu)建mapfor (int i = 0; i < quantity; i++) {put(genK.next(), genV.next());}}// A key Generator and a single value:public MapData(Generator<K> genK, V value, int quantity) { // 使用一個(gè) 泛型為key的generator,但值都相同的方法構(gòu)建 map。for (int i = 0; i < quantity; i++) {put(genK.next(), value);}}// An Iterable and a value Generator:public MapData(Iterable<K> genK, Generator<V> genV) { // 使用 Iterable 和 一個(gè) generator 構(gòu)建mapfor (K key : genK) {put(key, genV.next());}}// An Iterable and a single value:public MapData(Iterable<K> genK, V value) { // 使用一個(gè) 泛型為 key的Iterable 但值都相同的 方式構(gòu)建mapfor (K key : genK) {put(key, value);}}// Generic convenience methods:(泛型簡單方法,調(diào)用以上的構(gòu)造方法)public static <K, V> MapData<K, V> map(Generator<Pair<K, V>> gen,int quantity) {return new MapData<K, V>(gen, quantity);}public static <K, V> MapData<K, V> map(Generator<K> genK,Generator<V> genV, int quantity) {return new MapData<K, V>(genK, genV, quantity);}public static <K, V> MapData<K, V> map(Generator<K> genK, V value,int quantity) {return new MapData<K, V>(genK, value, quantity);}public static <K, V> MapData<K, V> map(Iterable<K> genK, Generator<V> genV) {return new MapData<K, V>(genK, genV);}public static <K, V> MapData<K, V> map(Iterable<K> genK, V value) {return new MapData<K, V>(genK, value);} }

【荔枝】調(diào)用MapData構(gòu)建 Map的荔枝

class Letters implements Generator<Pair<Integer, String>>, Iterable<Integer> {private int size = 9;private int number = 1;private char letter = 'A';public Pair<Integer, String> next() {return new Pair<Integer, String>(number++, "" + letter++);}public Iterator<Integer> iterator() {return new Iterator<Integer>() {public Integer next() {return number++;}public boolean hasNext() {return number < size;}public void remove() {throw new UnsupportedOperationException();}};} } public class MapDataTest {public static void main(String[] args) {/* 以下構(gòu)造方式看懂一個(gè)即可。非常經(jīng)典的使用 一個(gè)容器 構(gòu)造另一個(gè)容器的方法,調(diào)用過程非常經(jīng)典 */// Pair Generator:print(MapData.map(new Letters(), 11)); // Two separate generators:print(MapData.map(new CountingGenerator.Character(), new RandomGenerator.String(3), 8));// A key Generator and a single value:print(MapData.map(new CountingGenerator.Character(), "Value", 6));// An Iterable and a value Generator:print(MapData.map(new Letters(), new RandomGenerator.String(3)));// An Iterable and a single value:print(MapData.map(new Letters(), "Pop"));} }

// 打印結(jié)果: {1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J, 11=K} {a=YNz, b=brn, c=yGc, d=FOW, e=ZnT, f=cQr, g=Gse, h=GZM} {a=Value, b=Value, c=Value, d=Value, e=Value, f=Value} {1=mJM, 2=RoE, 3=suE, 4=cUO, 5=neO, 6=EdL, 7=smw, 8=HLG} {1=Pop, 2=Pop, 3=Pop, 4=Pop, 5=Pop, 6=Pop, 7=Pop, 8=Pop}

【補(bǔ)充】 可以使用工具來創(chuàng)建 任何用于 Map 或 Collection 的生成數(shù)據(jù)集,然后通過 構(gòu)造器 或 Map.putAll() 和 Collection.putAll() 來初始化 Map 和Collection;


【總結(jié)】Collection的一個(gè)項(xiàng)存儲一個(gè)值,而map 的一個(gè)項(xiàng)存儲一個(gè)鍵值對;以上是 把 generator 適配到 Collection 和 map 的構(gòu)造過程的方式,非常經(jīng)典的調(diào)用,果然厲害;


【17.2.3】使用 Abstract: 顯然, 各種容器對應(yīng) 的 Abstract類 是 繼承了 容器基類接口,實(shí)現(xiàn)基類接口的一部分方法或全部方法,然后實(shí)體類容器 再來繼承 Abstract類,這樣實(shí)體類容器無需實(shí)現(xiàn) 容器接口的全部方法。如AbstractCollection,?AbstractSet, AbstractList, AbstractMap 等容器抽象類;


【荔枝】創(chuàng)建定制的Map 和 Collection?

public class Countries {/* 二維數(shù)組 */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", "---" },// 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", "-" },{ "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(): 實(shí)現(xiàn) entrySet()方法來 應(yīng)用 AbstractMap 抽象類private static class FlyweightMap extends AbstractMap<String, String> {// 靜態(tài)內(nèi)部類private static class Entry implements Map.Entry<String, String> { // 靜態(tài)內(nèi)部類int index;Entry(int index) {this.index = index;}/* 判斷 key 是否相等 */public boolean equals(Object o) {return DATA[index][0].equals(o);}public String getKey() {return DATA[index][0];}public String getValue() {return DATA[index][1];}public String setValue(String value) {throw new UnsupportedOperationException();}/* key的 哈希值 */public int hashCode() {return DATA[index][0].hashCode();}}// Use AbstractSet by implementing size() & iterator(): 通過實(shí)現(xiàn) size() 和 iterator() 方法來應(yīng)用 AbstractSet 抽象類static class EntrySet extends AbstractSet<Map.Entry<String, String>> {// 靜態(tài)內(nèi)部類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;elsethis.size = size;}public int size() {return size;}private class Iter implements Iterator<Map.Entry<String, String>> { // 內(nèi)部類// Only one Entry object per Iterator:Collection col;private Entry entry = new Entry(-1);public boolean hasNext() {return entry.index < size - 1;}public Map.Entry<String, String> next() {entry.index++;return entry;}public void remove() {throw new UnsupportedOperationException();}}public Iterator<Map.Entry<String, String>> iterator() {return new Iter();}}private static Set<Map.Entry<String, String>> entries = new EntrySet(DATA.length);public Set<Map.Entry<String, String>> entrySet() {return entries;}}// Create a partial map of 'size' countries:static Map<String, String> select(final int size) {HashMap map = null;return new FlyweightMap() { @Overridepublic Set<Map.Entry<String, String>> entrySet() {return new EntrySet(size);}};}static Map<String, String> map = new FlyweightMap();// 返回 存儲首都的 map容器public static Map<String, String> capitals() {return map; // The entire map}// 返回給定size 的 mappublic static Map<String, String> capitals(int size) {return select(size); // A partial map}static List<String> names = new ArrayList<String>(map.keySet());// 返回所有的 names public static List<String> names() {return names;}// 返回給定 size 的 name listpublic static List<String> names(int size) {return new ArrayList<String>(select(size).keySet());}public static void main(String[] args) {print(capitals(10)); print(names(10));print(new HashMap<String, String>(capitals(3)));print(new LinkedHashMap<String, String>(capitals(3)));print(new TreeMap<String, String>(capitals(3)));print(new Hashtable<String, String>(capitals(3)));print(new HashSet<String>(names(6)));print(new LinkedHashSet<String>(names(6)));print(new TreeSet<String>(names(6)));print(new ArrayList<String>(names(6)));print(new LinkedList<String>(names(6)));print(capitals().get("BRAZIL"));} }

// 打印結(jié)果:: {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

【荔枝的關(guān)鍵之處】 通過繼承容器抽象類來創(chuàng)建定制的 Map 和 Collection 有多簡單。。為了創(chuàng)建只讀的 Map,可以繼承 AbstractMap 并實(shí)現(xiàn) entrySet() 方法。為了創(chuàng)建只讀的 Set ,可以繼承 AbstractSet 并實(shí)現(xiàn) iterator()  和 size()方法;


【定制ArrayList的荔枝】

public class CountingIntegerList extends AbstractList<Integer> {private int size;public CountingIntegerList(int size) {this.size = size < 0 ? 0 : size;}public Integer get(int index) {return Integer.valueOf(index);}public int size() {return size;}public static void main(String[] args) {System.out.println(new CountingIntegerList(30));} } /** 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]*/// :~

【代碼解說】為了基于 AbstractList 創(chuàng)建只讀的List, 必須實(shí)現(xiàn) size() 和 get() 方法;


【荔枝】創(chuàng)建定制的Map

public class CountingMapData extends AbstractMap<Integer, String> {private int size;private static String[] chars = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split(" ");public CountingMapData(int size) {if (size < 0)this.size = 0;this.size = size;}private static class Entry implements Map.Entry<Integer, String> {// 靜態(tài)內(nèi)部類int index;Entry(int index) {this.index = index;}public boolean equals(Object o) {return Integer.valueOf(index).equals(o);}public Integer getKey() {return index;}public String getValue() {return chars[index % chars.length]+ Integer.toString(index / chars.length);}public String setValue(String value) {throw new UnsupportedOperationException();}public int hashCode() {return Integer.valueOf(index).hashCode();}}public Set<Map.Entry<Integer, String>> entrySet() {// LinkedHashSet retains initialization order: // LinkedHashSet 保持了初始化順序。Set<Map.Entry<Integer, String>> entries = new LinkedHashSet<Map.Entry<Integer, String>>();for (int i = 0; i < size; i++)entries.add(new Entry(i));return entries;}public static void main(String[] args) {/*AbstractMap的toString() 方法調(diào)用了 entrySet().iterator() 迭代器*//* iteraotr迭代器 調(diào)用了 entry.getKey() 和 entry.getValue() 方法 */System.out.println(new CountingMapData(60));} }

/** 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}*/// :~

【17.3】Collection的功能方法


【荔枝】Collection方法列表展示

public class CollectionMethods {public static void main(String[] args) {Collection<String> c = new ArrayList<String>();c.addAll(Countries.names(6));c.add("ten");c.add("eleven");print("c = ", c);/* list 轉(zhuǎn) 數(shù)組 */Object[] array = c.toArray(); // Make a String array from the List:String[] str = c.toArray(new String[0]);/* Collections.max() 和 Collections.min() 方法:找出list的最大最小值 */print("Collections.max(c) = " + Collections.max(c));print("Collections.min(c) = " + Collections.min(c));/* addAll(): 把一個(gè)容器添加到另一個(gè)容器中 */Collection<String> c2 = new ArrayList<String>();c2.addAll(Countries.names(6));c.addAll(c2);print("c2.addAll(Countries.names(6)), c.addAll(c2), c = ", c);/* remove(): 移除某個(gè)元素 */c.remove(Countries.DATA[0][0]);print("c.remove(Countries.DATA[0][0]), c = ", c);c.remove(Countries.DATA[1][0]);print("c.remove(Countries.DATA[1][0]), c = ", c);/* removeAll(): 從c中移除c 和 c2 的交集元素 */print("c2 = ", c2);c.removeAll(c2);print("c.removeAll(c2), c = ", c);c.addAll(c2);print("c.addAll(c2), c = " + c);/* contains(): 集合是否包含單個(gè)元素 */String val = Countries.DATA[3][0];print("c.contains(" + val + ") = " + c.contains(val));/* containsAll(): 集合間是否存在包含關(guān)系 */print("c.containsAll(c2) = " + c.containsAll(c2));/* subList(start, end):截取子集,包括start,不包括end */Collection<String> c3 = ((List<String>) c).subList(3, 5);print("c3 = ((List<String>) c).subList(3, 5), c3 = " + c3);/* retainAll(): 求兩個(gè)集合的交集 */print("c2 = ", c2);c2.retainAll(c3);print("c2.retainAll(c3), c2 = ", c2);/* a.removeAll(b): 從a中移除a與b的交集 */c2.removeAll(c3);print("c2.removeAll(c3), c2.isEmpty() = " + c2.isEmpty());c = new ArrayList<String>();c.addAll(Countries.names(6));print("c.addAll(Countries.names(6)), c = ", c);c.clear(); // Remove all elementsprint("c.clear(), c = " + c);} }

// 打印結(jié)果: c = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI, ten, eleven] Collections.max(c) = ten Collections.min(c) = ALGERIA c2.addAll(Countries.names(6)), c.addAll(c2), c = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c.remove(Countries.DATA[0][0]), c = [ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c.remove(Countries.DATA[1][0]), c = [BENIN, BOTSWANA, BURKINA FASO, BURUNDI, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c2 = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c.removeAll(c2), c = [ten, eleven] c.addAll(c2), c = [ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c.contains(BOTSWANA) = true c.containsAll(c2) = true c3 = ((List<String>) c).subList(3, 5), c3 = [ANGOLA, BENIN] c2 = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c2.retainAll(c3), c2 = [ANGOLA, BENIN] c2.removeAll(c3), c2.isEmpty() = true c.addAll(Countries.names(6)), c = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] c.clear(), c = []


【17.4】可選操作

1)可選操作是什么: 執(zhí)行 各種不同的添加和移除的方法在 Collection接口中都是可選操作;

2)未獲支持的操作這種方式可以實(shí)現(xiàn)java容器類庫的一個(gè)重要目標(biāo): 容器應(yīng)該易學(xué)易用;

3)UnsupportedOperationException 未獲支持的異常

3.1)UnsupportedOperationException 必須是一種罕見事件。。大多數(shù)情況下, 所有操作都應(yīng)該是可以工作的,只有在特例中才會有 未獲支持的操作;

3.2)如果一個(gè)操作是未獲支持的,那么在實(shí)現(xiàn)接口時(shí)可能會導(dǎo)致?UnsupportedOperationException異常,而不是把 程序交給客戶之后才出現(xiàn)此異常,這種情況是有道理的;

3.3)注意: 未獲支持的操作只有在運(yùn)行時(shí)才能探測到,因此它們表示動態(tài)類型檢查;


【17.4.1】未獲支持的操作

【荔枝】當(dāng)執(zhí)行未獲支持操作時(shí),拋出UnsupportedOperationException異常

public class Unsupported {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<String>(subList);try {c.retainAll(c2);} catch (Exception e) {System.out.println("retainAll(): " + e);}try {c.removeAll(c2);} catch (Exception e) {System.out.println("removeAll(): " + e);}try {c.clear();} catch (Exception e) {System.out.println("clear(): " + e);}try {c.add("X");} catch (Exception e) {System.out.println("add(): " + e);}try {c.addAll(c2);} catch (Exception e) {System.out.println("addAll(): " + e);}try {c.remove("C");} catch (Exception e) {System.out.println("remove(): " + e);}// The List.set() method modifies the value but// doesn't change the size of the data structure:try {/* 對于Arrays.asList()生成的list, 修改其某個(gè)index上的值是可以的 */list.set(0, "X"); } catch (Exception e) {System.out.println("List.set(): " + e);}}public static void main(String[] args) {List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));// 可修改的拷貝,因?yàn)?new ArrayList 是利用list的基本數(shù)組數(shù)據(jù) 進(jìn)行深度拷貝。test("Modifiable Copy", new ArrayList<String>(list)); System.out.println();// 這個(gè)list(Arrays$ArrayList) 的基本數(shù)組 的 數(shù)據(jù) 是無法 改變的。test("Arrays.asList()", list); System.out.println();// 這是一種 不可修改的 拷貝,同 new ArrayList()test("unmodifiableList()", Collections.unmodifiableList(new ArrayList<String>(list))); } }

// 打印結(jié)果: --- 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

【代碼解說】

1)因?yàn)锳rrays.asList() 會生成一個(gè) List,它基于一個(gè)固定大小的數(shù)組,僅支持哪些不會改變數(shù)組大小的操作,對他而言是有道理的;

2)注意,應(yīng)該把Arrays.asList() 產(chǎn)生的list 作為構(gòu)造器參數(shù)傳給任何其他的 Collection(推薦用 Collections.addAll() 方法),這樣就可以生成允許使用所有容器方法的 list了;

3)list.set(0, "X") : 可以看到 修改 Arrays.asList() 產(chǎn)生的list 的 某個(gè)元素的時(shí)候 是不會拋出異常的,因?yàn)樗]有修改 list容器的大小;

4)Arrays.asList() 返回了固定尺寸的List, 而 Collections.unmodifiableList() 產(chǎn)生不可修改的列表:所以你會看見? 當(dāng) list = ArrayList.asList()的時(shí)候,?list.set(0, "X") 執(zhí)行通過;而當(dāng) list =?Collections.unmodifiableList() 時(shí),則會拋出?java.lang.UnsupportedOperationException 未獲支持的異常;


【17.5】List的功能方法

1)List的常用方法列表: add()方法添加對象,get() 方法取出一個(gè)元素, 調(diào)用 iterator() 方法獲取遍歷list的 迭代器 Iterator;

【荔枝】List方法列表

public class Lists {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(String collectionName, List<String> a) {print("\n// collectionName = " + collectionName + "from basicTest method.");a.add(1, "x"); // Add at location 1(即第2個(gè)位置)a.add("x"); // Add at endprint("a.add(1, \"x\"), a.add(\"x\"), a = " + a);a.addAll(Countries.names(5));a.addAll(3, Countries.names(5));print("a.addAll(Countries.names(5)), a.addAll(3, Countries.names(5)), a = " + a);b = a.contains("1"); // Is it in there?print("a.contains(\"1\") = " + b);// Is the entire collection in there?b = a.containsAll(Countries.names(5));print("a.containsAll(Countries.names(5)) = " + b);/* 以下操作成本對ArrayList很便宜,對LinkedList很昂貴 */s = a.get(1); // 取出 index = 1 處的值i = a.indexOf("1"); // Tell index of objectb = a.isEmpty(); // Any elements inside?it = a.iterator(); // Ordinary Iteratorlit = a.listIterator(); // ListIteratorlit = a.listIterator(3); // Start at loc 3i = a.lastIndexOf("1"); // Last matchSystem.out.println("a.get(1) = " + s + ", a.indexOf(\"1\") = " + i+ ", a.isEmpty() = " + b + ", a.iterator() = " + it + ", "+ "a.listIterator(3) = " + lit + ", a.lastIndexOf(\"1\") = " + a.lastIndexOf("1"));print("a = " + a);a.remove(1); // Remove location 1a.remove("3"); // Remove this objecta.remove("tr");a.set(1, "y"); // Set location 1 to "y"print("a.remove(1), a.remove(\"3\"), a.set(1, \"y\"), a = " + a);/* retainAll() 求交集 */print("a = " + a);a.retainAll(Countries.names(5)); print("Countries.names(5) = " + Countries.names(5));print("a.retainAll(Countries.names(5)), a = " + a);// Remove everything that's in the argument:a.removeAll(Countries.names(5));System.out.println("a.removeAll(Countries.names(5)), a = " + a);i = a.size(); // How big is it?a.clear(); // Remove all elements// mycode System.out.println("====== this is my mycode ======");a = new ArrayList(Arrays.asList("A B C D E F G".split(" ")));List subList = new ArrayList(Arrays.asList("A Z C I".split(" ")));print("a = " + a);print("subList = " + subList);a.retainAll(subList);print("a.retainAll(subList), a = " + a);print("sublist = " + subList);System.out.println("\n====== this is my mycode ======");a = new ArrayList(Arrays.asList("A B C D E F G".split(" ")));subList = new ArrayList(Arrays.asList("A Z C I".split(" ")));print("a = " + a);print("subList = " + subList);subList.retainAll(a);print("after subList.retainAll(a)");print("a = " + a);print("sublist = " + subList);System.out.println("====== this is my mycode ====== \nover");}/* 雙向移動的迭代器 */public static void iterMotion(String collectionName, List<String> a) {print("\n// collectionName = " + collectionName + ", from iterMotion method");print("a = " + a);ListIterator<String> it = a.listIterator(); // 雙向移動的迭代器b = it.hasNext();print("it.hasNext() = " + b);b = it.hasPrevious();print("it.hasPrevious() = " + b);s = it.next(); // 先返回值 后 counter++.print("it.next() = " + s);i = it.nextIndex(); // 返回當(dāng)值的 counter 大小print("it.nextIndex() = " + i);s = it.previous(); // --counter 先減 返回值 . print("it.previous() = " + s); i = it.previousIndex(); // --counter 先減print("it.previousIndex() = " + i);}/* 通過迭代器操作容器元素(增刪改查) */public static void iterManipulation(String collectionName, List<String> a) {print("\n// collectionName = " + collectionName + ", from iterManipulation method");ListIterator<String> it = a.listIterator();print("a = " + a);it.add("47");print("it.add(\"47\"), a = " + a);it.next(); // 先返回值,后更新游標(biāo) it.remove();// 移除游標(biāo)的上一個(gè)元素,然后更新游標(biāo); print("ListIterator.next(), ListIterator.remove(), a = " + a);// Must move to an element after remove():/* remove 操作后,必須調(diào)用next() 方法,因?yàn)?remove() 把 lastRet 賦值為 -1 */it.next();// Change the element after the deleted one:it.set("47");print("a = " + a);}public static void testVisual(String collectionName, List<String> a) {print("\n// collectionName = " + collectionName);print(a);List<String> b = Countries.names(5);print("b = " + b);ArrayList list;a.addAll(b);a.addAll(b);print("a.addAll(b) a.addAll(b); a = " + a);// Insert, remove, and replace elements// using a ListIterator:ListIterator<String> x = a.listIterator(a.size() / 2);x.add("one");print("x.add('one'); a = " + a);print(x.next());x.remove();print(x.next());x.set("47");print(a);// Traverse the list backwards:x = a.listIterator(a.size());while (x.hasPrevious())printnb(x.previous() + " ");print();print("====== testVisual finished ======\n");}// There are some things that only LinkedLists can do:public static void testLinkedList(String collectionName) {print("\n====== testLinkedList ======");LinkedList<String> ll = new LinkedList<String>();ll.addAll(Countries.names(5));print(ll);// Treat it like a stack, pushing:ll.addFirst("one");ll.addFirst("two");print(ll);// Like "peeking" at the top of a stack:print(ll.getFirst());// Like popping a stack:print(ll.removeFirst());print(ll.removeFirst());// Treat it like a queue, pulling elements// off the tail end:print(ll.removeLast());print(ll);print("====== testLinkedList over.======\n");}public static void main(String[] args) {// Make and fill a new list each time:basicTest("basicTest, LinkedList", new LinkedList<String>(Countries.names(5)));basicTest("basicTest, ArrayList", new ArrayList<String>(Countries.names(5)));iterMotion("iterMotion, LinkedList", new LinkedList<String>(Countries.names(5)));iterMotion("iterMotion, ArrayList", new ArrayList<String>(Countries.names(5)));iterManipulation("iterManipulation, LinkedList", new LinkedList<String>(Countries.names(5)));iterManipulation("iterManipulation, ArrayList", new ArrayList<String>(Countries.names(5)));testVisual("testVisual, LinkedList", new LinkedList<String>(Countries.names(5)));testLinkedList("testLinkedList");} } // 打印結(jié)果: // collectionName = basicTest, LinkedListfrom basicTest method. a.add(1, "x"), a.add("x"), a = [ALGERIA, x, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, x] a.addAll(Countries.names(5)), a.addAll(3, Countries.names(5)), a = [ALGERIA, x, ANGOLA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.contains("1") = false a.containsAll(Countries.names(5)) = true a.get(1) = x, a.indexOf("1") = -1, a.isEmpty() = false, a.iterator() = java.util.LinkedList$ListItr@15db9742, a.listIterator(3) = java.util.LinkedList$ListItr@6d06d69c, a.lastIndexOf("1") = -1 a = [ALGERIA, x, ANGOLA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.remove(1), a.remove("3"), a.set(1, "y"), a = [ALGERIA, y, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a = [ALGERIA, y, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] Countries.names(5) = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.retainAll(Countries.names(5)), a = [ALGERIA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.removeAll(Countries.names(5)), a = [] ====== this is my mycode ====== a = [A, B, C, D, E, F, G] subList = [A, Z, C, I] a.retainAll(subList), a = [A, C] sublist = [A, Z, C, I]====== this is my mycode ====== a = [A, B, C, D, E, F, G] subList = [A, Z, C, I] after subList.retainAll(a) a = [A, B, C, D, E, F, G] sublist = [A, C] ====== this is my mycode ====== over// collectionName = basicTest, ArrayListfrom basicTest method. a.add(1, "x"), a.add("x"), a = [ALGERIA, x, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, x] a.addAll(Countries.names(5)), a.addAll(3, Countries.names(5)), a = [ALGERIA, x, ANGOLA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.contains("1") = false a.containsAll(Countries.names(5)) = true a.get(1) = x, a.indexOf("1") = -1, a.isEmpty() = false, a.iterator() = java.util.ArrayList$Itr@7852e922, a.listIterator(3) = java.util.ArrayList$ListItr@4e25154f, a.lastIndexOf("1") = -1 a = [ALGERIA, x, ANGOLA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.remove(1), a.remove("3"), a.set(1, "y"), a = [ALGERIA, y, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a = [ALGERIA, y, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, x, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] Countries.names(5) = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.retainAll(Countries.names(5)), a = [ALGERIA, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.removeAll(Countries.names(5)), a = [] ====== this is my mycode ====== a = [A, B, C, D, E, F, G] subList = [A, Z, C, I] a.retainAll(subList), a = [A, C] sublist = [A, Z, C, I]====== this is my mycode ====== a = [A, B, C, D, E, F, G] subList = [A, Z, C, I] after subList.retainAll(a) a = [A, B, C, D, E, F, G] sublist = [A, C] ====== this is my mycode ====== over// collectionName = iterMotion, LinkedList, from iterMotion method a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] it.hasNext() = true it.hasPrevious() = false it.next() = ALGERIA it.nextIndex() = 1 it.previous() = ALGERIA it.previousIndex() = -1// collectionName = iterMotion, ArrayList, from iterMotion method a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] it.hasNext() = true it.hasPrevious() = false it.next() = ALGERIA it.nextIndex() = 1 it.previous() = ALGERIA it.previousIndex() = -1// collectionName = iterManipulation, LinkedList, from iterManipulation method a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] it.add("47"), a = [47, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] ListIterator.next(), ListIterator.remove(), a = [47, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a = [47, 47, BENIN, BOTSWANA, BURKINA FASO]// collectionName = iterManipulation, ArrayList, from iterManipulation method a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] it.add("47"), a = [47, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] ListIterator.next(), ListIterator.remove(), a = [47, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a = [47, 47, BENIN, BOTSWANA, BURKINA FASO]// collectionName = testVisual, LinkedList [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] b = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] a.addAll(b) a.addAll(b); a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] x.add('one'); a = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, one, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] BENIN BOTSWANA [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, ALGERIA, ANGOLA, one, 47, BURKINA FASO, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] BURKINA FASO BOTSWANA BENIN ANGOLA ALGERIA BURKINA FASO 47 one ANGOLA ALGERIA BURKINA FASO BOTSWANA BENIN ANGOLA ALGERIA ====== testVisual finished ============ testLinkedList ====== [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] [two, one, ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO] two two one BURKINA FASO [ALGERIA, ANGOLA, BENIN, BOTSWANA] ====== testLinkedList over.======

【17.6】Set和存儲順序

1)Set需要一種方式來維護(hù)存儲順序。而存儲順序的維護(hù)依賴于Set的不同實(shí)現(xiàn)(HashSet, TreeSet 和 LinkedHashSet);

2)Set 基類 和 子類:

2.1)Set接口: 存入 Set 的每個(gè)元素是唯一的,Set 不保持重復(fù)元素。 加入Set 的元素必須重寫 equals 方法 以確保對象唯一性;

2.2)HashSet* (默認(rèn)選擇,優(yōu)先推薦使用HashSet)為快速查找而設(shè)計(jì)的 Set。 存入HashSet 的元素重寫 hashCode 方法;

2.3)TreeSet:保持順序的 Set, 底層基于 紅黑樹實(shí)現(xiàn)。存入 TreeSet 的元素必須實(shí)現(xiàn) Comparable接口

2.4)LinkedHashSet: 具有HashSet的 快速查詢優(yōu)點(diǎn), 內(nèi)部使用鏈表維護(hù)元素順序(插入的次序)。存入LinkedHashSet的元素重寫 hashCode 方法


3)重寫 equals? 和 hashCode 方法:?

3.1)重寫equals方法:必須為 存入 HashSet 和? TreeSet? 的元素 重寫其 equals 方法;

3.2)重寫 hashCode方法: 當(dāng)元素被存儲到 HashSet 或 LinkedHashSet 中時(shí),必須重寫元素的 hashCode 方法;

(建議: 良好的編碼風(fēng)格是 同時(shí)重寫 equals 方法 和 hashCode 方法)


【荔枝-HashSet, TreeSet, LinkedHashSet存儲規(guī)則】

// 存入Set的元素, 必須重寫 equals 方法 class SetType {int i;public SetType(int n) {i = n;}/* 重寫 equals 方法 */@Overridepublic boolean equals(Object o) {return o instanceof SetType && (i == ((SetType) o).i);}public String toString() {return Integer.toString(i);} } /* 存入 HashSet的元素 */ class HashType extends SetType {public HashType(int n) {super(n);}/* 重寫 hashCode 方法 */public int hashCode() {return i;} } /* 存入 TreeSet的元素, 實(shí)現(xiàn) Comparable 接口 */ class TreeType extends SetType implements Comparable<TreeType> {public TreeType(int n) {super(n);}@Overridepublic int compareTo(TreeType arg) {return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));} }public class TypesForSets {static <T> Set<T> fill(Set<T> set, Class<T> type) {try {for (int i = 0; i < 10; i++)set.add(type.getConstructor(int.class).newInstance(i));} catch (Exception e) {throw new RuntimeException(e);}return set;}static <T> void test(Set<T> set, Class<T> type) {fill(set, type);fill(set, type); // Try to add duplicates, 不重復(fù)的。fill(set, type);System.out.println(set);}public static void main(String[] args) {test(new HashSet<HashType>(), HashType.class); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 無序test(new LinkedHashSet<HashType>(), HashType.class); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 輸出順序和插入順序同test(new TreeSet<TreeType>(), TreeType.class); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 按照排序規(guī)則,有序System.out.println("\n======\n");// Things that don't work: Set 存儲不重復(fù)元素不起作用了(凡是使用到 hash散列機(jī)制的容器,必須重寫該容器中存儲元素的 hashCode 方法,否則容器的存儲規(guī)則不奏效)test(new HashSet<SetType>(), SetType.class); // HashSet 添加 SetType元素,而SetType 沒有重寫 hashCode() 方法test(new HashSet<TreeType>(), TreeType.class); // HashSet 添加 TreeType元素,而TreeType 沒有重寫 hashCode() 方法test(new LinkedHashSet<SetType>(), SetType.class); // LinkedHashSet 添加 SetType元素,而SetType 沒有重寫 hashCode() 方法test(new LinkedHashSet<TreeType>(), TreeType.class); // LinkedHashSet 添加 TreeType元素,而TreeType 沒有重寫 hashCode() 方法System.out.println("\n======\n");try {test(new TreeSet<SetType>(), SetType.class); // SetType 沒有實(shí)現(xiàn) Comparable接口,不是Comparable子類,所以插入TreeSet失敗并拋出異常;} catch (Exception e) {System.out.println(e.getMessage());}try {test(new TreeSet<HashType>(), HashType.class); // HashType 沒有實(shí)現(xiàn) Comparable接口,不是Comparable子類,所以插入TreeSet失敗并拋出異常;} catch (Exception e) {System.out.println(e.getMessage());}} } /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 無序 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 輸出順序和插入順序同 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] // 按照排序規(guī)則,有序======[7, 3, 1, 9, 6, 5, 9, 1, 3, 8, 9, 0, 5, 2, 6, 1, 5, 4, 2, 0, 7, 4, 3, 8, 8, 7, 6, 4, 0, 2] [8, 0, 3, 9, 0, 2, 8, 5, 2, 3, 4, 1, 2, 9, 4, 1, 9, 6, 7, 7, 4, 1, 3, 8, 6, 5, 7, 5, 6, 0] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]======java.lang.ClassCastException: chapter17.SetType cannot be cast to java.lang.Comparable java.lang.ClassCastException: chapter17.HashType cannot be cast to java.lang.Comparable */ 【代碼解說】?

解說1)compareTo() 方法和equals() 方法的自然排序規(guī)則一致: equals 返回true,compareTo 方法返回 0;equals 返回 false, compareTo 方法返回 非0;

解說2)對于沒有重寫 hashCode() 方法的 SetType 或 TreeType, 如果將他們放置到任何散列實(shí)現(xiàn)中都會產(chǎn)生重復(fù)值;因?yàn)?HashSet 或 LinkedHashSet 中 重復(fù)的意義在于 元素的 哈希值或 hashCode 相同;


【干貨——HashSet 和 LinkedHashSet 底層采用 HashMap.key 進(jìn)行存儲元素,因?yàn)?LinkedHashSet 繼承 HashSet】

// HashSet 的構(gòu)造器 和 Iterator() 方法源碼public HashSet() {map = new HashMap<>();} public HashSet(Collection<? extends E> c) {map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));addAll(c);} public HashSet(int initialCapacity, float loadFactor) {map = new HashMap<>(initialCapacity, loadFactor);} public HashSet(int initialCapacity) {map = new HashMap<>(initialCapacity);} HashSet(int initialCapacity, float loadFactor, boolean dummy) {map = new LinkedHashMap<>(initialCapacity, loadFactor);}public Iterator<E> iterator() {return map.keySet().iterator();} 【干貨——TreeSet 底層采用 TreeMap.key 進(jìn)行存儲元素】

// TreeMap 的構(gòu)造器 和 iterator() 方法源碼 TreeSet(NavigableMap<E,Object> m) {this.m = m;} public TreeSet() {this(new TreeMap<E,Object>());} public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}public TreeSet(Collection<? extends E> c) {this();addAll(c);}public TreeSet(SortedSet<E> s) {this(s.comparator());addAll(s);} public Iterator<E> iterator() {return m.navigableKeySet().iterator();}
【荔枝-Object.equals() 和 hashCode() 方法源碼】

// Object.equals() and hashCode() 源碼public boolean equals(Object obj) {return (this == obj);} public native int hashCode();

4)HashSet, LinkedHashSet, TreeSet 存儲機(jī)制

HashSet, 以某種散列機(jī)制 保持所有元素;

LinkedHashSet, 按照元素插入的順序保存元素;?

TreeSet, 按照排序順序維護(hù)元素的次序, 排序規(guī)則 通過 實(shí)現(xiàn) Comparable接口并重寫 compareTo() 方法來實(shí)現(xiàn);


【17.6.1】SortedSet

1)介紹: SortedSet中的元素保證處于排序狀態(tài); 其 comparator() 方法返回當(dāng)前 Set 使用 的 Comparator;或者返回null 表示 以自然方式排序;

first() 返回第一個(gè)元素;

last() 返回最后一個(gè)元素;

subSet(from, to ) 生成set的子集, 包含from 不包含 to;

headSet(to)? 生成該set 的子集, 且子集元素的值 小于 to 元素的值

tailSet(from)??生成該set 的子集, 且子集元素的值 大于 或等于 from 元素的值


【荔枝-SortedSet方法演示】

public class SortedSetDemo {public static void main(String[] args) {SortedSet<String> sortedSet = new TreeSet<String>();Collections.addAll(sortedSet,"one two three four five six seven eight".split(" "));print("sortedSet = " + sortedSet); // sortedSet = [eight, five, four, one, seven, six, three, two]String low = sortedSet.first(); // 第一個(gè)元素String high = sortedSet.last(); // 最后一個(gè)元素print("low = " + low);print("high = " + 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();elseit.next();}print(low); // oneprint(high); // twoprint(sortedSet.subSet(low, high)); // 子集(包括low,不包括high):[one, seven, six, three]print(sortedSet.headSet(high)); // 元素值小于high的子集: [eight, five, four, one, seven, six, three]print(sortedSet.tailSet(low)); // 元素值大于等于low的子集:[one, seven, six, three, two]print("sortedSet.comparator() = " + sortedSet.comparator()); // sortedSet.comparator() = null(自然排序)} } 【代碼解說】SortedSet的排序: 按照對象的比較函數(shù)值? 對 元素排序;


【17.7】隊(duì)列

1)Queue的實(shí)現(xiàn): java SE5 中的實(shí)現(xiàn)是 LinkedList 和 PriorityQueue, 它們的差異在于 排序行為而不是性能;

/* Queue容器進(jìn)隊(duì)和出隊(duì)的荔枝 */ public class QueueBehavior {private static int count = 10;static <T> void test(Queue<T> queue, Generator<T> gen) {for (int i = 0; i < count; i++)queue.offer(gen.next()); // 插入尾部:offer() == add() 方法while (queue.peek() != null) // 不刪除頭部并返回頭部: peek() 當(dāng)queue為空返回空System.out.print(queue.remove() + " "); // 刪除頭部;System.out.println();}static class Gen implements Generator<String> {String[] s = ("one two three four five six seven " + "eight nine ten").split(" ");int i;public String next() {return s[i++];}}public static void main(String[] args) {test(new LinkedList<String>(), new Gen()); // 出隊(duì)順序與進(jìn)隊(duì)順序一致test(new PriorityQueue<String>(), new Gen()); // 不一致test(new ArrayBlockingQueue<String>(count), new Gen()); // 出隊(duì)順序與進(jìn)隊(duì)順序一致test(new ConcurrentLinkedQueue<String>(), new Gen()); // 出隊(duì)順序與進(jìn)隊(duì)順序一致test(new LinkedBlockingQueue<String>(), new Gen()); // 出隊(duì)順序與進(jìn)隊(duì)順序一致test(new PriorityBlockingQueue<String>(), new Gen()); // 不一致} }

【代碼解說】除了優(yōu)先級隊(duì)列?PriorityQueue 和 PriorityBlockingQueue, 其他隊(duì)列 將精確地按照進(jìn)隊(duì)順序進(jìn)行出隊(duì)操作;


【17.7.1】優(yōu)先級隊(duì)列

/* 優(yōu)先級隊(duì)列的荔枝 */ public class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {static class ToDoItem implements Comparable<ToDoItem> { // 靜態(tài)內(nèi)部類private char primary;private int secondary;private String item;public ToDoItem(char pri, int sec, String item) {primary = pri;secondary = sec;this.item = item;}/* 優(yōu)先級計(jì)算規(guī)則 */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;}public String toString() {return Character.toString(primary) + secondary + ": " + item + "; ";}}public void add(String item, char pri, int sec) {super.add(new ToDoItem(pri, sec, item));}public static void main(String[] args) {ToDoList toDoList = new ToDoList();toDoList.add("A4", 'C', 4);toDoList.add("A2", 'A', 2);toDoList.add("B7", 'B', 7);toDoList.add("C3", 'C', 3);toDoList.add("A1", 'A', 1);toDoList.add("B1", 'B', 1);while (!toDoList.isEmpty())System.out.print(toDoList.remove() + " ");} } /* A1: A1; A2: A2; B1: B1; B7: B7; C3: C3; C4: A4; */


【17.7.2】雙向隊(duì)列

1)介紹: 可以在雙向隊(duì)列(雙端隊(duì)列) 的任何一段添加 或 移除元素。LinkedList提供了支持 雙向隊(duì)列的方法,但在java 標(biāo)準(zhǔn)類庫中 沒有任何顯式 的用于雙向隊(duì)列的接口;

2)如何實(shí)現(xiàn)雙向隊(duì)列: 使用組合來創(chuàng)建一個(gè) Deque類, 并直接中 LinkedList中暴露方法,如下:

// 這是 作者 自定義的 雙向隊(duì)列 Deque, 該雙向隊(duì)列的底層基于 LinkedList public class Deque<T> {private LinkedList<T> deque = new LinkedList<T>();public void addFirst(T e) {deque.addFirst(e);}public void addLast(T e) {deque.addLast(e);}public T getFirst() {return deque.getFirst();}public T getLast() {return deque.getLast();}public T removeFirst() {return deque.removeFirst();}public T removeLast() {return deque.removeLast();}public int size() {return deque.size();}public String toString() {return deque.toString();}// And other methods as necessary... 這里添加其他必須的方法 } // /:~

【代碼解說】首先, 這個(gè)雙向隊(duì)列是 thinking-in-java 作者 自定義的,并不是java 類庫中的; 且該自定義的 雙向隊(duì)列是基于 LinkedList來實(shí)現(xiàn)的;

第二: Deque 雙向隊(duì)列沒有 Queue 那樣常用;


【17.8】理解Map

1)Map的幾種基本實(shí)現(xiàn): HashMap, TreeMap, LinkedHashMap, WeakHashMap, ConcurrentHashMap, IdentityHashMap 等;

2)Map的簡單荔枝:

【荔枝-通過 2維數(shù)組 來實(shí)現(xiàn) HashMap的鍵值對存儲原理

// 通過 2維數(shù)組 來實(shí)現(xiàn) HashMap的鍵值對存儲原理 public class AssociativeArray<K, V> {Set set = new HashSet();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}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<String, String>(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。當(dāng)容器存儲元素存滿的時(shí)候,繼續(xù)插入元素,拋出異常。} catch (ArrayIndexOutOfBoundsException e) {print("Too many objects!");}print("map = " + map);print("map.get(\"ocean\") = " + map.get("ocean"));} } /* Too many objects! map = sky : blue grass : green ocean : dancing tree : tall earth : brown sun : warm map.get("ocean") = dancing */


【17.8.1】性能

1)性能是映射表中的一個(gè)重要問題,當(dāng)在 get() 中使用線性搜索時(shí),執(zhí)行速度回相當(dāng)?shù)芈?#xff0c;這正是 HashMap 提高速度的地方;

2)散列碼: HashMap 使用了特殊的值,稱作 散列碼,來取代對鍵的緩慢搜索; HashMap 使用對象的hashCode()方法進(jìn)行快速查詢的;

3)下面是Map的基本實(shí)現(xiàn)類,其中HashMap 是默認(rèn)選擇:

HashMap:Map基于散列表實(shí)現(xiàn),取代了 Hashtable。通過構(gòu)造器設(shè)置容量和負(fù)載因子,以調(diào)整容器性能;

LinkedHashMap:使用鏈表維護(hù)內(nèi)部次序,其中輸出順序 是 其 插入順序 或者是最近最少使用(LRU, least recently used)次序

TreeMap:基于紅黑樹實(shí)現(xiàn), 被Comparable 或 Comparator 排序。TreeMap 是唯一有 subMap() 方法的Map, 它可以返回一個(gè)子樹;

WeakHashMap: 弱鍵映射,允許釋放映射所指向的對象;

ConcurrentHashMap:線程安全的Map, 不涉及同步加鎖;

IdentityHashMap:使用 == 代替 equals() 方法 對 鍵 進(jìn)行比較的散列映射;

4)散列:散列是映射中存儲元素時(shí)最常用的方式;

5)任何Map容器所存儲的元素必須滿足以下要求:任何鍵都必須重寫? equals() 方法; 如果存儲在HashMap的元素, 該元素還必須重寫 hashCode()方法;如果使用 TreeMap 來存儲元素,則元素必須實(shí)現(xiàn) Comparable;

【荔枝-Map實(shí)現(xiàn)類】

/* Map實(shí)現(xiàn)類的荔枝: HashMap, TreeMap, LinkedHashMap, ConcurrentHashMap, WeakHashMap */ public class Maps {Properties pro;public static void printKeys(Map<Integer, String> map) {printnb("map.size() = " + map.size() + ", ");printnb("map.keySet() = ");print(map.keySet()); // Produce a Set of the keys}public static void test(Map<Integer, String> map, String mapType) {System.out.println("\n ======" + mapType + "======");print("map.getClass().getSimpleName() = " + map.getClass().getSimpleName());map.putAll(new CountingMapData(5));// Map has 'Set' behavior for keys:map.putAll(new CountingMapData(5));printKeys(map);// Producing a Collection of the values:printnb("map.values() = ");print(map.values());print("map = "+ map);print("map.containsKey(3) = " + map.containsKey(3));print("map.get(3) = " + map.get(3));print("map.containsValue(\"C0\") = " + map.containsValue("C0"));Integer key = map.keySet().iterator().next();print("First key in map = " + key);map.remove(key);printKeys(map);map.clear();print("map.clear(), map.isEmpty() = " + map.isEmpty());map.putAll(new CountingMapData(10));// Operations on the Set change the Map:map.keySet().removeAll(map.keySet());print("map.isEmpty(): " + map.isEmpty());}public static void main(String[] args) {test(new HashMap<Integer, String>(), "HashMap");test(new TreeMap<Integer, String>(), "TreeMap");test(new LinkedHashMap<Integer, String>(), "LinkedHashMap");test(new IdentityHashMap<Integer, String>(), "IdentityHashMap");test(new ConcurrentHashMap<Integer, String>(), "ConcurrentHashMap");test(new WeakHashMap<Integer, String>(), "WeakHashMap");} } /*======HashMap====== map.getClass().getSimpleName() = HashMap map.size() = 5, map.keySet() = [0, 1, 2, 3, 4] map.values(): [A0, B0, C0, D0, E0] map = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): true First key in map: 0 map.size() = 4, map.keySet() = [1, 2, 3, 4] map.isEmpty(): true map.isEmpty(): true======TreeMap====== map.getClass().getSimpleName() = TreeMap map.size() = 5, map.keySet() = [0, 1, 2, 3, 4] map.values(): [A0, B0, C0, D0, E0] map = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): true First key in map: 0 map.size() = 4, map.keySet() = [1, 2, 3, 4] map.isEmpty(): true map.isEmpty(): true======LinkedHashMap====== map.getClass().getSimpleName() = LinkedHashMap map.size() = 5, map.keySet() = [0, 1, 2, 3, 4] map.values(): [A0, B0, C0, D0, E0] map = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): true First key in map: 0 map.size() = 4, map.keySet() = [1, 2, 3, 4] map.isEmpty(): true map.isEmpty(): true======IdentityHashMap====== map.getClass().getSimpleName() = IdentityHashMap map.size() = 5, map.keySet() = [0, 2, 4, 3, 1] // 無序 map.values(): [A0, C0, E0, D0, B0] map = {0=A0, 2=C0, 4=E0, 3=D0, 1=B0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): false First key in map: 0 map.size() = 4, map.keySet() = [2, 4, 3, 1] map.isEmpty(): true map.isEmpty(): true======ConcurrentHashMap====== map.getClass().getSimpleName() = ConcurrentHashMap map.size() = 5, map.keySet() = [0, 1, 2, 3, 4] map.values(): [A0, B0, C0, D0, E0] map = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): true First key in map: 0 map.size() = 4, map.keySet() = [1, 2, 3, 4] map.isEmpty(): true map.isEmpty(): true======WeakHashMap====== map.getClass().getSimpleName() = WeakHashMap map.size() = 5, map.keySet() = [4, 3, 2, 1, 0] map.values(): [E0, D0, C0, B0, A0] map = {4=E0, 3=D0, 2=C0, 1=B0, 0=A0} map.containsKey(3): true map.get(3): D0 map.containsValue("C0"): true First key in map: 4 map.size() = 4, map.keySet() = [3, 2, 1, 0] map.isEmpty(): true map.isEmpty(): true */ // Maps.java 調(diào)用了 CountingMapData 類 public class CountingMapData extends AbstractMap<Integer, String> {private int size;private static String[] chars = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split(" ");public CountingMapData(int size) {if (size < 0)this.size = 0;this.size = size;}private static class Entry implements Map.Entry<Integer, String> {// 靜態(tài)內(nèi)部類int index;Entry(int index) {this.index = index;}public boolean equals(Object o) {return Integer.valueOf(index).equals(o);}public Integer getKey() {return index;}public String getValue() {return chars[index % chars.length]+ Integer.toString(index / chars.length);}public String setValue(String value) {throw new UnsupportedOperationException();}public int hashCode() {return Integer.valueOf(index).hashCode();}}public Set<Map.Entry<Integer, String>> entrySet() {// LinkedHashSet retains initialization order: // LinkedHashSet 保持了初始化順序。Set<Map.Entry<Integer, String>> entries = new LinkedHashSet<Map.Entry<Integer, String>>();for (int i = 0; i < size; i++)entries.add(new Entry(i));return entries;}public static void main(String[] args) {/*AbstractMap的toString() 方法調(diào)用了 entrySet().iterator() 迭代器*//* iteraotr迭代器 調(diào)用了 entry.getKey() 和 entry.getValue() 方法 */System.out.println(new CountingMapData(60));} } /** 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}*/// :~ // CountingMapData extends AbstractMap, 調(diào)用的也是 AbstractMap的toString() //AbstractMap.toString() 源碼public String toString() {Iterator<Entry<K,V>> i = entrySet().iterator();if (! i.hasNext())return "{}";StringBuilder sb = new StringBuilder();sb.append('{');for (;;) {Entry<K,V> e = i.next();K key = e.getKey();V value = e.getValue();sb.append(key == this ? "(this Map)" : key);sb.append('=');sb.append(value == this ? "(this Map)" : value);if (! i.hasNext())return sb.append('}').toString();sb.append(',').append(' ');}}【代碼解說】Map容器中,鍵必須是唯一的,值可以重復(fù);


【17.8.2】SortedMap(TreeMap 是 SortedMap 現(xiàn)階段的唯一實(shí)現(xiàn))

1)使用SortedMap : 確保鍵處于排序狀態(tài);

2)SortMap的方法列表如下:

// 荔枝-SortedMap 方法列表 public class SortedMapDemo {public static void main(String[] args) {TreeMap<Integer, String> sortedMap = new TreeMap<Integer, String>(new CountingMapData(10));print("sortedMap = " +sortedMap); // sortedMap = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0}Integer low = sortedMap.firstKey(); // 第一個(gè)鍵Integer high = sortedMap.lastKey(); // 最后一個(gè)鍵print("sortedMap.firstKey() = " + low);print("sortedMap.lastKey() = " + 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();elseit.next();}print("low = " + low); // low = 3print("high = " + high); // high = 7print("sortedMap.subMap(low, high) = " + sortedMap.subMap(low, high));print("sortedMap.headMap(high) = " + sortedMap.headMap(high));print("sortedMap.tailMap(low) = " + sortedMap.tailMap(low));} } /* sortedMap = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0} sortedMap.firstKey() = 0 sortedMap.lastKey() = 9 low = 3 high = 7 sortedMap.subMap(low, high) = {3=D0, 4=E0, 5=F0, 6=G0}(map子集,包括low=3,不包括high=7) sortedMap.headMap(high) = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0}(map子集,鍵值小于high的鍵值對子集) sortedMap.tailMap(low) = {3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0}(map子集,鍵值大于等于low的鍵值對子集) */

sortedMap.subMap(low, high): map子集,包括low=3,不包括high=7;
sortedMap.headMap(high):map子集,鍵值小于high的鍵值對子集;
sortedMap.tailMap(low) : map子集,鍵值大于等于low的鍵值對子集;


【17.8.3】LinkedHashMap

1)為了提高速度, LinkedHashMap' 散列化所有元素: 遍歷鍵值對的順序 與 元素的插入順序相同 或 最近最少使用LRU順序;

2)LinkedHashMap 演示荔枝

// 荔枝-LinkedHashMap 演示 public class LinkedHashMapDemo {public static void main(String[] args) {LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<Integer, String>(new CountingMapData(9));print("linkedHashMap1 = " + linkedHashMap);// Least-recently-used order: 最近最少使用 LRU 順序linkedHashMap = new LinkedHashMap<Integer, String>(16, 0.75f, true);linkedHashMap.putAll(new CountingMapData(9));print("linkedHashMap2 = " + linkedHashMap);for (int i = 0; i < 6; i++)// Cause accesses:linkedHashMap.get(i);// 最近最少使用順序:0,1,2,3,4,5 分別先后被使用過而6,7,8沒有被使用,所以 LRU = 6, 7, 8, 0, 1, 2, 3, 4, 5,print("linkedHashMap3 = " + linkedHashMap); linkedHashMap.get(0);print("linkedHashMap4 = " + linkedHashMap); // 鍵為0的元素被使用過,所以鍵=0的entry 排到了最后;} } /* linkedHashMap1 = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0} linkedHashMap2 = {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0} linkedHashMap3 = {6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0} linkedHashMap4 = {6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0} */


【17.9】散列與散列碼(Hash 和 hashCode)

public class Groundhog {protected int number;public Groundhog(int n) {number = n;}public String toString() {return "Groundhog #" + number;} } // /:~ public class Prediction {private static Random rand = new Random(47);private boolean shadow = rand.nextDouble() > 0.5;public String toString() {if (shadow)return "Six more weeks of Winter!";elsereturn "Early Spring!";} } // /:~

// HashMap荔枝-以Groundhog作為key,Prediction作為value public class SpringDetector {// Uses a Groundhog or class derived from Groundhog:// 使用 Groundhog 或 其子類 的class 作為 typepublic static <T extends Groundhog> void detectSpring(Class<T> type)throws Exception {// 獲取構(gòu)造器(采用反射機(jī)制來創(chuàng)建 HashMap.key )Constructor<T> ghog = type.getConstructor(int.class);// 以Groundhog作為key,Prediction作為valueMap<Groundhog, Prediction> map = new HashMap<Groundhog, Prediction>();for (int i = 0; i < 10; i++)map.put(ghog.newInstance(i), new Prediction());print("map = " + map); // 打印map,調(diào)用map中每個(gè)entry的key 和 value的toString()方法Groundhog gh = ghog.newInstance(3);print("ghog.newInstance(3) = " + gh);if (map.containsKey(gh))print("map.get(gh) = " + map.get(gh));elseprint("Key not found: " + gh);}public static void main(String[] args) throws Exception {detectSpring(Groundhog.class);} } /* map = {Groundhog #1=Six more weeks of Winter!, Groundhog #4=Six more weeks of Winter!, Groundhog #5=Early Spring!, Groundhog #3=Early Spring!, Groundhog #8=Six more weeks of Winter!, Groundhog #7=Early Spring!, Groundhog #0=Six more weeks of Winter!, Groundhog #2=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #6=Early Spring!} ghog.newInstance(3) = Groundhog #3 Key not found: Groundhog #3 */ // AbstractMap.toString() 方法源碼 public String toString() {Iterator<Entry<K,V>> i = entrySet().iterator();if (! i.hasNext())return "{}";StringBuilder sb = new StringBuilder();sb.append('{');for (;;) {Entry<K,V> e = i.next();K key = e.getKey();V value = e.getValue();sb.append(key == this ? "(this Map)" : key);sb.append('=');sb.append(value == this ? "(this Map)" : value);if (! i.hasNext())return sb.append('}').toString();sb.append(',').append(' ');}}
【代碼解說】?SpringDetector.java 中, HashMap 明明插入了 number=3的?Groundhog 實(shí)例, 為什么以 number=3的Groundhog 實(shí)例 為鍵 無法查詢到 其value ?因?yàn)?Groundhog 自動繼承自基類 Object, 所以這里使用了 Object.hashCode() 方法生成散列碼,而hashCode() 返回結(jié)果默認(rèn) 使用對象的地址計(jì)算散列碼


1)HashMap 使用 equals() 方法: 判斷當(dāng)前的鍵是否與表中存在的鍵相同;

2)正確的 equals() 方法必須滿足下面5個(gè)條件:

自反性: x.equals(x) 一定返回 true;

對稱性:x.equals(y) == y.equals(x)? == true or false;

傳遞性:x.equals(y) ==true 和 y.equals(z) == true , 則推出? ?x.equals.(z) == true;

一致性:如果對象中用于等價(jià)比較的信息沒有改變,則 無論調(diào)用? x.equals(y) 多少次,返回的結(jié)果應(yīng)該保持一致,要么一直是true 或者一直 是 false;

對任何不是 null 的x, x.equals(null) 一定返回 false;


3)Object.hashCode 和 Object.equals() 方法的默認(rèn)實(shí)現(xiàn):

equals()方法默認(rèn)? :比較對象的地址;

hashCode()方法默認(rèn):?使用對象的地址計(jì)算散列碼

// Object.equals() 和 Object.hashCode() 源碼public boolean equals(Object obj) {return (this == obj);} public native int hashCode(); 【干貨】如果要使用自己的類作為 HashMap的鍵,必須重載或重寫 hashCode() 和 equals() 方法;

4)hashCode() 和 equals() 方法的區(qū)別在于:?hashCode方法用于計(jì)算鍵的散列碼并給元素分配對應(yīng)散列碼的存儲位置; equals 是比較兩個(gè)鍵值對間的 鍵 是否相同;


【荔枝-?HashMap荔枝-以Groundhog2作為key,Prediction作為value,并重寫了Groundhog2的 hashCode() 和 equals() 方法】

public class Groundhog2 extends Groundhog {public Groundhog2(int n) {super(n);}// 重寫 hashCode() 方法@Overridepublic int hashCode() {return number;}// 重寫 equals() 方法@Overridepublic boolean equals(Object o) {return o instanceof Groundhog2 && (number == ((Groundhog2) o).number);} } // 荔枝- HashMap荔枝-以Groundhog2作為key,Prediction作為value,并重寫了Groundhog2的 hashCode() 和 equals() 方法 // 現(xiàn)在可以找到鍵==3的value了 public class SpringDetector2 {public static void main(String[] args) throws Exception {SpringDetector.detectSpring(Groundhog2.class);} } /* map = {Groundhog #0=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!, Groundhog #3=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #5=Early Spring!, Groundhog #6=Early Spring!, Groundhog #7=Early Spring!, Groundhog #8=Six more weeks of Winter!, Groundhog #9=Six more weeks of Winter!} ghog.newInstance(3) = Groundhog #3 ( 已經(jīng)可以找到鍵==3的value了) map.get(gh) = Early Spring! */ 【代碼解說】instanceof關(guān)鍵字: instanceof 檢查了此對象是否為null, 如果instanceof 左邊的對象為null, 則instanceof 會返回 false;


【17.9.1】理解hashCode()?

1)散列的目的在于: 想要使用一個(gè)對象查找另外一個(gè)對象;

2) 荔枝-使用 一對ArrayList 實(shí)現(xiàn) Map

// 使用 一對ArrayList 實(shí)現(xiàn) Map public class SlowMap<K, V> extends AbstractMap<K, V> {private List<K> keys = new ArrayList<K>();private List<V> values = new ArrayList<V>();public V put(K key, V value) {V oldValue = get(key); // The old value or nullif (!keys.contains(key)) {keys.add(key);values.add(value);} elsevalues.set(keys.indexOf(key), value);return oldValue;}// key的數(shù)據(jù)類型為 Object, 而不是 泛型 K 類型public V get(Object key) { // key is type Object, not Kif (!keys.contains(key))return null;return values.get(keys.indexOf(key));}public Set<Map.Entry<K, V>> entrySet() {Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>(); // EntrySet 容器Iterator<K> ki = keys.iterator();Iterator<V> vi = values.iterator();while (ki.hasNext())set.add(new MapEntry<K, V>(ki.next(), vi.next()));return set;}public static void main(String[] args) {SlowMap<String, String> m = new SlowMap<String, String>();m.putAll(Countries.capitals(5));System.out.println("m.putAll(Countries.capitals(5)), m = " + m);System.out.println("m.get(\"BULGARIA\") = " + m.get("BULGARIA"));System.out.println("m.entrySet() = " + m.entrySet());} } /* m.putAll(Countries.capitals(5)), m = {ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone} m.get("BULGARIA") = null m.entrySet() = [ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone] */【代碼解說】 Map.entrySet() 方法必須產(chǎn)生一個(gè) Map.entry 對象集;


3)如果想要創(chuàng)建自己的 Map 類型, 就必須同時(shí)定義 Map.Entry 的實(shí)現(xiàn);

【荔枝-定義自己的 MapEntry類型,且必須實(shí)現(xiàn) Map.Entry 接口】

// 定義自己的 MapEntry類型,且必須實(shí)現(xiàn) Map.Entry 接口 public class MapEntry<K, V> implements Map.Entry<K, V> {private K key;private V value;public MapEntry(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}public V setValue(V v) {V result = value;value = v;return result;}// 重寫 hashCode 方法@Overridepublic int hashCode() {return (key == null ? 0 : key.hashCode())^ (value == null ? 0 : value.hashCode());}// 重寫 equals 方法@Overridepublic boolean equals(Object o) {if (!(o instanceof MapEntry))return false;MapEntry me = (MapEntry) o;return (key == null ? me.getKey() == null : key.equals(me.getKey()))&& (value == null ? me.getValue() == null : value.equals(me.getValue()));}public String toString() {return key + "=" + value;} } // /:~

【代碼解說】

entrySet() 方法使用了 HashSet 來保持 鍵值對, 并且MapEntry 只使用了 key的hashCode() 方法;

以上自定義的 MapEntry 不是一個(gè)恰當(dāng)?shù)膶?shí)現(xiàn): 因?yàn)樗鼊?chuàng)建了 鍵值對的副本。。entrySet() 的切當(dāng)實(shí)現(xiàn)應(yīng)該在 Map 中提供視圖, 而不是副本, 并且這個(gè)視圖允許對原始映射表進(jìn)行修改(副本就不行)。。

【HashMap.Entry 定義源碼】


【17.9.2】 為速度而散列

1)散列的價(jià)值在于速度:
散列使得查詢得以快速進(jìn)行;

2)哈希沖突由外部鏈接處理:數(shù)組并不直接保存值,而是保存值的 list。然后對list 中的值使用 equals() 方法進(jìn)行線性查詢。如果散列函數(shù)好的話,list 所存儲的值就很少 (list保存著 散列碼相同的元素)。

3)基于LinkedList自定義HashMap的荔枝:

// 基于LinkedList自定義HashMap的荔枝 public class SimpleHashMap<K, V> extends AbstractMap<K, V> {private HashMap map;// Choose a prime number for the hash table// size, to achieve a uniform distribution:static final int SIZE = 997; // 設(shè)置hashmap的大小為質(zhì)數(shù)// You can't have a physical array of generics,// but you can upcast to one:@SuppressWarnings("unchecked")// HashMap 通過 LinkedList 來實(shí)現(xiàn)LinkedList<MapEntry<K, V>>[] buckets = new LinkedList[SIZE];// 插入或更新(如果有的話則更新,沒有的話則插入)public V put(K key, V value) {V oldValue = null;int index = Math.abs(key.hashCode()) % SIZE; // 散列函數(shù)計(jì)算散列碼if (buckets[index] == null) // 每個(gè)數(shù)組元素不是 保存值,而是保存值的鏈表list.buckets[index] = new LinkedList<MapEntry<K, V>>();LinkedList<MapEntry<K, V>> bucket = buckets[index];MapEntry<K, V> pair = new MapEntry<K, V>(key, value);boolean found = false;ListIterator<MapEntry<K, V>> it = bucket.listIterator();while (it.hasNext()) {MapEntry<K, V> iPair = it.next();if (iPair.getKey().equals(key)) {oldValue = iPair.getValue();it.set(pair); // Replace old with newfound = true;break;}}if (!found)buckets[index].add(pair);return oldValue;}// 通過鍵獲取值public V get(Object key) {int index = Math.abs(key.hashCode()) % SIZE;if (buckets[index] == null)return null;for (MapEntry<K, V> iPair : buckets[index])if (iPair.getKey().equals(key))return iPair.getValue();return null;}public Set<Map.Entry<K, V>> entrySet() {Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>();for (LinkedList<MapEntry<K, V>> bucket : buckets) {if (bucket == null)continue;for (MapEntry<K, V> mpair : bucket)set.add(mpair);}return set;}public static void main(String[] args) {SimpleHashMap<String, String> m = new SimpleHashMap<String, String>();m.putAll(Countries.capitals(5));System.out.println("m = " + m);System.out.println("m.get(\"BENIN\") = " + m.get("BENIN"));System.out.println("m.entrySet() = " + m.entrySet());} } /* m = {ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone} m.get("BENIN") = Porto-Novo m.entrySet() = [ANGOLA=Luanda, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, ALGERIA=Algiers, BOTSWANA=Gaberone] */

// 定義自己的 MapEntry類型,且必須實(shí)現(xiàn) Map.Entry 接口 public class MapEntry<K, V> implements Map.Entry<K, V> {private K key;private V value;public MapEntry(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}public V setValue(V v) {V result = value;value = v;return result;}// 重寫 hashCode 方法@Overridepublic int hashCode() {return (key == null ? 0 : key.hashCode())^ (value == null ? 0 : value.hashCode());}// 重寫 equals 方法@Overridepublic boolean equals(Object o) {if (!(o instanceof MapEntry))return false;MapEntry me = (MapEntry) o;return (key == null ? me.getKey() == null : key.equals(me.getKey()))&& (value == null ? me.getValue() == null : value.equals(me.getValue()));}public String toString() {return key + "=" + value;} } // /:~

【編碼技巧】為使散列分布均勻, bucket桶的數(shù)量通常使用 質(zhì)數(shù);


【17.9.3】覆蓋hashCode() 方法

1)buckets數(shù)組下標(biāo)值: 依賴于具體的 HashMap對象的容量, 而容量的改變依賴于與容器的充滿程度 和 負(fù)載因子有關(guān)。

2)hashCode() 生成的結(jié)果:經(jīng)過處理后成為桶的下標(biāo)(如取模);

3)設(shè)計(jì)hashCode() 方法最重要的因素: 無論何時(shí), 對同一個(gè)對象調(diào)用hashCode() 都應(yīng)該生成同樣的值;

4)String 和 hashCode()方法的荔枝:String有個(gè)特點(diǎn): 如果程序中有多個(gè)String對象, 都包含相同的字符串序列,那么這些 String 對象都映射到同一塊內(nèi)存區(qū)域;

看個(gè)荔枝:

String s1 = "abc"; String s2 = "abc"; String s3 = new String("abc"); String s4 = new String("abc");



public class StringHashCode {public static void main(String[] args) {String[] hellos = "Hello Hello".split(" ");System.out.println(hellos[0].hashCode()); //System.out.println(hellos[1].hashCode());} } /* hashCode 相同 69609650 69609650*/5)散列碼 5.1)散列碼的生成范圍不重要:只要是int就行了。。好的hashCode() 函數(shù)應(yīng)該產(chǎn)生分布均勻的散列碼; 5.2)散列碼不必是獨(dú)一無二的:應(yīng)該更關(guān)注速度,而不是唯一性;但是通過 hashCode() 和 equals() 方法,必須能夠完全確定對象的身份; 6)如何寫出一份像樣的 hashCode() 方法實(shí)現(xiàn)(共5個(gè)步驟):
【荔枝-根據(jù)以上5個(gè)步驟重寫 hashCode() 方法】 // 荔枝-根據(jù)Joshua Bloch的指導(dǎo)意見重寫 存入Map容器的元素的 hashCode() 方法 public class CountedString {private static List<String> created = new ArrayList<String>();private String s;private int id = 0;public CountedString(String str) {s = str;created.add(s);// id is the total number of instances// of this string in use by CountedString:for (String s2 : created)if (s2.equals(s))id++;}public String toString() {return "key = {" + s + " , id = " + id + " , hashCode() = " + hashCode() + "}, value ";}// 重寫 hashCode() 方法(根據(jù)Joshua Bloch的指導(dǎo)意見重寫hashCode() 方法)public int hashCode() {int result = 17; // 給int變量 result 賦予某個(gè)非零常量,如17result = 37 * result + s.hashCode(); // 為對象內(nèi)每個(gè)有意義的域計(jì)算一個(gè)int類型的散列碼(s.hashCode)result = 37 * result + id; // 合并計(jì)算得到的散列碼return result;}// 重寫 equals() 方法public boolean equals(Object o) {return o instanceof CountedString && s.equals(((CountedString) o).s)&& id == ((CountedString) o).id;}public static void main(String[] args) {Map<CountedString, Integer> map = new HashMap<CountedString, Integer>();CountedString[] cs = new CountedString[5];for (int i = 0; i < cs.length; i++) {cs[i] = new CountedString("hi");map.put(cs[i], i); // Autobox int -> Integer}print("map = " + map);for (CountedString cstring : cs) {print("Looking up cstring = " + cstring + ", map.get(cstring) = " + map.get(cstring));}} } /* map = {key = {hi , id = 4 , hashCode() = 146450}, value =3, key = {hi , id = 5 , hashCode() = 146451}, value =4, key = {hi , id = 2 , hashCode() = 146448}, value =1, key = {hi , id = 3 , hashCode() = 146449}, value =2, key = {hi , id = 1 , hashCode() = 146447}, value =0} Looking up cstring = key = {hi , id = 1 , hashCode() = 146447}, value , map.get(cstring) = 0 Looking up cstring = key = {hi , id = 2 , hashCode() = 146448}, value , map.get(cstring) = 1 Looking up cstring = key = {hi , id = 3 , hashCode() = 146449}, value , map.get(cstring) = 2 Looking up cstring = key = {hi , id = 4 , hashCode() = 146450}, value , map.get(cstring) = 3 Looking up cstring = key = {hi , id = 5 , hashCode() = 146451}, value , map.get(cstring) = 4 */【荔枝】重寫 equals(), hashCode(), compareTo() 方法: // 根據(jù)Joshua Bloch的指導(dǎo)意見重寫 存入Map容器的元素的 hashCode() 方法 public class Individual implements Comparable<Individual> {private static long counter = 0;private final long id = counter++;private String name;public Individual(String name) {this.name = name;}// 'name' is optional:public Individual() {}public String toString() {return getClass().getSimpleName() + (name == null ? "" : " " + name);}public long id() {return id;}// 重寫 equals() 方法@Overridepublic boolean equals(Object o) {return o instanceof Individual && id == ((Individual) o).id;}// 重寫 hashCode() 方法(注意 hashCode() 方法的實(shí)現(xiàn)方式)@Overridepublic int hashCode() {int result = 17; // 給int變量 result 賦予某個(gè)非零常量,如17if (name != null)result = 37 * result + name.hashCode(); // 為對象內(nèi)每個(gè)有意義的域計(jì)算一個(gè)int類型的散列碼(s.hashCode)result = 37 * result + (int) id; // 合并計(jì)算得到的散列碼return result; }// 實(shí)現(xiàn) Comparable 接口,并重寫 compareTo() 方法@Overridepublic int compareTo(Individual arg) {// Compare by class name first:String first = getClass().getSimpleName();String argFirst = arg.getClass().getSimpleName();int firstCompare = first.compareTo(argFirst); // 1.第一次比較 if (firstCompare != 0)return firstCompare;if (name != null && arg.name != null) {// 2.若第一次比較沒有結(jié)果,則進(jìn)行第二次比較int secondCompare = name.compareTo(arg.name);if (secondCompare != 0)return secondCompare;}// 3. 若類型無法比較兩則大小,則用 id 進(jìn)行比較return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));} } // /:~【編程技巧】為新類編寫正確的 hashCode() 和 equals() 方法是很需要技巧的;
【17.10】選擇接口的不同實(shí)現(xiàn) 1)容器列表: Map, List, Set 和 Queue; 2)遺留類: Hashtable, Vector, Stack 是過去遺留下來的類,目的只是為了支持老程序,不建議使用;
【17.10.1】性能測試框架 【17.10.2】對List的選擇 1)get() 和 set() 方法的隨機(jī)訪問: 1.1)背后有數(shù)組支撐的 List 和 ArrayList:無論列表的大小如何,這些訪問都很快速和一致; // ArrayList.set() and get() 源碼 public E set(int index, E element) {rangeCheck(index);E oldValue = elementData(index);elementData[index] = element;return oldValue;} public E get(int index) {rangeCheck(index);return elementData(index);} 1.2)對于LinkedList:訪問時(shí)間對于較大列表明顯增加。。如果你需要執(zhí)行大量 的隨機(jī)訪問,不建議時(shí)間 LinkedList; // LinkedList.set() and get() 源碼 public E set(int index, E element) {checkElementIndex(index);Node<E> x = node(index); // for 循環(huán)遍歷E oldVal = x.item;x.item = element;return oldVal;} public E get(int index) {checkElementIndex(index);return node(index).item;} 2)add()方法在列表中間插入新元素: 2.1)ArrayList.add():當(dāng)列表變大時(shí), ArrayList的開銷變大; // ArrayList.add() 源碼 public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size + 1); // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;} 2.2)LinkedList.add():當(dāng)列表變大時(shí),而LinkedList的開銷一直都很低廉; // LinkedList.add() 源碼public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));} 3)insert 和 remove() 操作: 3.1)ArrayList:插入和移除代價(jià)特別高昂,并且其代價(jià)隨列表尺寸的增加而增加; // ArrayList.remove() 源碼 public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;} 3.2)LinkedList:插入和移除代價(jià)特別低廉, 并且不隨列表尺寸發(fā)生變化; // LinkedList.remove() 源碼 public E remove() {return removeFirst();}public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);} private E unlinkFirst(Node<E> f) {// assert f == first && f != null;final E element = f.item;final Node<E> next = f.next;f.item = null;f.next = null; // help GCfirst = next;if (next == null)last = null;elsenext.prev = null;size--;modCount++;return element;} // 或者 remove(i) public E remove(int index) {checkElementIndex(index);return unlink(node(index));} 【17.10.3】微基準(zhǔn)測試的危險(xiǎn) 1)微基準(zhǔn)測試的注意事項(xiàng): 1.1)不能做太多的假設(shè),且將你的測試窄化; 1.2)確保測試程序運(yùn)行足夠長時(shí)間, 以產(chǎn)生有意義的數(shù)據(jù);并且要考慮到 某些 java hotspot技術(shù)只有在 程序運(yùn)行了一段時(shí)間后才會踢爆問題; 2)剖析器:可以把性能分析工作做得很好。java剖析器(MinView.net/Books/BetterJava) 或 其他開源的剖析器; 3)經(jīng)驗(yàn)證: Math.random() 的范圍是 [0,1):
【17.10.4】對Set的選擇(基于HashMap的HashSet, 基于TreeMap的TreeSet, 基于LinkedHashMap 的 LinkedHashSet) 1)HashSet 優(yōu)于 TreeSet : 特別是添加 和 查詢元素時(shí); 2)TreeSet 存在的唯一原因:它可以維持元素的排序狀態(tài);所以當(dāng)需要一個(gè)排好序的Set時(shí), 才應(yīng)該使用 TreeSet 容器;因?yàn)門reeSet 內(nèi)部支持排序,并且因?yàn)榈歉锌赡軋?zhí)行的操作,所以 TreeSet 迭代 速度 優(yōu)于 HashSet; 3)注意插入操作: HashSet 優(yōu)于 LinkedHashSet, 因?yàn)檫@是由 維護(hù)鏈表所帶來額外開銷造成的;
【17.10.5】對Map的選擇 1)除了 IdentityHashMap, 所有的Map 實(shí)現(xiàn)的插入操作都會隨著 Map 尺寸的變大 而明顯變慢;查找的代價(jià)小很多; 2)Hashtable 與 HashMap 性能相當(dāng): 因?yàn)镠ashMap 是用來 代替 Hashtable 的,因此它們使用了相同的底層存儲和查找機(jī)制; 3)TreeMap 比 HashMap 要慢:TreeMap是創(chuàng)建有序列表的方式; 3.1)TreeMap 查找鍵的流程: 調(diào)用 TreeMap.keySet() 方法來獲取鍵的Set 視圖,然后調(diào)用 toArray() 來產(chǎn)生由這些鍵構(gòu)成的數(shù)組。。之后,你可以使用靜態(tài)方法 Arrays.binarySearch() 在排序數(shù)組中快速查找對象; 3.2)HashMap 查找鍵的流程:HashMap 本身就被設(shè)計(jì)為 可以快速查找鍵 (優(yōu)先推薦使用 HashMap 查找,除非 HashMap 沒有使用的情況下,使用TreeMap的查找鍵的方式); 4)插入效率:LinkedHashMap 比 HashMap 慢: 因?yàn)樗S護(hù)散列數(shù)據(jù)結(jié)構(gòu)的同時(shí)還要維護(hù) 鏈表(以保持插入順序); 5)IdentityHashMap: 使用 == 而不是 equals() 來比較元素;
【HashMap的性能因子】 1)幾個(gè)術(shù)語: 容量:桶的個(gè)數(shù); 初始容量:表在在創(chuàng)建時(shí)所擁有的桶個(gè)數(shù); 尺寸:當(dāng)前被存儲的桶的數(shù)量; 負(fù)載因子: 尺寸/ 容量; 2)指定負(fù)載因子: HashMap 和 HashSet 都允許指定負(fù)載因子(在構(gòu)造器中),表示當(dāng)負(fù)載情況達(dá)到該負(fù)載因子的水平時(shí), 容器將自動增加其容量(桶個(gè)數(shù)),實(shí)現(xiàn)方式是使桶的容量加倍,并重新將現(xiàn)有對象分布到新的桶位集中;(再散列); 3)默認(rèn)負(fù)載因子: HashMap的默認(rèn) 負(fù)載因子是 0.75 (表示當(dāng)表的負(fù)載率達(dá)到 0.75時(shí), 才進(jìn)行再散列); 4)負(fù)載因子的影響: 高負(fù)載因子可以降低表所需要的空間,但是會增加查找代價(jià),這非常重要;因?yàn)椴檎也僮魇?用的比較多的 操作; 5)如何避免再散列的開銷: 如果知道在 HashMap中存儲多少項(xiàng), 那么創(chuàng)建一個(gè)具有恰當(dāng)大小的初始容量 可以避免 再散列;
【17.11】實(shí)用方法 1)容器的方法列表: 被表示為java.util.Collections 類內(nèi)部的靜態(tài)方法, 如下:


【注意】min() 和 max() 方法 只能作用于 Collection對象, 而不能作用于 List;所以你不要擔(dān)心 Collection是否應(yīng)該被排序; 只有在 執(zhí)行bianrySearch 之前, 才確實(shí)需要對 List 或 數(shù)組進(jìn)行排序; 【荔枝-Collections容器工具方法列表】 // 荔枝-Collections 容器工具方法列表 public class Utilities {/* Arrays.asList() 產(chǎn)生是list大小是固定的,不能執(zhí)行add() 操作(會拋異常),但是可以修改即set() 操作*/static List<String> list = Arrays.asList("one Two three Four five six one".split(" "));public static void main(String[] args) {print("list = " + list);// Collections.disjoint(c1,c2):當(dāng)兩個(gè)集合沒有相同元素時(shí)返回true. disjoint == 互斥,不相交的; // Collections.singletonList("Four"): 產(chǎn)生不可變的Set, List, Map, 它們都只包含基于給定參數(shù)的內(nèi)容而形成的單一項(xiàng);print("Collections.disjoint(list, Collections.singletonList(\"Four\")) = " + Collections.disjoint(list, Collections.singletonList("Four")));// Collections.max() 求最大元素 (默認(rèn)字符大小寫敏感)print("Collections.max(list) = " + Collections.max(list));// Collections.min() 求最小元素 (默認(rèn)字符大小寫敏感)print("Collections.min(list) = " + Collections.min(list));// Collections.max(list, String.CASE_INSENSITIVE_ORDER)) 在不考慮字母大小寫敏感的基礎(chǔ)上求最大元素;print("Collections.max(list, String.CASE_INSENSITIVE_ORDER) = " + Collections.max(list, String.CASE_INSENSITIVE_ORDER));// Collections.min(list, String.CASE_INSENSITIVE_ORDER)) 在不考慮字母大小寫敏感的基礎(chǔ)上求最小元素;print("Collections.min(list, String.CASE_INSENSITIVE_ORDER) = " + Collections.min(list, String.CASE_INSENSITIVE_ORDER));// Arrays.asList() 產(chǎn)生是list大小是固定的List<String> sublist = Arrays.asList("Four five six".split(" "));print("Arrays.asList(\"Four five six\".split(\" \")) = " + sublist);// Collections.indexOfSubList(list, sublist) 返回sublist在list中第一次出現(xiàn)的位置,或者找不到時(shí)返回 -1;print("Collections.indexOfSubList(list, sublist) = " + Collections.indexOfSubList(list, sublist)); // 返回3,注意// Collections.lastIndexOfSubList(list, sublist) 返回sublist在list中最后一次出現(xiàn)的位置,或者找不到時(shí)返回 -1;print("Collections.lastIndexOfSubList(list, sublist) = " + Collections.lastIndexOfSubList(list, sublist)); // 返回3,注意// Collections.replaceAll(list, "one", "Yo"), 用 Yo 替換 list中的所有 oneCollections.replaceAll(list, "one", "Yo");print("Collections.replaceAll(list, \"one\", \"Yo\"), list = " + list);// Collections.reverse(list), 逆轉(zhuǎn)list中所有元素的順序Collections.reverse(list);print("Collections.reverse(list), list = " + list);// Collections.rotate(list, 3); 所有元素向后移動 3個(gè)位置,并將末尾的元素循環(huán)移到前面去;Collections.rotate(list, 3);print("Collections.rotate(list, 3), list = " + list);List<String> source = Arrays.asList("in the matrix".split(" "));print("Arrays.asList(\"in the matrix\".split(\" \")) = source = " + source);// Collections.copy(list, source), 將source中的元素復(fù)制到list中(從位置0開始復(fù)制),若source元素?cái)?shù)量小于list,則list后面的元素會保留;Collections.copy(list, source);print("Collections.copy(list, source), list = " + list);// Collections.swap(list, i, j), 交換list中 位置i 和位置j 的元素Collections.swap(list, 0, list.size() - 1);print("Collections.swap(list, 0, list.size() - 1), list = " + list);// Collections.shuffle(list, new Random(47)), 隨機(jī)改變指定列表的順序, 也可以不指定 Random,而使用默認(rèn)的RandomCollections.shuffle(list, new Random(47));print("Collections.shuffle(list, new Random(47)), list = " + list);// Collections.fill(list, "pop"), 用 pop 替換list中的所有元素;Collections.fill(list, "pop");print("Collections.fill(list, \"pop\"), list = " + list);// Collections.frequency(list, "pop"),返回list中 等于 pop 的元素個(gè)數(shù)print("Collections.frequency(list, \"pop\") = " + Collections.frequency(list, "pop"));// Collections.nCopies(3, "snap"), 返回大小為3的 且大小和值均不可改變的List, 所有元素都引用 snap;List<String> dups = Collections.nCopies(3, "snap");print("Collections.nCopies(3, \"snap\") = dups = " + dups);// Collections.disjoint(list, dups) 兩個(gè)集合如果不相交,沒有交集,返回true, 否則返回falseprint("Collections.disjoint(list, dups) = " + Collections.disjoint(list, dups));print("Collections.disjoint(list, dups), list = " + list);// Collections.enumeration(dups), 為參數(shù)dups 生成一個(gè) 舊式 枚舉類Enumeration<String> e = Collections.enumeration(dups);print("Enumeration<String> e = Collections.enumeration(dups), e= ");Vector<String> v = new Vector<String>();while (e.hasMoreElements()) {String s = e.nextElement();print(s);v.addElement(s); // 把元素插入 vector}// Converting an old-style Vector to a List via an Enumeration:// 通過一個(gè)老式枚舉類 把一個(gè)老式 Vector 轉(zhuǎn)換為 ListArrayList<String> arrayList = Collections.list(v.elements());print("ArrayList<String> arrayList = Collections.list(v.elements()), arrayList: " + arrayList);} } /* list = [one, Two, three, Four, five, six, one] Collections.disjoint(list, Collections.singletonList("Four")) = false Collections.max(list) = three Collections.min(list) = Four Collections.max(list, String.CASE_INSENSITIVE_ORDER) = Two Collections.min(list, String.CASE_INSENSITIVE_ORDER) = five Arrays.asList("Four five six".split(" ")) = [Four, five, six] Collections.indexOfSubList(list, sublist) = 3 Collections.lastIndexOfSubList(list, sublist) = 3 Collections.replaceAll(list, "one", "Yo"), list = [Yo, Two, three, Four, five, six, Yo] Collections.reverse(list), list = [Yo, six, five, Four, three, Two, Yo] Collections.rotate(list, 3), list = [three, Two, Yo, Yo, six, five, Four] Arrays.asList("in the matrix".split(" ")) = source = [in, the, matrix] Collections.copy(list, source), list = [in, the, matrix, Yo, six, five, Four] Collections.swap(list, 0, list.size() - 1), list = [Four, the, matrix, Yo, six, five, in] Collections.shuffle(list, new Random(47)), list = [six, matrix, the, Four, Yo, five, in] Collections.fill(list, "pop"), list = [pop, pop, pop, pop, pop, pop, pop] Collections.frequency(list, "pop") = 7 Collections.nCopies(3, "snap") = dups = [snap, snap, snap] Collections.disjoint(list, dups) = true Collections.disjoint(list, dups), list = [pop, pop, pop, pop, pop, pop, pop] Enumeration<String> e = Collections.enumeration(dups), e= snap snap snap ArrayList<String> arrayList = Collections.list(v.elements()), arrayList: [snap, snap, snap] */ 【17.11.1】List的排序和查找 1)List排序和查找所使用的方法 與 對象數(shù)組所使用的相應(yīng)方法 有相同的名字和語法,只是用 Collections 的 static 代替 Arrays 的方法而已; // 荔枝-List的排序和查詢 public class ListSortSearch {public static void main(String[] args) {// Utilities.list: static List<String> list = Arrays.asList("one Two three Four five six one".split(" "));List<String> list = new ArrayList<String>(Utilities.list);list.addAll(Utilities.list);print("list = " + list);// Collections.shuffle(list, new Random(47)), 隨機(jī)改變list的元素順序Collections.shuffle(list, new Random(47));print("Collections.shuffle(list, new Random(47)), list = " + list);// Use a ListIterator to trim off the last elements:// 移除位置10(包括0)之后的元素ListIterator<String> it = list.listIterator(10);while (it.hasNext()) {it.next(); // 刪除元素時(shí),先執(zhí)行 next() 然后執(zhí)行 remove()。 因?yàn)閚ext()中, cursor = i;cursor = i+1; return elementData[lastRet = i]it.remove(); // remove() 方法中: 先 ArrayList.this.remove(lastRet), cursor = lastRet,lastRet = -1}print("Trimmed: " + list);// Collections.sort(list), list排序;(默認(rèn)大小寫敏感)Collections.sort(list);print("Collections.sort(list); list = " + list);String key = list.get(7);// Collections.binarySearch(list, key), 通過二叉查找找出 元素key的位置(默認(rèn)大小寫敏感)int index = Collections.binarySearch(list, key);print("Location of " + key + " is " + index + ", list.get(" + index + ") = " + list.get(index));// Collections.sort(list, String.CASE_INSENSITIVE_ORDER); list排序(大小寫不敏感)Collections.sort(list, String.CASE_INSENSITIVE_ORDER);print("Collections.sort(list, String.CASE_INSENSITIVE_ORDER), list = " + list);key = list.get(7);// Collections.binarySearch(list, key), 通過二叉查找找出 元素key的位置(大小寫不敏感)index = Collections.binarySearch(list, key, String.CASE_INSENSITIVE_ORDER);print("Location of " + key + " is " + index + ", list.get(" + index+ ") = " + list.get(index));} } /* list = [one, Two, three, Four, five, six, one, one, Two, three, Four, five, six, one] Collections.shuffle(list, new Random(47)), list = [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] Collections.sort(list); list = [Four, Two, five, five, one, one, six, six, three, three] Location of six is 7, list.get(7) = six Collections.sort(list, String.CASE_INSENSITIVE_ORDER), list = [five, five, Four, one, one, six, six, three, three, Two] Location of three is 7, list.get(7) = three */ // ArrrayList$Itr.next() remove() 方法源碼public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.remove(lastRet);cursor = lastRet;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}} 【17.11.2】設(shè)定Collection 或 Map 為不可修改(創(chuàng)建只讀容器) 1)荔枝-創(chuàng)建只讀容器,方法列表如下: // 荔枝-創(chuàng)建只讀容器,方法列表如下: /* Collections.unmodifiableCollection(new ArrayList()) Collections.unmodifiableList(new ArrayList()) Collections.unmodifiableSet(new HashSet()) Collections.unmodifiableSortedSet(new TreeSet()) // TreeSet 繼承了 SortedSet Collections.unmodifiableMap(new HashMap()) Collections.unmodifiableSortedMap(new TreeMap()) // TreeMap 繼承了 SortedMap */ public class ReadOnly {static Collection<String> data = new ArrayList<String>(Countries.names(6));public static void main(String[] args) {// Collections.unmodifiableCollection(new ArrayList<String>(data)), 創(chuàng)建只讀的 ArrayListCollection<String> c = Collections.unmodifiableCollection(new ArrayList<String>(data));print(c); // Reading is OK// ! c.add("one"); // Can't change it// Collections.unmodifiableList(new ArrayList<String>(data)), 創(chuàng)建只讀的 ArrayListList<String> a = Collections.unmodifiableList(new ArrayList<String>(data));ListIterator<String> lit = a.listIterator();print(lit.next()); // Reading is OK// ! lit.add("one"); // Can't change it// Collections.unmodifiableSet(new HashSet<String>(data)), 創(chuàng)建只讀的 HashSetSet<String> s = Collections.unmodifiableSet(new HashSet<String>(data));print(s); // Reading is OK// ! s.add("one"); // Can't change it// For a SortedSet: // Collections.unmodifiableSortedSet(new TreeSet<String>(data)), 創(chuàng)建只讀的 TreeSetSet<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data));print("Set<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data)), ss = " + ss);// Collections.unmodifiableMap(), 創(chuàng)建只讀的 HashMapMap<String, String> m = Collections.unmodifiableMap(new HashMap<String, String>(Countries.capitals(6)));print(m); // Reading is OK// ! m.put("Ralph", "Howdy!");// For a SortedMap:// Collections.unmodifiableSortedMap(),創(chuàng)建只讀的TreeMapMap<String, String> sm = Collections.unmodifiableSortedMap(new TreeMap<String, String>(Countries.capitals(6)));} } /* Collection<String> c = Collections.unmodifiableCollection(new ArrayList<String>(data)) c = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] ALGERIA [BENIN, BOTSWANA, ANGOLA, BURKINA FASO, ALGERIA, BURUNDI] Set<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data)), ss = [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] {BENIN=Porto-Novo, BOTSWANA=Gaberone, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ALGERIA=Algiers, BURUNDI=Bujumbura} */ // Collections 創(chuàng)建不可修改容器的方法源碼public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {return new UnmodifiableCollection<>(c);}// Collection$UnmodifiableCollection.java 源碼static class UnmodifiableCollection<E> implements Collection<E>, Serializable {private static final long serialVersionUID = 1820017752578914078L;final Collection<? extends E> c;UnmodifiableCollection(Collection<? extends E> c) {if (c==null)throw new NullPointerException();this.c = c;}public int size() {return c.size();}public boolean isEmpty() {return c.isEmpty();}public boolean contains(Object o) {return c.contains(o);}public Object[] toArray() {return c.toArray();}public <T> T[] toArray(T[] a) {return c.toArray(a);}public String toString() {return c.toString();}public Iterator<E> iterator() {return new Iterator<E>() {private final Iterator<? extends E> i = c.iterator();public boolean hasNext() {return i.hasNext();}public E next() {return i.next();}public void remove() {throw new UnsupportedOperationException();}@Overridepublic void forEachRemaining(Consumer<? super E> action) {// Use backing collection versioni.forEachRemaining(action);}};}public static <T> List<T> unmodifiableList(List<? extends T> list) {return (list instanceof RandomAccess ?new UnmodifiableRandomAccessList<>(list) :new UnmodifiableList<>(list));}public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {return new UnmodifiableSet<>(s);}public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s) {return new UnmodifiableSortedSet<>(s);}public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {return new UnmodifiableMap<>(m);}public static <K,V> SortedMap<K,V> unmodifiableSortedMap(SortedMap<K, ? extends V> m) {return new UnmodifiableSortedMap<>(m);} 【代碼解說】對不可修改的容器調(diào)用 修改方法(如新增或編輯),編譯器并不會報(bào)錯(cuò),但是任何改變?nèi)萜鲀?nèi)容的操作在執(zhí)行時(shí)都會引起 UnsupportedOperationException; 17.11.3】 Collection 或 Map的同步控制 1)Collections 類有辦法自動同步整個(gè)容器; 【干貨】 Collections 有很多有用的工具方法,如 通過Collections 創(chuàng)建不可修改的容器 和 自動同步整個(gè)容器; 【荔枝-Collection 或 Map的同步機(jī)制, Colletions 自動同步整個(gè)容器的方法列表】 // 荔枝-Collection 或 Map的同步機(jī)制, Colletions 自動同步整個(gè)容器的方法列表 // Collections.synchronizedCollection(new ArrayList()) // Collections.synchronizedList(new ArrayList()) // Collections.synchronizedSet(new HashSet<String>()) // Collections.synchronizedMap(new HashMap<String, String>()) // Collections.synchronizedSortedMap(new TreeMap<String, String>()) public class Synchronization {public static void main(String[] args) {Collection<String> c = Collections.synchronizedCollection(new ArrayList<String>());List<String> list = Collections.synchronizedList(new ArrayList<String>());Set<String> s = Collections.synchronizedSet(new HashSet<String>());Set<String> ss = Collections.synchronizedSortedSet(new TreeSet<String>());Map<String, String> m = Collections.synchronizedMap(new HashMap<String, String>());Map<String, String> sm = Collections.synchronizedSortedMap(new TreeMap<String, String>());} } // /:~// Collections 自動同步整個(gè)容器的方法源碼public static <T> Collection<T> synchronizedCollection(Collection<T> c) {return new SynchronizedCollection<>(c);}public static <T> List<T> synchronizedList(List<T> list) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list) :new SynchronizedList<>(list));}public static <T> Set<T> synchronizedSet(Set<T> s) {return new SynchronizedSet<>(s);}public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s) {return new SynchronizedSortedSet<>(s);}public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {return new SynchronizedMap<>(m);}public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) {return new SynchronizedSortedMap<>(m);}【java容器的快速報(bào)錯(cuò)機(jī)制】 1)快速報(bào)錯(cuò)機(jī)制: java容器類類庫采用了 快速報(bào)錯(cuò)機(jī)制。。該機(jī)制會探測容器上的任何除了你的進(jìn)程操作外的所有變化,一旦它發(fā)現(xiàn)其他進(jìn)程修改了容器,就會立刻拋出 ConcurrentModificationException異常。 2)工作原理: 只需創(chuàng)建一個(gè) 迭代器,然后向迭代器所指向的 Collection添加 同步方法,如下: 【荔枝-java容器的快速報(bào)錯(cuò)機(jī)制】 // 荔枝-快速報(bào)錯(cuò)機(jī)制 public class FailFast {public static void main(String[] args) {Collection<String> c = new ArrayList<String>();Iterator<String> it = c.iterator();c.add("An object");try {System.out.println("before it.next()");String s = it.next(); // it.next() 拋出異常System.out.println("after it.next()");} catch (ConcurrentModificationException e) {System.out.println(e);}} } /* before it.next() java.util.ConcurrentModificationException */ 【代碼解說】 it.next() 方法 拋出了異常。 因?yàn)槌绦虿糠? 在取得 容器的迭代器后, 程序部分2 又向容器插入了元素。。當(dāng)程序的不同部分修改同一個(gè)容器時(shí),就可能導(dǎo)致容器狀態(tài)不一致,所以java快速報(bào)錯(cuò)機(jī)制拋出了異常;; 這里拋出異常的原因是: 獲得迭代器后, 迭代器的?int expectedModCount = modCount; expectedModCount 被賦值, 然后 c.add("An object"); 又 把 mocCount ++ 了。又迭代器的next() 方法 需要校驗(yàn) expectedModCount 與 modCount 是否相等; 顯然 它們是不等的,所以 next() 方法拋出異常; // ArrayList$Itr.java 源碼荔枝 private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];} final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();} // ... } 【補(bǔ)充】 ConcurrentHashMap, CopyOnWriteArrayList 和 CopyOnWriteArraySet d都使用了可以避免 java.util.ConcurrentModificationException 的技術(shù); 【17.12】持有引用 1)對象是可獲得的:如果一個(gè)對象是可獲得的,垃圾回收器不會釋放它;如果是不可獲得的,垃圾回收期回收它是安全的; 2)普通引用:指的是沒有經(jīng)過 Reference 包裝過的引用,不會被垃圾回收器回收釋放; 3)有3個(gè)類繼承自 Reference:SoftReference, WeakReference, PhantomReference; 對應(yīng)不同級別的可獲得性(由強(qiáng)到弱排列)3.1)SoftReference:實(shí)現(xiàn)內(nèi)存敏感的高速緩存; 3.2)WeakReference:實(shí)現(xiàn)規(guī)范映射而設(shè)計(jì)的,不妨礙垃圾回收期回收 鍵值對; 4)使用 SoftReference 和 WeakReference 時(shí),可以選擇是否要將它們放入 ReferenceQueue;而 PhantomReference 僅僅依賴于 ReferencQueue; 【荔枝】 class VeryBig {private static final int SIZE = 10000;private long[] la = new long[SIZE];private String ident;public VeryBig(String id) {ident = id;}public String toString() {return ident;}protected void finalize() {System.out.println("Finalizing " + ident);} }public class References {private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();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 = new Integer(args[0]);LinkedList<SoftReference<VeryBig>> sa = new LinkedList<SoftReference<VeryBig>>();for (int i = 0; i < size; i++) {sa.add(new SoftReference<VeryBig>(new VeryBig("Soft " + i), rq));System.out.println("Just created: " + sa.getLast());checkQueue();}LinkedList<WeakReference<VeryBig>> wa = new LinkedList<WeakReference<VeryBig>>();for (int i = 0; i < size; i++) {wa.add(new WeakReference<VeryBig>(new VeryBig("Weak " + i), rq));System.out.println("Just created: " + wa.getLast());checkQueue();}SoftReference<VeryBig> s = new SoftReference<VeryBig>(new VeryBig("Soft"));WeakReference<VeryBig> w = new WeakReference<VeryBig>(new VeryBig("Weak"));System.gc();LinkedList<PhantomReference<VeryBig>> pa = new LinkedList<PhantomReference<VeryBig>>();for (int i = 0; i < size; i++) {pa.add(new PhantomReference<VeryBig>(new VeryBig("Phantom " + i),rq));System.out.println("Just created: " + pa.getLast());checkQueue();} } } /* 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.WeakReference@75b84c92 Just created: java.lang.ref.WeakReference@6bc7c054 Just created: java.lang.ref.WeakReference@232204a1 Just created: java.lang.ref.WeakReference@4aa298b7 Just created: java.lang.ref.WeakReference@7d4991ad Just created: java.lang.ref.WeakReference@28d93b30 Just created: java.lang.ref.WeakReference@1b6d3586 Just created: java.lang.ref.WeakReference@4554617c Just created: java.lang.ref.WeakReference@74a14482 Just created: java.lang.ref.WeakReference@1540e19d Finalizing Weak Finalizing Weak 9 Finalizing Weak 8 Finalizing Weak 7 Finalizing Weak 6 Finalizing Weak 5 Finalizing Weak 4 Finalizing Weak 3 Finalizing Weak 2 Finalizing Weak 1 Finalizing Weak 0 Just created: java.lang.ref.PhantomReference@677327b6 In queue: null Just created: java.lang.ref.PhantomReference@14ae5a5 In queue: null Just created: java.lang.ref.PhantomReference@7f31245a In queue: null Just created: java.lang.ref.PhantomReference@6d6f6e28 In queue: null Just created: java.lang.ref.PhantomReference@135fbaa4 In queue: null 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 */ 【17.12.1】 WeakHashMap: 用來保存 WeakReference; 1)這是一種節(jié)約存儲空間的技術(shù): 因?yàn)?WeakHashMap 允許垃圾回收器 自動清理鍵值對; 【荔枝WeakHashMap 允許垃圾回收器 自動清理鍵值】 class Element {private String ident; // 縮進(jìn)量public Element(String id) {ident = id;}public String toString() {return ident;}// 重寫 hashCode() 方法public int hashCode() {return ident.hashCode();}// 重寫 equals() 方法public boolean equals(Object r) {return r instanceof Element && ident.equals(((Element) r).ident);}protected void finalize() { // 垃圾回收時(shí)調(diào)用的方法System.out.println("Finalizing " + getClass().getSimpleName() + " "+ ident);} }class Key extends Element {public Key(String id) {super(id);} }class Value extends Element {public 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 = new Integer(args[0]);Key[] keys = new Key[size];// WeakHashMap 允許垃圾回收器 自動清理鍵值;WeakHashMap<Key, Value> map = new WeakHashMap<Key, Value>();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; // 把序號為3的倍數(shù)的位置上的元素保存為真實(shí)引用map.put(k, v);}System.gc(); // 垃圾回收} } /* 可以看到垃圾回收器 僅僅回收 不存在 keys[] 數(shù)組中的對象;(因?yàn)椴淮嬖趉eys[] 數(shù)組,該引用就不是真實(shí)引用,是不可獲得的引用,可以回收) Finalizing Key 476 Finalizing Key 245 Finalizing Key 244 Finalizing Key 242 Finalizing Key 241 Finalizing Key 239 Finalizing Key 238 Finalizing Key 236 Finalizing Key 235 Finalizing Key 233 Finalizing Key 232 Finalizing Key 230 Finalizing Key 229 Finalizing Key 227 Finalizing Key 226 */ 【17.13】Java 1.0/1.1 容器 【17.13.1】 Vector 和 Enumeration 1)Vector: 可以看做是 ArrayList; 2)Enumeration(==迭代器): java 1.0/1.1 版本 發(fā)明了 枚舉類 取代 迭代器;Enumeration 是一個(gè)接口,并不是一個(gè)實(shí)現(xiàn)類; 【迭代器-Enumeration】 // Vector 和 Enumeration 的荔枝 public class Enumerations {public static void main(String[] args) {// Vector 相當(dāng)于 ArrayList, Vector 是 老式的 ArrayList,不推薦 VectorVector<String> v = new Vector<String>(Countries.names(10));// Enumeration枚舉類 相當(dāng)于迭代器 Iterator, Enumeration 是老式的 Iterator,不推薦 EnumerationEnumeration<String> e = v.elements(); // 生成 Enumeration 對象;// Enumeration 的列表遍歷方式:while (e.hasMoreElements())System.out.print(e.nextElement() + ", ");// Collections.enumeration(new ArrayList<String>()), 把 ArrayList的Iterator迭代器 轉(zhuǎn)換為 Enumeration 枚舉類;e = Collections.enumeration(new ArrayList<String>());} } /* Collections.enumeration() 方法源碼 public static <T> Enumeration<T> enumeration(final Collection<T> c) {return new Enumeration<T>() { // 靜態(tài)內(nèi)部類private final Iterator<T> i = c.iterator();public boolean hasMoreElements() {return i.hasNext();}public T nextElement() {return i.next();}}; } */ /* ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC, CHAD, */ 【17.13.2】 Hashtable: 沒有理由使用 Hashtable了,使用 HashMap 替代; 【17.13.3】Stack:棧, 棧可以通過 LinkedList 快速實(shí)現(xiàn); 1)Stack 通過繼承 Vector 來實(shí)現(xiàn)棧,而不是 通過Vector 來構(gòu)建 Stack :這是一個(gè)糟糕的設(shè)計(jì),永遠(yuǎn)不要使用 Stack; public class Stack<E> extends Vector<E> { // Stack 實(shí)現(xiàn)源碼/*** Creates an empty Stack.*/public Stack() {} 【荔枝-實(shí)現(xiàn)棧的多種方式演示(但絕不能使用 Stack ,在生產(chǎn)過程中)】 // 枚舉類 enum Month {JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER } // 荔枝-實(shí)現(xiàn)棧的多種方式演示(但絕不能使用 Stack ,在生產(chǎn)過程中) public class Stacks {public static void main(String[] args) {// 第1種方式,通過 StackStack<String> stack = new Stack<String>();for (Month m : Month.values())stack.push(m.toString()); // 進(jìn)棧操作print("stack = " + stack);// Treating a stack as a Vector:stack.addElement("The last line");print("element 5 = " + stack.elementAt(5));print("popping elements:");while (!stack.empty())printnb(stack.pop() + " ");// 第2種方式,通過 LinkedListLinkedList<String> lstack = new LinkedList<String>();for (Month m : Month.values())lstack.addFirst(m.toString());System.out.println();print("lstack = " + lstack);while (!lstack.isEmpty())printnb(lstack.removeFirst() + " ");// Using the Stack class from// the Holding Your Objects Chapter:// 第3種方式: 通過 基于 LinkedList 的 Stack 來實(shí)現(xiàn)net.mindview.util.Stack<String> stack2 = new net.mindview.util.Stack<String>();for (Month m : Month.values())stack2.push(m.toString());System.out.println();print("stack2 = " + stack2);while (!stack2.empty())printnb(stack2.pop() + " ");} } /* 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 */【17.13.4】BitSet 1)BitSet: 最小容量是long:64位;可以高效率地存儲大量 開關(guān)信息; 2)如果需要高效的訪問時(shí)間, BitSet 比 本地?cái)?shù)組稍慢一點(diǎn); 【荔枝-Bits 插入 和 數(shù)據(jù)演示】 // 荔枝-Bits 插入 和 數(shù)據(jù)演示 public class Bits {public static void printBitSet(BitSet b) {print("bits: " + b);StringBuilder bbits = new StringBuilder();for (int j = 0; j < b.size(); j++)bbits.append(b.get(j) ? "1" : "0");print("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--) // 每個(gè)byte 1個(gè)字節(jié) = 8位,所以需要8次位操作if (((1 << i) & bt) != 0)bb.set(i);elsebb.clear(i);print("byte value: " + bt); // byte value: -107printBitSet(bb); // bits: {0, 2, 4, 7} bit pattern: 1010100100000000000000000000000000000000000000000000000000000000System.out.println();short st = (short) rand.nextInt();BitSet bs = new BitSet();for (int i = 15; i >= 0; i--) // 每個(gè)short 2個(gè)字節(jié) = 16位,所以需要16次位操作if (((1 << i) & st) != 0)bs.set(i);elsebs.clear(i);print("short value: " + st); //short value: 1302printBitSet(bs); //bits: {1, 2, 4, 8, 10} bit pattern: 0110100010100000000000000000000000000000000000000000000000000000System.out.println(); //int it = rand.nextInt();BitSet bi = new BitSet();for (int i = 31; i >= 0; i--) // 每個(gè) int 4個(gè)字節(jié) = 32位if (((1 << i) & it) != 0)bi.set(i);elsebi.clear(i);print("int value: " + it); // int value: -2014573909printBitSet(bi); // bits: {0, 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24, 25, 26, 31}// bit pattern: 1101010101010000001101111110000100000000000000000000000000000000System.out.println(); //// Test bitsets >= 64 bits:BitSet b127 = new BitSet();b127.set(127); print("set bit 127: " + b127); // set bit 127: {127}BitSet b255 = new BitSet(65);b255.set(255);print("set bit 255: " + b255); // set bit 255: {255}BitSet b1023 = new BitSet(512);b1023.set(1023);b1023.set(1024);print("set bit 1023: " + b1023); // set bit 1023: {1023, 1024}} } /* byte value: -107 bits: {0, 2, 4, 7} bit pattern: 1010100100000000000000000000000000000000000000000000000000000000short value: 1302 bits: {1, 2, 4, 8, 10} bit pattern: 0110100010100000000000000000000000000000000000000000000000000000int value: -2014573909 bits: {0, 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24, 25, 26, 31} bit pattern: 1101010101010000001101111110000100000000000000000000000000000000set bit 127: {127} set bit 255: {255} set bit 1023: {1023, 1024} */3)如何選擇使用 BitSet 還是 EnumSet 3.1)BitSet: 只有在運(yùn)行時(shí)才知道需要多少個(gè)標(biāo)志, 或標(biāo)志命名不合理, 或需要BitSet中的某種特殊操作; 3.2)EnumSet:允許按照名字而不是數(shù)字位的位置進(jìn)行操作,可以減少錯(cuò)誤; 還可以防止你因不注意添加新的標(biāo)志位置;

總結(jié)

以上是生活随笔為你收集整理的think-in-java(17)容器深入研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。