think-in-java(17)容器深入研究
注意: 17章接著 11章繼續分析 java容器,?think-in-java(11)
【17.1】完整的容器分類方法
【容器分類網絡解說】
1)接口:虛線框,沒有實線入邊(沒有實體類繼承關系,只有接口繼承關系);
2)抽象類:虛線框,有實現入邊(有實體類繼承關系);SortedSet 除外,SortedSet 明顯是一個接口, TreeSet 實現了?NavigableSet 接口,而?NavigableSet 接口繼承了?SortedSet ?接口(來自java 8 版本);
3)實體類:其他的都是繼承關系;
4)常用實體類: 加粗實線框;
5)Collections 和 Arrays 是工具類;
java 5 添加了新特性:
1)Queue接口(隊列可以通過 LinkedList來實現了,當然包括 Stack棧數據結構)機器實現 PriorityQueue 和 BlockingQueue阻塞隊列(21章 java 多線程編程介紹);
2)ConcurrentMap 接口 和 實現類 CurrentHashMap,也在21章;
3)CopyOnWriteArrayList 和 CopyOnWriteArraySet, 也是多線程的內容;
4)EnumSet 和 EnumMap,為使用 enum 而設計的 Set 和 Map的特殊實現;
5)Collections類中添加了多個 便利的工具方法;
【17.2】填充容器
1)用單個對象引用來填充 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) {/* 構建4個StringAddress對象,并封裝到List ,操作的都是同一個對象 */List<StringAddress> list = new ArrayList<StringAddress>(Collections.nCopies(4, new StringAddress("Hello")));System.out.println(list);/* 把 新對象StringAddress引用 填充到list中 ,操作的都是同一個對象 *//* Collections.fill() 方法只能替換 list中已經存在的元素,不能添加新元素 */Collections.fill(list, new StringAddress("World!"));System.out.println(list);} }
// 打印結果(List使用的都是同一個對象引用): [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的子容器都有接受另一個 Collection 對象的構造器,用所接受的 Collection對象中的元素來填充新容器;
【荔枝】通過適配器模式模擬 將 Collection容器對象作為輸入參數傳入 另一個 Collection的構造方法
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容器作為參數傳入 LinkedHashSet 構造器(這個適配器代碼很經典的) */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);} }
// 打印結果: [strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government]
public interface Generator<T> { T next(); } ///:~ 【元素的輸出順序與 插入順序相同】 因為?LinkedHashSet 保持了插入順序的鏈表列表;
【CollectionData 應用的荔枝】
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)));} } // 打印結果 [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的構造器中;
// 鍵值對類 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) { // 使用一個 generator 構建 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) { // 使用兩個 generator 構建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) { // 使用一個 泛型為key的generator,但值都相同的方法構建 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 和 一個 generator 構建mapfor (K key : genK) {put(key, genV.next());}}// An Iterable and a single value:public MapData(Iterable<K> genK, V value) { // 使用一個 泛型為 key的Iterable 但值都相同的 方式構建mapfor (K key : genK) {put(key, value);}}// Generic convenience methods:(泛型簡單方法,調用以上的構造方法)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);} }
【荔枝】調用MapData構建 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) {/* 以下構造方式看懂一個即可。非常經典的使用 一個容器 構造另一個容器的方法,調用過程非常經典 */// 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"));} }
// 打印結果: {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}
【補充】 可以使用工具來創建 任何用于 Map 或 Collection 的生成數據集,然后通過 構造器 或 Map.putAll() 和 Collection.putAll() 來初始化 Map 和Collection;
【總結】Collection的一個項存儲一個值,而map 的一個項存儲一個鍵值對;以上是 把 generator 適配到 Collection 和 map 的構造過程的方式,非常經典的調用,果然厲害;
【17.2.3】使用 Abstract: 顯然, 各種容器對應 的 Abstract類 是 繼承了 容器基類接口,實現基類接口的一部分方法或全部方法,然后實體類容器 再來繼承 Abstract類,這樣實體類容器無需實現 容器接口的全部方法。如AbstractCollection,?AbstractSet, AbstractList, AbstractMap 等容器抽象類;
【荔枝】創建定制的Map 和 Collection?
public class Countries {/* 二維數組 */public static final String[][] DATA = {// Africa{ "ALGERIA", "Algiers" },{ "ANGOLA", "Luanda" },{ "BENIN", "Porto-Novo" },{ "BOTSWANA", "Gaberone" },{ "BURKINA FASO", "Ouagadougou" },{ "BURUNDI", "Bujumbura" },{ "CAMEROON", "Yaounde" },{ "CAPE VERDE", "Praia" },{ "CENTRAL AFRICAN REPUBLIC", "Bangui" },{ "CHAD", "N'djamena" },{ "COMOROS", "Moroni" },{ "CONGO", "Brazzaville" },{ "DJIBOUTI", "Dijibouti" },{ "EGYPT", "Cairo" },{ "EQUATORIAL GUINEA", "Malabo" },{ "ERITREA", "Asmara" },{ "ETHIOPIA", "Addis Ababa" },{ "GABON", "Libreville" },{ "THE GAMBIA", "Banjul" },{ "GHANA", "Accra" },{ "GUINEA", "Conakry" },{ "BISSAU", "Bissau" },{ "COTE D'IVOIR (IVORY COAST)", "Yamoussoukro" },{ "KENYA", "Nairobi" },{ "LESOTHO", "Maseru" },{ "LIBERIA", "Monrovia" },{ "LIBYA", "Tripoli" },{ "MADAGASCAR", "Antananarivo" },{ "MALAWI", "Lilongwe" },{ "MALI", "Bamako" },{ "MAURITANIA", "Nouakchott" },{ "MAURITIUS", "Port Louis" },{ "MOROCCO", "Rabat" },{ "MOZAMBIQUE", "Maputo" },{ "NAMIBIA", "Windhoek" },{ "NIGER", "Niamey" },{ "NIGERIA", "Abuja" },{ "RWANDA", "Kigali" },{ "SAO TOME E PRINCIPE", "Sao Tome" },{ "SENEGAL", "Dakar" },{ "SEYCHELLES", "Victoria" },{ "SIERRA LEONE", "Freetown" },{ "SOMALIA", "Mogadishu" },{ "SOUTH AFRICA", "Pretoria/Cape Town" },{ "SUDAN", "Khartoum" },{ "SWAZILAND", "Mbabane" },{ "TANZANIA", "Dodoma" },{ "TOGO", "Lome" },{ "TUNISIA", "Tunis" },{ "UGANDA", "Kampala" },{ "DEMOCRATIC REPUBLIC OF THE CONGO (ZAIRE)", "Kinshasa" },{ "ZAMBIA", "Lusaka" },{ "ZIMBABWE", "Harare" },// Asia{ "AFGHANISTAN", "Kabul" },{ "BAHRAIN", "Manama" },{ "BANGLADESH", "Dhaka" },{ "BHUTAN", "Thimphu" },{ "BRUNEI", "Bandar Seri Begawan" },{ "CAMBODIA", "Phnom Penh" },{ "CHINA", "Beijing" },{ "CYPRUS", "Nicosia" },{ "INDIA", "New Delhi" },{ "INDONESIA", "Jakarta" },{ "IRAN", "Tehran" },{ "IRAQ", "Baghdad" },{ "ISRAEL", "Jerusalem" },{ "JAPAN", "Tokyo" },{ "JORDAN", "Amman" },{ "KUWAIT", "Kuwait City" },{ "LAOS", "Vientiane" },{ "LEBANON", "Beirut" },{ "MALAYSIA", "Kuala Lumpur" },{ "THE MALDIVES", "Male" },{ "MONGOLIA", "Ulan Bator" },{ "MYANMAR (BURMA)", "Rangoon" },{ "NEPAL", "Katmandu" },{ "NORTH KOREA", "P'yongyang" },{ "OMAN", "Muscat" },{ "PAKISTAN", "Islamabad" },{ "PHILIPPINES", "Manila" },{ "QATAR", "Doha" },{ "SAUDI ARABIA", "Riyadh" },{ "SINGAPORE", "Singapore" },{ "SOUTH KOREA", "Seoul" },{ "SRI LANKA", "Colombo" },{ "SYRIA", "Damascus" },{ "TAIWAN (REPUBLIC OF CHINA)", "Taipei" },{ "THAILAND", "Bangkok" },{ "TURKEY", "Ankara" },{ "UNITED ARAB EMIRATES", "Abu Dhabi" },{ "VIETNAM", "Hanoi" },{ "YEMEN", "Sana'a" },// Australia and Oceania{ "AUSTRALIA", "Canberra" },{ "FIJI", "Suva" },{ "KIRIBATI", "Bairiki" },{ "MARSHALL ISLANDS", "Dalap-Uliga-Darrit" },{ "MICRONESIA", "Palikir" },{ "NAURU", "Yaren" },{ "NEW ZEALAND", "Wellington" },{ "PALAU", "Koror" },{ "PAPUA NEW GUINEA", "Port Moresby" },{ "SOLOMON ISLANDS", "Honaira" },{ "TONGA", "Nuku'alofa" },{ "TUVALU", "Fongafale" },{ "VANUATU", "< Port-Vila" },{ "WESTERN SAMOA", "Apia" },// Eastern Europe and former USSR{ "ARMENIA", "Yerevan" },{ "AZERBAIJAN", "Baku" },{ "BELARUS (BYELORUSSIA)", "Minsk" },{ "BULGARIA", "Sofia" },{ "GEORGIA", "Tbilisi" },{ "KAZAKSTAN", "Almaty" },{ "KYRGYZSTAN", "Alma-Ata" },{ "MOLDOVA", "Chisinau" },{ "RUSSIA", "Moscow" },{ "TAJIKISTAN", "Dushanbe" },{ "TURKMENISTAN", "Ashkabad" },{ "UKRAINE", "Kyiv" },{ "UZBEKISTAN", "Tashkent" },// Europe{ "ALBANIA", "Tirana" }, { "ANDORRA", "Andorra la Vella" },{ "AUSTRIA", "Vienna" }, { "BELGIUM", "Brussels" },{ "BOSNIA", "-" },{ "HERZEGOVINA", "Sarajevo" },{ "CROATIA", "Zagreb" },{ "CZECH REPUBLIC", "Prague" },{ "DENMARK", "Copenhagen" },{ "ESTONIA", "Tallinn" },{ "FINLAND", "Helsinki" },{ "FRANCE", "Paris" },{ "GERMANY", "Berlin" },{ "GREECE", "Athens" },{ "HUNGARY", "Budapest" },{ "ICELAND", "Reykjavik" },{ "IRELAND", "Dublin" },{ "ITALY", "Rome" },{ "LATVIA", "Riga" },{ "LIECHTENSTEIN", "Vaduz" },{ "LITHUANIA", "Vilnius" },{ "LUXEMBOURG", "Luxembourg" },{ "MACEDONIA", "Skopje" },{ "MALTA", "Valletta" },{ "MONACO", "Monaco" },{ "MONTENEGRO", "Podgorica" },{ "THE NETHERLANDS", "Amsterdam" },{ "NORWAY", "Oslo" },{ "POLAND", "Warsaw" },{ "PORTUGAL", "Lisbon" },{ "ROMANIA", "Bucharest" },{ "SAN MARINO", "San Marino" },{ "SERBIA", "Belgrade" },{ "SLOVAKIA", "Bratislava" },{ "SLOVENIA", "Ljuijana" },{ "SPAIN", "Madrid" },{ "SWEDEN", "Stockholm" },{ "SWITZERLAND", "Berne" },{ "UNITED KINGDOM", "London" },{ "VATICAN CITY", "---" },// 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(): 實現 entrySet()方法來 應用 AbstractMap 抽象類private static class FlyweightMap extends AbstractMap<String, String> {// 靜態內部類private static class Entry implements Map.Entry<String, String> { // 靜態內部類int index;Entry(int index) {this.index = index;}/* 判斷 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(): 通過實現 size() 和 iterator() 方法來應用 AbstractSet 抽象類static class EntrySet extends AbstractSet<Map.Entry<String, String>> {// 靜態內部類private int size;EntrySet(int size) {if (size < 0)this.size = 0;// Can't be any bigger than the array:else if (size > DATA.length)this.size = DATA.length;elsethis.size = size;}public int size() {return size;}private class Iter implements Iterator<Map.Entry<String, String>> { // 內部類// 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"));} }
// 打印結果:: {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
【荔枝的關鍵之處】 通過繼承容器抽象類來創建定制的 Map 和 Collection 有多簡單。。為了創建只讀的 Map,可以繼承 AbstractMap 并實現 entrySet() 方法。為了創建只讀的 Set ,可以繼承 AbstractSet 并實現 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 創建只讀的List, 必須實現 size() 和 get() 方法;
【荔枝】創建定制的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> {// 靜態內部類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() 方法調用了 entrySet().iterator() 迭代器*//* iteraotr迭代器 調用了 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 轉 數組 */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(): 把一個容器添加到另一個容器中 */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(): 移除某個元素 */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(): 集合是否包含單個元素 */String val = Countries.DATA[3][0];print("c.contains(" + val + ") = " + c.contains(val));/* containsAll(): 集合間是否存在包含關系 */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(): 求兩個集合的交集 */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);} }
// 打印結果: 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 = []
1)可選操作是什么: 執行 各種不同的添加和移除的方法在 Collection接口中都是可選操作;
2)未獲支持的操作這種方式可以實現java容器類庫的一個重要目標: 容器應該易學易用;
3)UnsupportedOperationException 未獲支持的異常
3.1)UnsupportedOperationException 必須是一種罕見事件。。大多數情況下, 所有操作都應該是可以工作的,只有在特例中才會有 未獲支持的操作;
3.2)如果一個操作是未獲支持的,那么在實現接口時可能會導致?UnsupportedOperationException異常,而不是把 程序交給客戶之后才出現此異常,這種情況是有道理的;
3.3)注意: 未獲支持的操作只有在運行時才能探測到,因此它們表示動態類型檢查;
【17.4.1】未獲支持的操作
【荔枝】當執行未獲支持操作時,拋出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, 修改其某個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(" "));// 可修改的拷貝,因為 new ArrayList 是利用list的基本數組數據 進行深度拷貝。test("Modifiable Copy", new ArrayList<String>(list)); System.out.println();// 這個list(Arrays$ArrayList) 的基本數組 的 數據 是無法 改變的。test("Arrays.asList()", list); System.out.println();// 這是一種 不可修改的 拷貝,同 new ArrayList()test("unmodifiableList()", Collections.unmodifiableList(new ArrayList<String>(list))); } }
// 打印結果: --- 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)因為Arrays.asList() 會生成一個 List,它基于一個固定大小的數組,僅支持哪些不會改變數組大小的操作,對他而言是有道理的;
2)注意,應該把Arrays.asList() 產生的list 作為構造器參數傳給任何其他的 Collection(推薦用 Collections.addAll() 方法),這樣就可以生成允許使用所有容器方法的 list了;
3)list.set(0, "X") : 可以看到 修改 Arrays.asList() 產生的list 的 某個元素的時候 是不會拋出異常的,因為它并沒有修改 list容器的大小;
4)Arrays.asList() 返回了固定尺寸的List, 而 Collections.unmodifiableList() 產生不可修改的列表:所以你會看見? 當 list = ArrayList.asList()的時候,?list.set(0, "X") 執行通過;而當 list =?Collections.unmodifiableList() 時,則會拋出?java.lang.UnsupportedOperationException 未獲支持的異常;
【17.5】List的功能方法
1)List的常用方法列表: add()方法添加對象,get() 方法取出一個元素, 調用 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個位置)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(); // 返回當值的 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(); // 先返回值,后更新游標 it.remove();// 移除游標的上一個元素,然后更新游標; print("ListIterator.next(), ListIterator.remove(), a = " + a);// Must move to an element after remove():/* remove 操作后,必須調用next() 方法,因為 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");} } // 打印結果: // 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需要一種方式來維護存儲順序。而存儲順序的維護依賴于Set的不同實現(HashSet, TreeSet 和 LinkedHashSet);
2)Set 基類 和 子類:
2.1)Set接口: 存入 Set 的每個元素是唯一的,Set 不保持重復元素。 加入Set 的元素必須重寫 equals 方法 以確保對象唯一性;
2.2)HashSet* (默認選擇,優先推薦使用HashSet): 為快速查找而設計的 Set。 存入HashSet 的元素重寫 hashCode 方法;
2.3)TreeSet:保持順序的 Set, 底層基于 紅黑樹實現。存入 TreeSet 的元素必須實現 Comparable接口;
2.4)LinkedHashSet: 具有HashSet的 快速查詢優點, 內部使用鏈表維護元素順序(插入的次序)。存入LinkedHashSet的元素重寫 hashCode 方法;
3)重寫 equals? 和 hashCode 方法:?
3.1)重寫equals方法:必須為 存入 HashSet 和? TreeSet? 的元素 重寫其 equals 方法;
3.2)重寫 hashCode方法: 當元素被存儲到 HashSet 或 LinkedHashSet 中時,必須重寫元素的 hashCode 方法;
(建議: 良好的編碼風格是 同時重寫 equals 方法 和 hashCode 方法)
【荔枝-HashSet, TreeSet, LinkedHashSet存儲規則】
// 存入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的元素, 實現 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, 不重復的。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], 按照排序規則,有序System.out.println("\n======\n");// Things that don't work: Set 存儲不重復元素不起作用了(凡是使用到 hash散列機制的容器,必須重寫該容器中存儲元素的 hashCode 方法,否則容器的存儲規則不奏效)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 沒有實現 Comparable接口,不是Comparable子類,所以插入TreeSet失敗并拋出異常;} catch (Exception e) {System.out.println(e.getMessage());}try {test(new TreeSet<HashType>(), HashType.class); // HashType 沒有實現 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] // 按照排序規則,有序======[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() 方法的自然排序規則一致: equals 返回true,compareTo 方法返回 0;equals 返回 false, compareTo 方法返回 非0;
解說2)對于沒有重寫 hashCode() 方法的 SetType 或 TreeType, 如果將他們放置到任何散列實現中都會產生重復值;因為 HashSet 或 LinkedHashSet 中 重復的意義在于 元素的 哈希值或 hashCode 相同;
【干貨——HashSet 和 LinkedHashSet 底層采用 HashMap.key 進行存儲元素,因為 LinkedHashSet 繼承 HashSet】
// HashSet 的構造器 和 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 進行存儲元素】
// TreeMap 的構造器 和 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 存儲機制
HashSet, 以某種散列機制 保持所有元素;
LinkedHashSet, 按照元素插入的順序保存元素;?
TreeSet, 按照排序順序維護元素的次序, 排序規則 通過 實現 Comparable接口并重寫 compareTo() 方法來實現;
【17.6.1】SortedSet
1)介紹: SortedSet中的元素保證處于排序狀態; 其 comparator() 方法返回當前 Set 使用 的 Comparator;或者返回null 表示 以自然方式排序;
first() 返回第一個元素;
last() 返回最后一個元素;
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(); // 第一個元素String high = sortedSet.last(); // 最后一個元素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的排序: 按照對象的比較函數值? 對 元素排序;
【17.7】隊列
1)Queue的實現: java SE5 中的實現是 LinkedList 和 PriorityQueue, 它們的差異在于 排序行為而不是性能;
/* Queue容器進隊和出隊的荔枝 */ 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() 當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()); // 出隊順序與進隊順序一致test(new PriorityQueue<String>(), new Gen()); // 不一致test(new ArrayBlockingQueue<String>(count), new Gen()); // 出隊順序與進隊順序一致test(new ConcurrentLinkedQueue<String>(), new Gen()); // 出隊順序與進隊順序一致test(new LinkedBlockingQueue<String>(), new Gen()); // 出隊順序與進隊順序一致test(new PriorityBlockingQueue<String>(), new Gen()); // 不一致} }【代碼解說】除了優先級隊列?PriorityQueue 和 PriorityBlockingQueue, 其他隊列 將精確地按照進隊順序進行出隊操作;
【17.7.1】優先級隊列
/* 優先級隊列的荔枝 */ public class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {static class ToDoItem implements Comparable<ToDoItem> { // 靜態內部類private char primary;private int secondary;private String item;public ToDoItem(char pri, int sec, String item) {primary = pri;secondary = sec;this.item = item;}/* 優先級計算規則 */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; */
1)介紹: 可以在雙向隊列(雙端隊列) 的任何一段添加 或 移除元素。LinkedList提供了支持 雙向隊列的方法,但在java 標準類庫中 沒有任何顯式 的用于雙向隊列的接口;
2)如何實現雙向隊列: 使用組合來創建一個 Deque類, 并直接中 LinkedList中暴露方法,如下:
// 這是 作者 自定義的 雙向隊列 Deque, 該雙向隊列的底層基于 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... 這里添加其他必須的方法 } // /:~
【代碼解說】首先, 這個雙向隊列是 thinking-in-java 作者 自定義的,并不是java 類庫中的; 且該自定義的 雙向隊列是基于 LinkedList來實現的;
第二: Deque 雙向隊列沒有 Queue 那樣常用;
1)Map的幾種基本實現: HashMap, TreeMap, LinkedHashMap, WeakHashMap, ConcurrentHashMap, IdentityHashMap 等;
2)Map的簡單荔枝:
【荔枝-通過 2維數組 來實現 HashMap的鍵值對存儲原理】
// 通過 2維數組 來實現 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。當容器存儲元素存滿的時候,繼續插入元素,拋出異常。} 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 */
1)性能是映射表中的一個重要問題,當在 get() 中使用線性搜索時,執行速度回相當地慢,這正是 HashMap 提高速度的地方;
2)散列碼: HashMap 使用了特殊的值,稱作 散列碼,來取代對鍵的緩慢搜索; HashMap 使用對象的hashCode()方法進行快速查詢的;
3)下面是Map的基本實現類,其中HashMap 是默認選擇:
HashMap:Map基于散列表實現,取代了 Hashtable。通過構造器設置容量和負載因子,以調整容器性能;
LinkedHashMap:使用鏈表維護內部次序,其中輸出順序 是 其 插入順序 或者是最近最少使用(LRU, least recently used)次序;
TreeMap:基于紅黑樹實現, 被Comparable 或 Comparator 排序。TreeMap 是唯一有 subMap() 方法的Map, 它可以返回一個子樹;
WeakHashMap: 弱鍵映射,允許釋放映射所指向的對象;
ConcurrentHashMap:線程安全的Map, 不涉及同步加鎖;
IdentityHashMap:使用 == 代替 equals() 方法 對 鍵 進行比較的散列映射;
4)散列:散列是映射中存儲元素時最常用的方式;
5)任何Map容器所存儲的元素必須滿足以下要求:任何鍵都必須重寫? equals() 方法; 如果存儲在HashMap的元素, 該元素還必須重寫 hashCode()方法;如果使用 TreeMap 來存儲元素,則元素必須實現 Comparable;
【荔枝-Map實現類】
/* Map實現類的荔枝: 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 調用了 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> {// 靜態內部類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() 方法調用了 entrySet().iterator() 迭代器*//* iteraotr迭代器 調用了 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, 調用的也是 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容器中,鍵必須是唯一的,值可以重復;
【17.8.2】SortedMap(TreeMap 是 SortedMap 現階段的唯一實現)
1)使用SortedMap : 確保鍵處于排序狀態;
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(); // 第一個鍵Integer high = sortedMap.lastKey(); // 最后一個鍵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} */
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 {// 獲取構造器(采用反射機制來創建 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,調用map中每個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 實例, 為什么以 number=3的Groundhog 實例 為鍵 無法查詢到 其value ?因為 Groundhog 自動繼承自基類 Object, 所以這里使用了 Object.hashCode() 方法生成散列碼,而hashCode() 返回結果默認 使用對象的地址計算散列碼;
1)HashMap 使用 equals() 方法: 判斷當前的鍵是否與表中存在的鍵相同;
2)正確的 equals() 方法必須滿足下面5個條件:
自反性: 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;
一致性:如果對象中用于等價比較的信息沒有改變,則 無論調用? x.equals(y) 多少次,返回的結果應該保持一致,要么一直是true 或者一直 是 false;
對任何不是 null 的x, x.equals(null) 一定返回 false;
3)Object.hashCode 和 Object.equals() 方法的默認實現:
equals()方法默認? :比較對象的地址;
hashCode()方法默認:?使用對象的地址計算散列碼
// Object.equals() 和 Object.hashCode() 源碼public boolean equals(Object obj) {return (this == obj);} public native int hashCode(); 【干貨】如果要使用自己的類作為 HashMap的鍵,必須重載或重寫 hashCode() 和 equals() 方法;4)hashCode() 和 equals() 方法的區別在于:?hashCode方法用于計算鍵的散列碼并給元素分配對應散列碼的存儲位置; equals 是比較兩個鍵值對間的 鍵 是否相同;
【荔枝-?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() 方法 // 現在可以找到鍵==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 ( 已經可以找到鍵==3的value了) map.get(gh) = Early Spring! */ 【代碼解說】instanceof關鍵字: instanceof 檢查了此對象是否為null, 如果instanceof 左邊的對象為null, 則instanceof 會返回 false;
【17.9.1】理解hashCode()?
1)散列的目的在于: 想要使用一個對象查找另外一個對象;
2) 荔枝-使用 一對ArrayList 實現 Map
// 使用 一對ArrayList 實現 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的數據類型為 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() 方法必須產生一個 Map.entry 對象集;
3)如果想要創建自己的 Map 類型, 就必須同時定義 Map.Entry 的實現;
【荔枝-定義自己的 MapEntry類型,且必須實現 Map.Entry 接口】
// 定義自己的 MapEntry類型,且必須實現 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 不是一個恰當的實現: 因為它創建了 鍵值對的副本。。entrySet() 的切當實現應該在 Map 中提供視圖, 而不是副本, 并且這個視圖允許對原始映射表進行修改(副本就不行)。。
【HashMap.Entry 定義源碼】
【17.9.2】 為速度而散列
1)散列的價值在于速度:散列使得查詢得以快速進行;
2)哈希沖突由外部鏈接處理:數組并不直接保存值,而是保存值的 list。然后對list 中的值使用 equals() 方法進行線性查詢。如果散列函數好的話,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; // 設置hashmap的大小為質數// You can't have a physical array of generics,// but you can upcast to one:@SuppressWarnings("unchecked")// HashMap 通過 LinkedList 來實現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; // 散列函數計算散列碼if (buckets[index] == null) // 每個數組元素不是 保存值,而是保存值的鏈表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類型,且必須實現 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桶的數量通常使用 質數;
【17.9.3】覆蓋hashCode() 方法
1)buckets數組下標值: 依賴于具體的 HashMap對象的容量, 而容量的改變依賴于與容器的充滿程度 和 負載因子有關。
2)hashCode() 生成的結果:經過處理后成為桶的下標(如取模);
3)設計hashCode() 方法最重要的因素: 無論何時, 對同一個對象調用hashCode() 都應該生成同樣的值;
4)String 和 hashCode()方法的荔枝:String有個特點: 如果程序中有多個String對象, 都包含相同的字符串序列,那么這些 String 對象都映射到同一塊內存區域;
看個荔枝:
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() 函數應該產生分布均勻的散列碼; 5.2)散列碼不必是獨一無二的:應該更關注速度,而不是唯一性;但是通過 hashCode() 和 equals() 方法,必須能夠完全確定對象的身份; 6)如何寫出一份像樣的 hashCode() 方法實現(共5個步驟):
【荔枝-根據以上5個步驟重寫 hashCode() 方法】 // 荔枝-根據Joshua Bloch的指導意見重寫 存入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() 方法(根據Joshua Bloch的指導意見重寫hashCode() 方法)public int hashCode() {int result = 17; // 給int變量 result 賦予某個非零常量,如17result = 37 * result + s.hashCode(); // 為對象內每個有意義的域計算一個int類型的散列碼(s.hashCode)result = 37 * result + id; // 合并計算得到的散列碼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() 方法: // 根據Joshua Bloch的指導意見重寫 存入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() 方法的實現方式)@Overridepublic int hashCode() {int result = 17; // 給int變量 result 賦予某個非零常量,如17if (name != null)result = 37 * result + name.hashCode(); // 為對象內每個有意義的域計算一個int類型的散列碼(s.hashCode)result = 37 * result + (int) id; // 合并計算得到的散列碼return result; }// 實現 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.若第一次比較沒有結果,則進行第二次比較int secondCompare = name.compareTo(arg.name);if (secondCompare != 0)return secondCompare;}// 3. 若類型無法比較兩則大小,則用 id 進行比較return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));} } // /:~【編程技巧】為新類編寫正確的 hashCode() 和 equals() 方法是很需要技巧的;
【17.10】選擇接口的不同實現 1)容器列表: Map, List, Set 和 Queue; 2)遺留類: Hashtable, Vector, Stack 是過去遺留下來的類,目的只是為了支持老程序,不建議使用;
【17.10.1】性能測試框架 【17.10.2】對List的選擇 1)get() 和 set() 方法的隨機訪問: 1.1)背后有數組支撐的 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:訪問時間對于較大列表明顯增加。。如果你需要執行大量 的隨機訪問,不建議時間 LinkedList; // LinkedList.set() and get() 源碼 public E set(int index, E element) {checkElementIndex(index);Node<E> x = node(index); // for 循環遍歷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():當列表變大時, 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():當列表變大時,而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:插入和移除代價特別高昂,并且其代價隨列表尺寸的增加而增加; // 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:插入和移除代價特別低廉, 并且不隨列表尺寸發生變化; // 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】微基準測試的危險 1)微基準測試的注意事項: 1.1)不能做太多的假設,且將你的測試窄化; 1.2)確保測試程序運行足夠長時間, 以產生有意義的數據;并且要考慮到 某些 java hotspot技術只有在 程序運行了一段時間后才會踢爆問題; 2)剖析器:可以把性能分析工作做得很好。java剖析器(MinView.net/Books/BetterJava) 或 其他開源的剖析器; 3)經驗證: Math.random() 的范圍是 [0,1):
【17.10.4】對Set的選擇(基于HashMap的HashSet, 基于TreeMap的TreeSet, 基于LinkedHashMap 的 LinkedHashSet) 1)HashSet 優于 TreeSet : 特別是添加 和 查詢元素時; 2)TreeSet 存在的唯一原因:它可以維持元素的排序狀態;所以當需要一個排好序的Set時, 才應該使用 TreeSet 容器;因為TreeSet 內部支持排序,并且因為迭代是更有可能執行的操作,所以 TreeSet 迭代 速度 優于 HashSet; 3)注意插入操作: HashSet 優于 LinkedHashSet, 因為這是由 維護鏈表所帶來額外開銷造成的;
【17.10.5】對Map的選擇 1)除了 IdentityHashMap, 所有的Map 實現的插入操作都會隨著 Map 尺寸的變大 而明顯變慢;查找的代價小很多; 2)Hashtable 與 HashMap 性能相當: 因為HashMap 是用來 代替 Hashtable 的,因此它們使用了相同的底層存儲和查找機制; 3)TreeMap 比 HashMap 要慢:TreeMap是創建有序列表的方式; 3.1)TreeMap 查找鍵的流程: 調用 TreeMap.keySet() 方法來獲取鍵的Set 視圖,然后調用 toArray() 來產生由這些鍵構成的數組。。之后,你可以使用靜態方法 Arrays.binarySearch() 在排序數組中快速查找對象; 3.2)HashMap 查找鍵的流程:HashMap 本身就被設計為 可以快速查找鍵 (優先推薦使用 HashMap 查找,除非 HashMap 沒有使用的情況下,使用TreeMap的查找鍵的方式); 4)插入效率:LinkedHashMap 比 HashMap 慢: 因為它維護散列數據結構的同時還要維護 鏈表(以保持插入順序); 5)IdentityHashMap: 使用 == 而不是 equals() 來比較元素;
【HashMap的性能因子】 1)幾個術語: 容量:桶的個數; 初始容量:表在在創建時所擁有的桶個數; 尺寸:當前被存儲的桶的數量; 負載因子: 尺寸/ 容量; 2)指定負載因子: HashMap 和 HashSet 都允許指定負載因子(在構造器中),表示當負載情況達到該負載因子的水平時, 容器將自動增加其容量(桶個數),實現方式是使桶的容量加倍,并重新將現有對象分布到新的桶位集中;(再散列); 3)默認負載因子: HashMap的默認 負載因子是 0.75 (表示當表的負載率達到 0.75時, 才進行再散列); 4)負載因子的影響: 高負載因子可以降低表所需要的空間,但是會增加查找代價,這非常重要;因為查找操作是 用的比較多的 操作; 5)如何避免再散列的開銷: 如果知道在 HashMap中存儲多少項, 那么創建一個具有恰當大小的初始容量 可以避免 再散列;
【17.11】實用方法 1)容器的方法列表: 被表示為java.util.Collections 類內部的靜態方法, 如下:
【注意】min() 和 max() 方法 只能作用于 Collection對象, 而不能作用于 List;所以你不要擔心 Collection是否應該被排序; 只有在 執行bianrySearch 之前, 才確實需要對 List 或 數組進行排序; 【荔枝-Collections容器工具方法列表】 // 荔枝-Collections 容器工具方法列表 public class Utilities {/* Arrays.asList() 產生是list大小是固定的,不能執行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):當兩個集合沒有相同元素時返回true. disjoint == 互斥,不相交的; // Collections.singletonList("Four"): 產生不可變的Set, List, Map, 它們都只包含基于給定參數的內容而形成的單一項;print("Collections.disjoint(list, Collections.singletonList(\"Four\")) = " + Collections.disjoint(list, Collections.singletonList("Four")));// Collections.max() 求最大元素 (默認字符大小寫敏感)print("Collections.max(list) = " + Collections.max(list));// Collections.min() 求最小元素 (默認字符大小寫敏感)print("Collections.min(list) = " + Collections.min(list));// Collections.max(list, String.CASE_INSENSITIVE_ORDER)) 在不考慮字母大小寫敏感的基礎上求最大元素;print("Collections.max(list, String.CASE_INSENSITIVE_ORDER) = " + Collections.max(list, String.CASE_INSENSITIVE_ORDER));// Collections.min(list, String.CASE_INSENSITIVE_ORDER)) 在不考慮字母大小寫敏感的基礎上求最小元素;print("Collections.min(list, String.CASE_INSENSITIVE_ORDER) = " + Collections.min(list, String.CASE_INSENSITIVE_ORDER));// Arrays.asList() 產生是list大小是固定的List<String> sublist = Arrays.asList("Four five six".split(" "));print("Arrays.asList(\"Four five six\".split(\" \")) = " + sublist);// Collections.indexOfSubList(list, sublist) 返回sublist在list中第一次出現的位置,或者找不到時返回 -1;print("Collections.indexOfSubList(list, sublist) = " + Collections.indexOfSubList(list, sublist)); // 返回3,注意// Collections.lastIndexOfSubList(list, sublist) 返回sublist在list中最后一次出現的位置,或者找不到時返回 -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), 逆轉list中所有元素的順序Collections.reverse(list);print("Collections.reverse(list), list = " + list);// Collections.rotate(list, 3); 所有元素向后移動 3個位置,并將末尾的元素循環移到前面去;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中的元素復制到list中(從位置0開始復制),若source元素數量小于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)), 隨機改變指定列表的順序, 也可以不指定 Random,而使用默認的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 的元素個數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) 兩個集合如果不相交,沒有交集,返回true, 否則返回falseprint("Collections.disjoint(list, dups) = " + Collections.disjoint(list, dups));print("Collections.disjoint(list, dups), list = " + list);// Collections.enumeration(dups), 為參數dups 生成一個 舊式 枚舉類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:// 通過一個老式枚舉類 把一個老式 Vector 轉換為 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排序和查找所使用的方法 與 對象數組所使用的相應方法 有相同的名字和語法,只是用 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)), 隨機改變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(); // 刪除元素時,先執行 next() 然后執行 remove()。 因為next()中, 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排序;(默認大小寫敏感)Collections.sort(list);print("Collections.sort(list); list = " + list);String key = list.get(7);// Collections.binarySearch(list, key), 通過二叉查找找出 元素key的位置(默認大小寫敏感)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】設定Collection 或 Map 為不可修改(創建只讀容器) 1)荔枝-創建只讀容器,方法列表如下: // 荔枝-創建只讀容器,方法列表如下: /* 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)), 創建只讀的 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)), 創建只讀的 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)), 創建只讀的 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)), 創建只讀的 TreeSetSet<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data));print("Set<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data)), ss = " + ss);// Collections.unmodifiableMap(), 創建只讀的 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(),創建只讀的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 創建不可修改容器的方法源碼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);} 【代碼解說】對不可修改的容器調用 修改方法(如新增或編輯),編譯器并不會報錯,但是任何改變容器內容的操作在執行時都會引起 UnsupportedOperationException; 【17.11.3】 Collection 或 Map的同步控制 1)Collections 類有辦法自動同步整個容器; 【干貨】 Collections 有很多有用的工具方法,如 通過Collections 創建不可修改的容器 和 自動同步整個容器; 【荔枝-Collection 或 Map的同步機制, Colletions 自動同步整個容器的方法列表】 // 荔枝-Collection 或 Map的同步機制, Colletions 自動同步整個容器的方法列表 // 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 自動同步整個容器的方法源碼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容器的快速報錯機制】 1)快速報錯機制: java容器類類庫采用了 快速報錯機制。。該機制會探測容器上的任何除了你的進程操作外的所有變化,一旦它發現其他進程修改了容器,就會立刻拋出 ConcurrentModificationException異常。 2)工作原理: 只需創建一個 迭代器,然后向迭代器所指向的 Collection添加 同步方法,如下: 【荔枝-java容器的快速報錯機制】 // 荔枝-快速報錯機制 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() 方法 拋出了異常。 因為程序部分1 在取得 容器的迭代器后, 程序部分2 又向容器插入了元素。。當程序的不同部分修改同一個容器時,就可能導致容器狀態不一致,所以java快速報錯機制拋出了異常;; 這里拋出異常的原因是: 獲得迭代器后, 迭代器的?int expectedModCount = modCount; expectedModCount 被賦值, 然后 c.add("An object"); 又 把 mocCount ++ 了。又迭代器的next() 方法 需要校驗 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();} // ... } 【補充】 ConcurrentHashMap, CopyOnWriteArrayList 和 CopyOnWriteArraySet d都使用了可以避免 java.util.ConcurrentModificationException 的技術; 【17.12】持有引用 1)對象是可獲得的:如果一個對象是可獲得的,垃圾回收器不會釋放它;如果是不可獲得的,垃圾回收期回收它是安全的; 2)普通引用:指的是沒有經過 Reference 包裝過的引用,不會被垃圾回收器回收釋放; 3)有3個類繼承自 Reference:SoftReference, WeakReference, PhantomReference; 對應不同級別的可獲得性(由強到弱排列); 3.1)SoftReference:實現內存敏感的高速緩存; 3.2)WeakReference:實現規范映射而設計的,不妨礙垃圾回收期回收 鍵值對; 4)使用 SoftReference 和 WeakReference 時,可以選擇是否要將它們放入 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)這是一種節約存儲空間的技術: 因為 WeakHashMap 允許垃圾回收器 自動清理鍵值對; 【荔枝WeakHashMap 允許垃圾回收器 自動清理鍵值】 class Element {private String ident; // 縮進量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() { // 垃圾回收時調用的方法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的倍數的位置上的元素保存為真實引用map.put(k, v);}System.gc(); // 垃圾回收} } /* 可以看到垃圾回收器 僅僅回收 不存在 keys[] 數組中的對象;(因為不存在keys[] 數組,該引用就不是真實引用,是不可獲得的引用,可以回收) 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 版本 發明了 枚舉類 取代 迭代器;Enumeration 是一個接口,并不是一個實現類; 【迭代器-Enumeration】 // Vector 和 Enumeration 的荔枝 public class Enumerations {public static void main(String[] args) {// Vector 相當于 ArrayList, Vector 是 老式的 ArrayList,不推薦 VectorVector<String> v = new Vector<String>(Countries.names(10));// Enumeration枚舉類 相當于迭代器 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迭代器 轉換為 Enumeration 枚舉類;e = Collections.enumeration(new ArrayList<String>());} } /* Collections.enumeration() 方法源碼 public static <T> Enumeration<T> enumeration(final Collection<T> c) {return new Enumeration<T>() { // 靜態內部類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 快速實現; 1)Stack 通過繼承 Vector 來實現棧,而不是 通過Vector 來構建 Stack :這是一個糟糕的設計,永遠不要使用 Stack; public class Stack<E> extends Vector<E> { // Stack 實現源碼/*** Creates an empty Stack.*/public Stack() {} 【荔枝-實現棧的多種方式演示(但絕不能使用 Stack ,在生產過程中)】 // 枚舉類 enum Month {JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER } // 荔枝-實現棧的多種方式演示(但絕不能使用 Stack ,在生產過程中) 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()); // 進棧操作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 來實現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位;可以高效率地存儲大量 開關信息; 2)如果需要高效的訪問時間, BitSet 比 本地數組稍慢一點; 【荔枝-Bits 插入 和 數據演示】 // 荔枝-Bits 插入 和 數據演示 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--) // 每個byte 1個字節 = 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--) // 每個short 2個字節 = 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--) // 每個 int 4個字節 = 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: 只有在運行時才知道需要多少個標志, 或標志命名不合理, 或需要BitSet中的某種特殊操作; 3.2)EnumSet:允許按照名字而不是數字位的位置進行操作,可以減少錯誤; 還可以防止你因不注意添加新的標志位置;
總結
以上是生活随笔為你收集整理的think-in-java(17)容器深入研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关闭阀门有什么讲究
- 下一篇: thinking-in-java(13)