主頁 >  其他 > 第十七章 容器深入研究

第十七章 容器深入研究

2020-10-12 14:21:25 其他

文章目錄

          • 1.完整的容器分類法
          • 2.填充容器
            • 復制同一個物件參考來填充容器
            • 一種Generator解決方案
            • Map生成器
            • 使用Abstract類
          • 3.Collection的功能方法
          • 4.可選操作
          • 5.List的功能方法
          • 6.Set和存盤順序
            • 型別
            • 從輸出看出
            • SortedSet
          • 7.佇列
            • 優先級佇列
            • 雙向佇列
          • 8.理解Map
            • 標準的Java類別庫中包含了Map的幾種基本實作,包括:
            • 實作Map介面
            • SortedMap
            • LinkedHashMap
          • 9.散列與散列碼
            • 為速度而散列
            • 覆寫hashCode()
          • 10.選擇介面的不同實作
            • 例如List介面
            • Set介面
            • 對List的選擇
            • 對Set的選擇
            • 對Map的選擇
          • 11.實用方法
            • List的排序和查詢
            • 設定Collection或Map為不可修改
            • Collection或Map的同步控制
          • 12.持有參考
          • 13.Java 1.0/1.1 的容器
            • Vector和Enumeration

在這里插入圖片描述

1.完整的容器分類法

在這里插入圖片描述

2.填充容器
  1. 復制同一個物件參考來填充容器
  • (1) Collections.nCopies()
  • (2) Collections.fill()
  1. toString():回傳類名+散列碼的無符號十六進制

  2. 一種Generator解決方案
  • (1) 所有的Collection子型別都有一個接收另一個Collection物件的構造器,用所接收的Collection物件中的元素來填充新的容器
  • (2) 泛型便利方法(例如addAll())可以減少在使用類時所必需的型別檢查數量
  1. Map生成器
  • (1) 例
package Chapter17.Example;

import java.util.*;

import net.mindview.util.*;

import static net.mindview.util.Print.*;

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();
            }
        };
    }
}
package Chapter17.Example;
import net.mindview.util.Generator;

import java.util.Iterator;
import java.util.LinkedHashMap;

public class MapData<K, V> extends LinkedHashMap<K, V> {
    public MapData(Generator<Pair<K, V>> gen, int quantity) {
        for(int i = 0; i < quantity; ++i) {
            Pair<K, V> p = (Pair)gen.next();
            this.put(p.key, p.value);
        }

    }
    public MapData(Generator<K> genK, Generator<V> genV, int quantity) {
        for(int i = 0; i < quantity; ++i) {
            this.put(genK.next(), genV.next());
        }

    }
    public MapData(Generator<K> genK, V value, int quantity) {
        for(int i = 0; i < quantity; ++i) {
            this.put(genK.next(), value);
        }

    }
    public MapData(Iterable<K> genK, Generator<V> genV) {
        Iterator var4 = genK.iterator();

        while(var4.hasNext()) {
            K key = (K) var4.next();
            this.put(key, genV.next());
        }

    }
    public MapData(Iterable<K> genK, V value) {
        Iterator var4 = genK.iterator();

        while(var4.hasNext()) {
            K key = (K) var4.next();
            this.put(key, value);
        }

    }
    public static <K, V> MapData<K, V> map(Generator<Pair<K, V>> gen, int quantity) {
        return new MapData(gen, quantity);
    }
    public static <K, V> MapData<K, V> map(Generator<K> genK, Generator<V> genV, int quantity) {
        return new MapData(genK, genV, quantity);
    }
    public static <K, V> MapData<K, V> map(Generator<K> genK, V value, int quantity) {
        return new MapData(genK, value, quantity);
    }
    public static <K, V> MapData<K, V> map(Iterable<K> genK, Generator<V> genV) {
        return new MapData(genK, genV);
    }
    public static <K, V> MapData<K, V> map(Iterable<K> genK, V value) {
        return new MapData(genK, value);
    }
}
package Chapter17.Example;

import net.mindview.util.CountingGenerator;
import net.mindview.util.RandomGenerator;

import static net.mindview.util.Print.print;

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"));
    }
}
package Chapter17.Example;

public class Pair<K, V> {
    public final K key;
    public final V value;

    public Pair(K k, V v) {
        key = k;
        value = v;
    }
}
  • (2) 創建一個Pair<V,K>類,再呼叫Generator.next()方法來組裝Map
  • (3) Map配接器可以使用各種不同的Generator、Iterator和常量值的組合來填充Map初始化物件
  • (4) 通過產生一個Iterator還實作了Iterable,
  1. 使用Abstract類
  • (1) 例:
package Chapter17.Test5;

import java.util.*;

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 class Entry implements Map.Entry<Integer, String> {
        int index;

        Entry(int index) {
            this.index = index;
        }

        public boolean equals(Object o) {
            return o instanceof Entry && index == ((Entry) o).index;
        }

        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();
        }
    }

    class EntrySet extends AbstractSet<Map.Entry<Integer, String>> {
        public int size() {
            return size;
        }

        private class Iter implements Iterator<Map.Entry<Integer, String>> {
            private Entry entry = new Entry(-1);

            public boolean hasNext() {
                return entry.index < size - 1;
            }

            public Map.Entry<Integer, String> next() {
                entry.index++;
                return entry;
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        public Iterator<Map.Entry<Integer, String>> iterator() {
            return new Iter();
        }
    }

    private Set<Map.Entry<Integer, String>> entries =
            new EntrySet();

    public Set<Map.Entry<Integer, String>> entrySet() {
        return entries;
    }
}
package Chapter17.Test5;

public class Test5 {
    public static void main(String[] args) {
        System.out.println(new CountingMapData(60));
    }
}
  • (1) 產生容器的測驗資料:創建定制的Collection和Map實作
  • (2) 享元設計模式:在普通的解決方案需要過多的物件,或者產生普通物件太占用空間時使用享元
  • (3) 為了創建只讀的Map,可以繼承AbstractMap并實作entrySet(),為了創建只讀的Set,可以繼承AbstractSet并實作iterator()和size()
3.Collection的功能方法
  1. boolean add(T):像容器添加內容添加失敗回傳false
  2. boolean addAll(Collection<? extends E> c):添加引數中所有元素,有任意元素添加成功則回傳true
  3. void clear():移除容器中所有元素
  4. boolean contains(T):如果容器已經持有泛型型別T的引數,則回傳true
  5. boolean containsAll(Collection<?>):如果容器中持有引數中所有元素回傳true
  6. boolean isEmpty():容器中沒有元素,回傳true
  7. Iterator iterator():回傳一個Iterator,可用來遍歷容器中的元素
  8. boolean remove(Object o):如果元素再容器中,則移除一個元素實體,如果做了移除動作,則回傳true
  9. boolean removeAll(Collection<?> c):一處引數中所有元素,如果有移除動作,則回傳true
  10. boolean retainAll(Collection<?> c):只保存引數中元素,Collection發生改變回傳true
  11. int size():回傳容器中元素數目
  12. Object[] toArray():回傳一個陣列,該陣列包含容器中所有元素
  13. T[] toArray(T[] a):回傳一個包含所有容器內元素的陣列,但回傳型別為T而不簡單是Object
4.可選操作
  1. 執行各種不同的添加和移除的方法在Collection介面中都是可選操作,這意味著實作類并不需要為這些方法提供功能定義
  2. “可選操作”可以防止設計中出現介面爆炸的情況
  3. 容器應該易學易用,未獲得支持的操作時一種特例,可以延遲到需要時再實作
  4. 如果一個操作未獲得支持,那么實作介面會導致UnsupportedOperationException例外
  5. 未獲支持的操作
  • (1) 最常見的未獲支持的操作,都是源于背后由固定尺寸的資料結構支持的容器
  • (2) 任何會引起對底層資料結構的尺寸進行修改的方法都會產生一個UnsupportedOperationException例外,以表示對未獲支持操作的呼叫(一個編程錯誤)
  • (3) 未獲支持的操作只有在運行時才能檢測到,因此它們表示動態型別檢查
5.List的功能方法
  1. 基本的List很容易使用:大多數時候只是呼叫add()添加物件,使用get()一次取出一個元素,以及呼叫iterator()獲取用于該序列的Iterator
package Chapter17.Example2;

import java.util.*;

import net.mindview.util.*;

import static net.mindview.util.Print.*;

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(List<String> a) {//包含每個List都可執行的操作
        a.add(1, "x"); // Add at location 1
        a.add("x"); // Add at end
        // Add a collection:
        a.addAll(Countries.names(25));
        // Add a collection starting at location 3:
        a.addAll(3, Countries.names(25));
        b = a.contains("1"); // Is it in there?
        // Is the entire collection in there?
        b = a.containsAll(Countries.names(25));
        // Lists allow random access, which is cheap
        // for ArrayList, expensive for LinkedList:
        s = a.get(1); // Get (typed) object at location 1
        i = a.indexOf("1"); // Tell index of object
        b = a.isEmpty(); // Any elements inside?
        it = a.iterator(); // Ordinary Iterator
        lit = a.listIterator(); // ListIterator
        lit = a.listIterator(3); // Start at loc 3
        i = a.lastIndexOf("1"); // Last match
        a.remove(1); // Remove location 1
        a.remove("3"); // Remove this object
        a.set(1, "y"); // Set location 1 to "y"
        // Keep everything that's in the argument
        // (the intersection of the two sets):
        a.retainAll(Countries.names(25));
        // Remove everything that's in the argument:
        a.removeAll(Countries.names(25));
        i = a.size(); // How big is it?
        a.clear(); // Remove all elements
    }

    public static void iterMotion(List<String> a) {//使用Iterator遍歷元素
        ListIterator<String> it = a.listIterator();
        b = it.hasNext();
        b = it.hasPrevious();
        s = it.next();
        i = it.nextIndex();
        s = it.previous();
        i = it.previousIndex();
    }

    public static void iterManipulation(List<String> a) {//使用Iterator修改元素
        ListIterator<String> it = a.listIterator();
        it.add("47");
        // Must move to an element after add():
        it.next();
        // Remove the element after the newly produced one:
        it.remove();
        // Must move to an element after remove():
        it.next();
        // Change the element after the deleted one:
        it.set("47");
    }

    public static void testVisual(List<String> a) {//查看List的操作效果
        print(a);
        List<String> b = Countries.names(25);
        print("b = " + b);
        a.addAll(b);
        a.addAll(b);
        print(a);
        // Insert, remove, and replace elements
        // using a ListIterator:
        ListIterator<String> x = a.listIterator(a.size() / 2);
        x.add("one");
        print(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");
    }

    // There are some things that only LinkedLists can do:
    public static void testLinkedList() {//LinkedList專用的操作
        LinkedList<String> ll = new LinkedList<String>();
        ll.addAll(Countries.names(25));
        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);
    }

    public static void main(String[] args) {
        // Make and fill a new list each time:
        basicTest(
                new LinkedList<String>(Countries.names(25)));
        basicTest(
                new ArrayList<String>(Countries.names(25)));
        iterMotion(
                new LinkedList<String>(Countries.names(25)));
        iterMotion(
                new ArrayList<String>(Countries.names(25)));
        iterManipulation(
                new LinkedList<String>(Countries.names(25)));
        iterManipulation(
                new ArrayList<String>(Countries.names(25)));
        testVisual(
                new LinkedList<String>(Countries.names(25)));
        testLinkedList();
    }
} /* (Execute to see output) *///:~
6.Set和存盤順序
  1. 不同的Set實作不僅具有不同的行為,而且對于可以在特定的Set中放置的元素的型別也有不同的要求

  2. 型別
  • (1) Set(interface):存入Set的每個元素都必須是唯一的,因為Set不保存重復元素,加入Set的元素必須定義equals()方法以確保物件的唯一性,
  • (2) HashSet:為快速查找而設計的Set,存入HashSet的元素必須定義hashCode()
  • (3) TreeSet:保持次序的Set,底層為樹結構,使用它可以從set中提取有序的序列,元素必須實作Comparable介面
  • (4) LinkedHashSet:具有HashSet的查詢速度,且內部使用鏈表維護元素的順序,元素也必須實作hashCode()方法
  1. 從輸出看出
  • (1) HashSet以某種神秘的順序保存所有的元素
  • (2) LinkedHashSet按照元素插入的順序保存元素
  • (3) 而TreeSet按照排序順序維護元素(按照CompareTo()的實作方式)
  1. SortedSet
  • (1) SortedSet中的元素可以保證處于排序狀態
  • (2) SortSet:按物件的比較函式對元素排列
  • (3) Comparator comparator() 回傳當前Set使用的Comparator;或者回傳null,表示以自然方式排序
  • (4) Object first() 回傳容器中第一個元素
  • (5) Object last()回傳容器最后一個元素
  • (6) SortedSet subSet(from,to)生成此Set子集,范圍從from(包含)到 to(不包含)
  • (7) SortSet headSet(to) 生成此Set子集,由 小于 to的元素組成
  • (8) SortSet tailSet(to) 生成此Set子集,由 大于或等于 from的元素組成
7.佇列
  1. Queue在Java SE5中僅有的兩個實作是LInkedList和PriorityQueue(優先佇列,不保證輸出順序),他們的差異在于排序行為而不是性能

  2. 優先級佇列
  • (1)
package Chapter17.Example3;

import java.util.*;

/**
 * @author:YiMing
 * @version:1.0
 */
class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
    static class ToDoItem implements Comparable<ToDoItem> {
        private char primary;
        private int secondary;
        private String item;

        public ToDoItem(String td, char pri, int sec) {
            primary = pri;
            secondary = sec;
            item = td;
        }

        /**
         * compareable介面里面就是這么申明的,比較兩個數,
         * 1.大于0就是說明是大于的關系,
         * 2.小于0就說明是小于的關系,
         * 3.等于0就說明是相等的關系
         * @param arg
         * @return
         */
        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 td, char pri, int sec) {
        super.add(new ToDoItem(td, pri, sec));
    }

    public static void main(String[] args) {
        ToDoList toDoList = new ToDoList();
        toDoList.add("Empty trash", 'C', 4);
        toDoList.add("Feed dog", 'A', 2);
        toDoList.add("Feed bird", 'B', 7);
        toDoList.add("Mow lawn", 'C', 3);
        toDoList.add("Water lawn", 'A', 1);
        toDoList.add("Feed cat", 'B', 1);
        while (!toDoList.isEmpty()) System.out.println(toDoList.remove());
    }
} /* Output:
A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash
*///:~

  • (2) 優先佇列通過實作Comparable進行控制
  1. 雙向佇列
  • (1)
package Chapter17.Example4;

import java.util.LinkedList;

public class Deque<T> {
    private LinkedList<T> deque = new LinkedList();

    public Deque() {
    }

    public void addFirst(T e) {
        this.deque.addFirst(e);
    }

    public void addLast(T e) {
        this.deque.addLast(e);
    }

    public T getFirst() {
        return this.deque.getFirst();
    }

    public T getLast() {
        return this.deque.getLast();
    }

    public T removeFirst() {
        return this.deque.removeFirst();
    }

    public T removeLast() {
        return this.deque.removeLast();
    }

    public int size() {
        return this.deque.size();
    }

    public String toString() {
        return this.deque.toString();
    }
}

package Chapter17.Example4;

import static net.mindview.util.Print.*;

public class DequeTest {
    static void fillTest(Deque<Integer> deque) {
        for (int i = 20; i < 27; i++)
            deque.addFirst(i);
        for (int i = 50; i < 55; i++)
            deque.addLast(i);
    }

    public static void main(String[] args) {
        Deque<Integer> di = new Deque<Integer>();
        fillTest(di);
        print(di);
        while (di.size() != 0)
            printnb(di.removeFirst() + " ");
        print();
        fillTest(di);
        while (di.size() != 0)
            printnb(di.removeLast() + " ");
    }
} /* Output:
[26, 25, 24, 23, 22, 21, 20, 50, 51, 52, 53, 54]
26 25 24 23 22 21 20 50 51 52 53 54
54 53 52 51 50 20 21 22 23 24 25 26
*///:~


  • (2) 雙向佇列可以在任何一端添加或移除元素
  • (3) 可以使用組合來創建一個Deque類,并直接從LinkedList中暴露相關的方法
8.理解Map
  1. 標準的Java類別庫中包含了Map的幾種基本實作,包括:
  • (1) HashMap
  • (2) TreeMap
  • (3) LinkedHashMap
  • (4) WeakHashMap
  • (5) ConcurrentHashMap
  • (6) IdentityHashMap
  1. 它們都有同樣的基本介面Map,但是行為特性各不相同,這表現在效率、鍵值對的保存及呈現次序、物件的保存周期、映射表如何在多執行緒程式中作業和判定“鍵”等價的策略等方面

  2. 實作Map介面
  • (1) HashMap
    • Map基于散串列的實作(取代了Hashtable),插入和查詢鍵值對的開銷是固定的,可以i通過構造器設定容量和負載因子,以調整容器的性能
  • (2) LinkedHashMap
    • 類似于HashMap,但是迭代遍歷它時,取得“鍵值對”的順序是其插入次序,或者是最近最少使用(LRU)次序,只比HashMap慢一點,而在迭代訪問時反而更快,因為它使用鏈表維護內部次序
  • (3) TreeMap
    • 基于紅黑樹實作,查看“鍵”或“鍵值對”時,他們會被排序(次序由Comparable或Comparator決定)TreeMap的特點在于,所得到的結果是經過排序的,TreeMap是唯一帶有subMap()方法的Map,他回傳一個子樹
  • (4) WeakHashMap
    • 弱鍵 映射,允許釋放映射所指向的物件;這是為解決某類特殊問題而設計的,如果映射之外沒有參考指向某個“鍵”,則此“鍵”可以被垃圾收集器回收
  • (5) ConcurrentHashMap
    • 一種執行緒安全的Map,它不涉及同步枷鎖,
  • (6) IdentityHashMap
    • 使用==代替equals()對“鍵”進行比較的散列映射
  1. 散列是映射中存盤元素時最常用的方式

  2. 鍵必須是唯一的,而值可以有重復

  3. SortedMap
  • (1) 使用SortedMap(TreeMap是其現階段的唯一實作),可以確保鍵處于排序狀態
  • (2) Comparator comparator():回傳當前Map使用的Comparator;或者回傳null,以表示自然排序
  • (3) T firstKey():回傳Map中的第一個鍵
  • (4) T lastKey():回傳Map中的最后一個鍵
  • (5) SortMap subMap(fromKet,toKey):生成此Map的子集,范圍從fromKey(包含)到toKey(不包含)的鍵確定
  • (6) SortMap headMap(toKey):生成此Map的子集,由鍵 小于toKey的所有鍵值對組成
  • (7) SortMap tailMap(fromKey):生成此Map的子集,由鍵 大于或等于 fromeKey的所有鍵值對組成
  1. LinkedHashMap
  • (1) 為了提高速度,LinkedHashMap散列化所有的元素,但是在遍歷鍵值對時,卻又以元素的插入順序回傳鍵值對
  • (2) 可以在構造器中設定LinkedHashMap,使之采用基于訪問最近最少使用演算法,于是沒有被訪問過的元素就會出現在佇列的前面
  • (3)
package Chapter17.Example5;

import java.util.*;

import net.mindview.util.*;

import static net.mindview.util.Print.*;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedMap =
                new LinkedHashMap<Integer, String>(
                        new CountingMapData(9));
        print(linkedMap);
        // Least-recently-used order:
        linkedMap =
                new LinkedHashMap<Integer, String>(16, 0.75f, true);
        linkedMap.putAll(new CountingMapData(9));
        print(linkedMap);
        for (int i = 0; i < 6; i++) // Cause accesses:
            linkedMap.get(i);//訪問前五個后,就被放在最后面了,最后的6,7,8被提前了
        print(linkedMap);
        linkedMap.get(0);//訪問過“0“后也被放到了最后
        print(linkedMap);
    }
} /* Output:
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0}
{6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}
*///:~

9.散列與散列碼
  1. hashMap使用equals()判斷當前的鍵是否與表中存在的鍵相同

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

  • (1) 自反性,對任意x,x.equals(x)一定回傳true,
  • (2) 對稱性,對任意x和y,如果y.equals(x)回傳true,則x.equals(y)也回傳true,
  • (3) 傳遞性,對任意x,y,z,如果有x.equals(y)回傳true,y.equals(z)回傳true,則x.equals(z)一定回傳true,
  • (4) 一致性,對任意x和y,如果物件中用于等價比較資訊沒有改變,那么無論呼叫x.equals(y)多少次,回傳結果保持一致,
  • (5) 對任何不是null的x,x.equals(null)一定回傳false,
  1. 如果要使用自己的類作為HashMap的鍵,必須同時多載hashCode()和equals()
  2. 理解hashCode()
  • 使用散列的目的在于:想要使用一個物件來查找另一個物件
  1. 為速度而散列
  • (1)
package Chapter17.Example6;

import Chapter17.Test15.MapEntry;
import net.mindview.util.Countries;

import java.util.*;

public class SimpleHashMap<K,V> extends AbstractMap<K,V> {
  // Choose a prime number for the hash table
  // size, to achieve a uniform distribution:
  static final int SIZE = 997;
  // You can't have a physical array of generics,
  // but you can upcast to one:
  @SuppressWarnings("unchecked")
  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)
      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 new
        found = 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<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(25));
    System.out.println(m);
    System.out.println(m.get("ERITREA"));
    System.out.println(m.entrySet());
  }
}

  • (2) 散列的價值在于速度:散列使得查詢得以快速進行
  • (3) 陣列并不保存鍵本身,而是通過鍵物件生成一個數字,將其作為陣列的下標,這個數字就是散列碼,由定義在Object中的、且可能由你的類覆寫的hashCode()方法生成
  • (4) HashMap快的原因:不是查詢整個list,而是快速地跳到陣列的某個位置,只對很少的元素進行比較
  • (5) 由于散串列中的“槽位”(slot)通常稱為桶位(bucket),因此我們將表示實際散串列的陣列命名為bucket,為使散列分布均勻,同的數量通常使用質數
  • (6) 為了能夠自動處理沖突,使用一個LinkedList的陣列;每一個新的元素只是直接添加到list末尾的某個特定桶中
  1. 覆寫hashCode()
  • (1) 你無法控制bucket陣列的下標值的產生,這個值依賴于具體的HashMap物件的容量,而容量的改變與容器的充滿程度和負載因子有關

  • (2) 設計hashCode()是最重要的因素是:無論何時,對同一個物件呼叫hasCode()都應該生成個同樣的值

  • (3) 如果你的hashCode()方法依賴于物件中易變的資料,因此資料發生變化時,hashCode()就會生成一個不同的散列碼,相當于產生了一個不同的鍵

  • (4) 不應該使hashCode()依賴于具有唯一型的物件資訊,尤其是使用this值,這只能產生很糟糕的hashCode(),應為這樣無法生成一個新的鍵,使之與put()中源氏鍵值相同

  • (5) String有個特點:如果程式中有多個String物件,都包含相同的字串序列,那么這些String物件都映射到同一塊記憶體區域

  • (6) 怎樣寫出一份像樣的hashCode基本指導

    • i.

      package Chapter17.Example7;
      
      import java.util.*;
      import static net.mindview.util.Print.*;
      
      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 "String: " + s + " id: " + id +
            " hashCode(): " + hashCode();
        }
        public int hashCode() {
          // The very simple approach:
          // return s.hashCode() * id;
          // Using Joshua Bloch's recipe:
          int result = 17;
          result = 37 * result + s.hashCode();
          result = 37 * result + id;
          return result;
        }
        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);
          for(CountedString cstring : cs) {
            print("Looking up " + cstring);
            print(map.get(cstring));
          }
        }
      } /* Output: (Sample)
      {String: hi id: 4 hashCode(): 146450=3, String: hi id: 1 hashCode(): 146447=0, String: hi id: 3 hashCode(): 146449=2, String: hi id: 5 hashCode(): 146451=4, String: hi id: 2 hashCode(): 146448=1}
      Looking up String: hi id: 1 hashCode(): 146447
      0
      Looking up String: hi id: 2 hashCode(): 146448
      1
      Looking up String: hi id: 3 hashCode(): 146449
      2
      Looking up String: hi id: 4 hashCode(): 146450
      3
      Looking up String: hi id: 5 hashCode(): 146451
      4
      *///:~
      
      
    • ii.給int變數result賦予某個非零值常量,例如17

    • iii.為物件內每個有意義的域f計算一個int散列碼c:

      • boolean
        • c=(f?0:1)
      • byte,char,short或int
        • c=(int)f
      • long
        • c=(int)(f^(f>>>32))
      • float
        • c=Float.float.ToIntBits(f)
      • double
        • long l=Doule.doubleToLongBits(f);c=(int)(l^(l>>>32))
      • object,其equals()呼叫這個域的equals()
        • c=f.hashCode()
      • 陣列
        • 對每個元素應用上述規則
    • iv.回傳result

    • v.檢查hashCode()最后生成結果,確保相同的物件有相同的散列碼,

  • (7) compareTo()方法有一個比較結構,因此它會產生一個排序序列,排序規則首先按照實際型別排序,然后如果有名字按名字排序,最后按創建順序排序:

      package Chapter17.Example7;
      
      import typeinfo.pets.Individual;
      import typeinfo.pets.Pet;
      
      import java.util.*;
      
      public class IndividualTest {
        public static void main(String[] args) {
          Set<Individual> pets = new TreeSet<Individual>();
          for(List<? extends Pet> lp :
              MapOfList.petPeople.values())
            for(Pet p : lp)
              pets.add(p);
          System.out.println(pets);
        }
      } /* Output:
      [Cat Elsie May, Cat Pinkola, Cat Shackleton, Cat Stanford aka Stinky el Negro, Cymric Molly, Dog Margrett, Mutt Spot, Pug Louie aka Louis Snorkelstein Dupree, Rat Fizzy, Rat Freckly, Rat Fuzzy]
      *///:~
    
    
10.選擇介面的不同實作
  1. 盡管實際上只有4種容器:Map,List,Set,Queue,但是每種介面都有不止一個實作版本

  2. 容器之間的區別:所使用的介面是由什么樣的資料結構實作的

  3. 例如List介面
  • (1) ArrayList(底層由陣列支持)
  • (2) LinkedList(底層由雙向鏈表實作)
  1. Set介面
  • (1) HashSet(查詢速度最快)
  • (2) LinkedHashSet(保持元素插入順序)
  • (3) TreeSet(基于TreeMap,生成一個總是處于排序狀態的Set)
  1. 性能測驗框架
package Chapter17.Example8;
// Framework for performing timed tests of containers.

public abstract class Test<C> {
  String name;
  public Test(String name) { this.name = name; }
  // Override this method for different tests.
  // Returns actual number of repetitions of test.
  abstract int test(C container, TestParam tp);
} ///:~

package Chapter17.Example8;
// Applies Test objects to lists of different containers.

import java.util.*;

public class Tester<C> {
  public static int fieldWidth = 8;
  public static TestParam[] defaultParams= TestParam.array(
    10, 5000, 100, 5000, 1000, 5000, 10000, 500);
  // Override this to modify pre-test initialization:
  protected C initialize(int size) { return container; }
  protected C container;
  private String headline = "";
  private List<Test<C>> tests;
  private static String stringField() {
    return "%" + fieldWidth + "s";
  }
  private static String numberField() {
    return "%" + fieldWidth + "d";
  }
  private static int sizeWidth = 5;
  private static String sizeField = "%" + sizeWidth + "s";
  private TestParam[] paramList = defaultParams;
  public Tester(C container, List<Test<C>> tests) {
    this.container = container;
    this.tests = tests;
    if(container != null)
      headline = container.getClass().getSimpleName();
  }
  public Tester(C container, List<Test<C>> tests,
      TestParam[] paramList) {
    this(container, tests);
    this.paramList = paramList;
  }
  public void setHeadline(String newHeadline) {
    headline = newHeadline;
  }
  // Generic methods for convenience :
  public static <C> void run(C cntnr, List<Test<C>> tests){
    new Tester<C>(cntnr, tests).timedTest();
  }
  public static <C> void run(C cntnr,
      List<Test<C>> tests, TestParam[] paramList) {
    new Tester<C>(cntnr, tests, paramList).timedTest();
  }
  private void displayHeader() {
    // Calculate width and pad with '-':
    int width = fieldWidth * tests.size() + sizeWidth;
    int dashLength = width - headline.length() - 1;
    StringBuilder head = new StringBuilder(width);
    for(int i = 0; i < dashLength/2; i++)
      head.append('-');
    head.append(' ');
    head.append(headline);
    head.append(' ');
    for(int i = 0; i < dashLength/2; i++)
      head.append('-');
    System.out.println(head);
    // Print column headers:
    System.out.format(sizeField, "size");
    for(Test test : tests)
      System.out.format(stringField(), test.name);
    System.out.println();
  }
  // Run the tests for this container:
  public void timedTest() {
    displayHeader();
    for(TestParam param : paramList) {
      System.out.format(sizeField, param.size);
      for(Test<C> test : tests) {
        C kontainer = initialize(param.size);
        long start = System.nanoTime();
        // Call the overriden method:
        int reps = test.test(kontainer, param);
        long duration = System.nanoTime() - start;
        long timePerRep = duration / reps; // Nanoseconds
        System.out.format(numberField(), timePerRep);
      }
      System.out.println();
    }
  }
} ///:~

package Chapter17.Example8;
// A "data transfer object."

public class TestParam {
  public final int size;
  public final int loops;
  public TestParam(int size, int loops) {
    this.size = size;
    this.loops = loops;
  }
  // Create an array of TestParam from a varargs sequence:
  public static TestParam[] array(int... values) {
    int size = values.length/2;
    TestParam[] result = new TestParam[size];
    int n = 0;
    for(int i = 0; i < size; i++)
      result[i] = new TestParam(values[n++], values[n++]);
    return result;
  }
  // Convert a String array to a TestParam array:
  public static TestParam[] array(String[] values) {
    int[] vals = new int[values.length];
    for(int i = 0; i < vals.length; i++)
      vals[i] = Integer.decode(values[i]);
    return array(vals);
  }
} ///:~


  1. 對List的選擇
package Chapter17.Example9;
// Demonstrates performance differences in Lists.
// {Args: 100 500} Small to keep build testing short
import java.util.*;
import net.mindview.util.*;

public class ListPerformance {
  static Random rand = new Random();
  static int reps = 1000;
  static List<Test<List<Integer>>> tests =
    new ArrayList<Test<List<Integer>>>();
  static List<Test<LinkedList<Integer>>> qTests =
    new ArrayList<Test<LinkedList<Integer>>>();
  static {
    tests.add(new Test<List<Integer>>("add") {
      int test(List<Integer> list, TestParam tp) {
        int loops = tp.loops;
        int listSize = tp.size;
        for(int i = 0; i < loops; i++) {
          list.clear();
          for(int j = 0; j < listSize; j++)
            list.add(j);
        }
        return loops * listSize;
      }
    });
    tests.add(new Test<List<Integer>>("get") {
      int test(List<Integer> list, TestParam tp) {
        int loops = tp.loops * reps;
        int listSize = list.size();
        for(int i = 0; i < loops; i++)
          list.get(rand.nextInt(listSize));
        return loops;
      }
    });
    tests.add(new Test<List<Integer>>("set") {
      int test(List<Integer> list, TestParam tp) {
        int loops = tp.loops * reps;
        int listSize = list.size();
        for(int i = 0; i < loops; i++)
          list.set(rand.nextInt(listSize), 47);
        return loops;
      }
    });
    tests.add(new Test<List<Integer>>("iteradd") {
      int test(List<Integer> list, TestParam tp) {
        final int LOOPS = 1000000;
        int half = list.size() / 2;
        ListIterator<Integer> it = list.listIterator(half);
        for(int i = 0; i < LOOPS; i++)
          it.add(47);
        return LOOPS;
      }
    });
    tests.add(new Test<List<Integer>>("insert") {
      int test(List<Integer> list, TestParam tp) {
        int loops = tp.loops;
        for(int i = 0; i < loops; i++)
          list.add(5, 47); // Minimize random-access cost
        return loops;
      }
    });
    tests.add(new Test<List<Integer>>("remove") {
      int test(List<Integer> list, TestParam tp) {
        int loops = tp.loops;
        int size = tp.size;
        for(int i = 0; i < loops; i++) {
          list.clear();
          list.addAll(new CountingIntegerList(size));
          while(list.size() > 5)
            list.remove(5); // Minimize random-access cost
        }
        return loops * size;
      }
    });
    // Tests for queue behavior:
    qTests.add(new Test<LinkedList<Integer>>("addFirst") {
      int test(LinkedList<Integer> list, TestParam tp) {
        int loops = tp.loops;
        int size = tp.size;
        for(int i = 0; i < loops; i++) {
          list.clear();
          for(int j = 0; j < size; j++)
            list.addFirst(47);
        }
        return loops * size;
      }
    });
    qTests.add(new Test<LinkedList<Integer>>("addLast") {
      int test(LinkedList<Integer> list, TestParam tp) {
        int loops = tp.loops;
        int size = tp.size;
        for(int i = 0; i < loops; i++) {
          list.clear();
          for(int j = 0; j < size; j++)
            list.addLast(47);
        }
        return loops * size;
      }
    });
    qTests.add(
      new Test<LinkedList<Integer>>("rmFirst") {
        int test(LinkedList<Integer> list, TestParam tp) {
          int loops = tp.loops;
          int size = tp.size;
          for(int i = 0; i < loops; i++) {
            list.clear();
            list.addAll(new CountingIntegerList(size));
            while(list.size() > 0)
              list.removeFirst();
          }
          return loops * size;
        }
      });
    qTests.add(new Test<LinkedList<Integer>>("rmLast") {
      int test(LinkedList<Integer> list, TestParam tp) {
        int loops = tp.loops;
        int size = tp.size;
        for(int i = 0; i < loops; i++) {
          list.clear();
          list.addAll(new CountingIntegerList(size));
          while(list.size() > 0)
            list.removeLast();
        }
        return loops * size;
      }
    });
  }
  static class ListTester extends Tester<List<Integer>> {
    public ListTester(List<Integer> container,
        List<Test<List<Integer>>> tests) {
      super(container, tests);
    }
    // Fill to the appropriate size before each test:
    @Override protected List<Integer> initialize(int size){
      container.clear();
      container.addAll(new CountingIntegerList(size));
      return container;
    }
    // Convenience method:
    public static void run(List<Integer> list,
        List<Test<List<Integer>>> tests) {
      new ListTester(list, tests).timedTest();
    }
  }
  public static void main(String[] args) {
    if(args.length > 0)
      Tester.defaultParams = TestParam.array(args);
    // Can only do these two tests on an array:
    Tester<List<Integer>> arrayTest =
      new Tester<List<Integer>>(null, tests.subList(1, 3)){
        // This will be called before each test. It
        // produces a non-resizeable array-backed list:
        @Override protected
        List<Integer> initialize(int size) {
          Integer[] ia = Generated.array(Integer.class,
            new CountingGenerator.Integer(), size);
          return Arrays.asList(ia);
        }
      };
    arrayTest.setHeadline("Array as List");
    arrayTest.timedTest();
    Tester.defaultParams= TestParam.array(
      10, 5000, 100, 5000, 1000, 1000, 10000, 200);
    if(args.length > 0)
      Tester.defaultParams = TestParam.array(args);
    ListTester.run(new ArrayList<Integer>(), tests);
    ListTester.run(new LinkedList<Integer>(), tests);
    ListTester.run(new Vector<Integer>(), tests);
    Tester.fieldWidth = 12;
    Tester<LinkedList<Integer>> qTest =
      new Tester<LinkedList<Integer>>(
        new LinkedList<Integer>(), qTests);
    qTest.setHeadline("Queue tests");
    qTest.timedTest();
  }
} /* Output: (Sample)
--- Array as List ---
 size     get     set
   10     130     183
  100     130     164
 1000     129     165
10000     129     165
--------------------- ArrayList ---------------------
 size     add     get     set iteradd  insert  remove
   10     121     139     191     435    3952     446
  100      72     141     191     247    3934     296
 1000      98     141     194     839    2202     923
10000     122     144     190    6880   14042    7333
--------------------- LinkedList ---------------------
 size     add     get     set iteradd  insert  remove
   10     182     164     198     658     366     262
  100     106     202     230     457     108     201
 1000     133    1289    1353     430     136     239
10000     172   13648   13187     435     255     239
----------------------- Vector -----------------------
 size     add     get     set iteradd  insert  remove
   10     129     145     187     290    3635     253
  100      72     144     190     263    3691     292
 1000      99     145     193     846    2162     927
10000     108     145     186    6871   14730    7135
-------------------- Queue tests --------------------
 size    addFirst     addLast     rmFirst      rmLast
   10         199         163         251         253
  100          98          92         180         179
 1000          99          93         216         212
10000         111         109         262         384
*///:~

注:由于篇幅問題,代碼中的Test、Tester、TestParam類見:Chapter17\Example9

  • (1) 對于背后是陣列支撐的List和ArrayList,無論串列大小如何,這些訪問都快速一致,而對于LinkedList訪問時間對于較大的串列明顯增加,如果需要執行大量隨機訪問,鏈接鏈表不會是一種好的選擇
  • (2) 使用迭代器在串列中間插入新的元素,對于ArrayList,當串列變大時,其開銷將變得高昂,但是對于LinkedList,相對來說比較低廉,并且不隨串列尺寸而發生變化,這是因為ArrayList在插入時,必須創建空間并將它的所有參考向前移動,這會隨ArrayList的尺寸增加而產生高昂的代價,LinkedList自需要鏈接新的元素,而不必修改串列中剩余的元素,一次你可以認為串列尺寸如果變化,其代價大致相同
  • (3) LinkedList對List端點會進行特殊處理——這使得在將LinkedList用作Queue時,速度可以得到提高
  • (4) 在LinkedList中的插入和移除代價相當低廉,并且不隨串列尺寸發生變化,但是對于ArrayList,插入操作代價很高昂,并且不隨串列尺寸的增加而增加
  • (5) 從Queue測驗中,可以看到LinkedList可以多么快速的從串列的端點插入和移除元素,這正是對Queue行為所做的優化
  • (6) 將ArrayList作為默認首選,只有你需要使用額外的功能,或者當程式的性能因為經常從表中間進行插入和洗掉而變差的時候,才去選擇LinkedList,如果使用的是固定數量的元素,那么既可以使用背后有支撐的List,也可以選擇真正的陣列
  • (7) CopyOnWriteArrayList是List的一個特殊實作,專門用于并發編程
  1. 微基準測驗的危險
  • Math.random()的輸出中的,其范圍是[0,1)
  1. 對Set的選擇
  • (1) HashSet的性能基本上總是比TreeSet的好,特別是在添加和查詢元素時,而這兩個操作是最重要的操作,TreeSet存在的唯一原因是它可以維持元素的排序狀態
  • (2) 對于插入操作,LinkedHashSet比HashSet的代價更高,這是由維護鏈表所帶來額外開銷造成的
  1. 對Map的選擇
  • (1) 所有的Map實作的插入操作都會隨著Map尺寸的變大明顯變慢,但是查找的代價比插入小很多,
  • (2) Hashtable的性能大體和HashMap相當,因為HashMap是用來替代Hashtable的,因此它們使用了相同的底層存盤和查找機制
  • (3) TreeMap
    • i.TreeMap是一種創建有序串列的方式,樹的行為是:總是保證有序,并且不必進行特殊的排序,一旦你填充了一個TreeMap,就可以呼叫keySet()方法來獲取鍵的Set視圖,然后呼叫toArray()來產生這些鍵構成的陣列,
    • ii.可以使用靜態方法Arrays.binarySearch()在排序陣列中快速查找物件
    • iii.只有你要求Map始終保持有序時,才需要使用TreeMap
  • (4) LingkedHashMap在插入時比HashMap慢一點,因為它維護散列資料結構的同時還要維護鏈表,就是因為這個串列,使得其迭代速度更快
  • (5) IdentityHashMap則具有完全不同的性能,因為它使用==而不是equals()來比較元素
  • (6) HashMap的性能因子,可以通過手工調整HashMap來提高性能,從而滿足我們特定應用的需求:
    • i.容量:表中的桶位數
    • ii.初始容量:表在創建時所擁有的桶位數
    • iii.尺寸:表中當前存盤的項數
    • iv.負載因子:尺寸/容量,負載輕的表產生的可能性小,因此對于插入和查找都是最理想的
  • (7) HashMap使用的默認負載因子時0.75,這個因子在時間和空間代價之間達到了平衡,更高的負載因子可以降低表所需空間,但是會增加查找代價
11.實用方法
  1. 在這里插入圖片描述

  2. List的排序和查詢
  • (1) List排序和查詢所使用的方法與物件陣列所使用的相應方法有相同的名字與語法,只是用Collections的static方法代替Arrays的方法而已
  • (2) 如果使用Comparator進行排序,那么binarySearch()必須使用相同的Comparator
  1. 設定Collection或Map為不可修改
  • (1) Collections.unmodifiableCollection()用于創建“不可修改”的容器(回傳值是容器的只讀版本),任何修改容器內的操作(例如add()、put())都會引起UnsupportedOperationException例外
  • (2) 在將容器設為只讀之前,必須填入有意義的資料,裝在資料后就不可修改了
  1. Collection或Map的同步控制
  • (1) 直接將新生成的容器傳遞給適當的同步方法
    • Collections.synchronizedCollection(
      new ArrayList());
    • Collections.synchronizedList(
      new ArrayList());
    • Collections.synchronizedSet(
      new HashSet());
    • Collections.synchronizedSortedSet(
      new TreeSet());
    • Collections.synchronizedMap(
      new HashMap<String,String>())
    • Collections.synchronizedSortedMap(
      new TreeMap<String,String>())
  • (2) 快速報錯
    • Java容器有一種保護機制,能夠防止多個行程同時修改同一個容器的內容
    • Java容器類類別庫采用快速報錯機制,它會探查容器上的任何除了你行程所進行的操作以外的所有變化,一旦發現其他行程修改了容器,就會拋出ConcurrentModificationException例外
    • ConcurrentHashMap、CopyOnWriteArrayList和CopyOnWriteArrayList都使用了可以避免ConcurrentModificationException例外
12.持有參考
  1. java.lang.ref類別庫包含了一組類,這些類為垃圾回收提供了更大的靈活性
  2. 有三個繼承自抽象類Reference的類:SoftReference、WeakReference和PhantomReference,當垃圾回收器正在考察的物件只能通過某個Reference物件才“可獲得”時,上述這些不同的派生類為垃圾回收器提供了不同級別的間接性指示
  3. 使用Renference物件,可以繼續使用該物件,而在記憶體消耗殆盡的時候由允許釋放該物件
  4. Soft reference:用以實作記憶體敏感的高速快取
  5. Weak reference:是為實作“規范映射”而設計的,它不妨礙垃圾回收映射的“鍵”(或“值”)
  6. Phantom reference:用以呼叫回收前的清理作業,它比Java終止機制更靈活
  7. WeakHashMap:容器類中有一種特殊的Map,即WeakHashMap,它被用來保存WeakReference
13.Java 1.0/1.1 的容器
  1. Vector和Enumeration
  • Vector是唯一可以自我擴展的序列
  • Enumeration只是介面而不是實作
  • boolean hasMoreElement():如果此列舉包含更多元素,該方法就回傳true
  • Object nextElement():該方法回傳此列舉中的下一個元素(如果該有下一個元素),否則拋出例外
  • Collection.emumeration():從Collection生成一個Enumeration
  1. Hashtable
  • 在新程式中,應使用HashMap
  1. Stack
  • Stack繼承了Vector,它擁有Vector所有的特點和行為,在加上一些額外的Stack行為
  1. BitSet
  • 如果想要高效率地存盤大量“開/關”資訊,BitSet是很好的選擇,不過它的效率僅是對空間而言;如果需要高效的訪問時間,BitSet比本地陣列稍微慢一點

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/169043.html

標籤:AI

上一篇:完美解決ArcGIS10.x柵格空間插值報錯無法進行和匯出插值柵格結果出錯的問題

下一篇:學習vue-router路由守衛,這一篇就夠了

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more