集合學習

列表相等只需關心元素數據

下邊是一個判斷列表相等的例子,代碼以下:java

1 public static void main(String[] args){
2         ArrayList<String> arr1 = new ArrayList<>();
3         arr1.add("A");
4 
5         Vector<String> arr2 = new Vector<>();
6         arr2.add("A");
7 
8         System.out.print("" + arr1.equals(arr2));
9 }

運行結果爲:true編程

緣由分析:兩者都是列表(List),實現了List接口,也都繼承了AbstractList抽象類,其equals方法是在AbstractList中定義的,其源代碼以下:數組

 1     public boolean equals(Object o) {
 2         if (o == this)
 3             return true;
 4         if (!(o instanceof List))
 5             return false;
 6 
 7         ListIterator<E> e1 = listIterator();
 8         ListIterator<?> e2 = ((List<?>) o).listIterator();
 9         while (e1.hasNext() && e2.hasNext()) {
10             E o1 = e1.next();
11             Object o2 = e2.next();
12             if (!(o1==null ? o2==null : o1.equals(o2)))
13                 return false;
14         }
15         return !(e1.hasNext() || e2.hasNext());
16     }

能夠看到,這裏只要求實現List接口。只要List中全部的元素且位置相同,就代表兩個List是相等的。安全

其餘的集合類型,如Set、Map等與此相同,也是隻關心集合元素。併發

子列表只是原列表的一個視圖

List提供了subList方法,用於返回一個列表的子列表。dom

 1     public static void main(String[] args){
 2         List<String> c = new ArrayList<>();
 3         c.add("A");
 4         c.add("B");
 5 
 6         List<String> c1 = new ArrayList<>(c);
 7         List<String> c2 = c.subList(0, c.size());
 8         c2.add("C");
 9 
10         System.out.println("c == c1?" + c.equals(c1));
11         System.out.println("c == c2?" + c.equals(c2));
12     }

在上邊的例子中,c1是經過ArrayList的構造函數建立的,c2是經過subList方法建立的而後添加了一個元素。運行結果以下:函數

1 c == c1?false
2 c == c2?true

爲何會有上邊的結果呢?來看下subList的源碼:this

1     public List<E> subList(int fromIndex, int toIndex) {
2         return (this instanceof RandomAccess ?
3                 new RandomAccessSubList<>(this, fromIndex, toIndex) :
4                 new SubList<>(this, fromIndex, toIndex));
5     }

subList是由AbstractList實現的,它根據是否是能夠隨機存取來提供不一樣的SubList實現方式。因爲RandomAccessSubList也是SubList的子類,因此全部的操做都是由SubList類實現的。來看一下SubList類的代碼:加密

class SubList<E> extends AbstractList<E> {
    private final AbstractList<E> l;
    private final int offset;
    private int size;

    SubList(AbstractList<E> list, int fromIndex, int toIndex) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > list.size())
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
        l = list;
        offset = fromIndex;
        size = toIndex - fromIndex;
        this.modCount = l.modCount;
    }

    public E get(int index) {
        rangeCheck(index);
        checkForComodification();
        return l.get(index+offset);
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);
        checkForComodification();
        l.add(index+offset, element);
        this.modCount = l.modCount;
        size++;
    }

    /*其他代碼省略,能夠在AbstractList.java文件中自行查看*/  
}

不難發現,subList返回的SubList類並無生成一個數組或者鏈表,它自己只是原列表的一個視圖而已,所以全部的修改動做都反映在了原列表上。spa

使用subList處理足部列表

看一個簡單的需求,一個有100個元素,如今要刪除索引位置爲20~30的元素。能夠經過以下的代碼實現:

1     public static void main(String[] args) {
2         List<Integer> initData = Collections.nCopies(100, 0);
3 
4         ArrayList<Integer> list = new ArrayList<>(initData);
5 
6         list.subList(20, 30).clear();
7     }

可是,這個時候就不要再對原列表進行操做了。來看下邊的代碼:

 1     public static void main(String[] args) {
 2         List<Integer> initData = Collections.nCopies(100, 0);
 3 
 4         ArrayList<Integer> list = new ArrayList<>(initData);
 5         List<Integer> subList = list.subList(20, 30);
 6         list.add(0);
 7 
 8         System.out.print("原列表長度:" + list.size());
 9         System.out.print("子列表長度:" + subList.size());
10     }

  程序運行時,size方法報ConcurrentModificationException。由於subList取出的磊表示原列表的一個視圖,原數據集修改了,可是subList取出的子列表不會從新生成,再後面對子列表進行操做時,就會檢測到修改計數器與預期的不相同,因而拋出併發修改異常。問題最終仍是在子列表提供的size方法的檢查上:

    public int size() {
        checkForComodification();
        return size;
    }

    private void checkForComodification() {
        if (this.modCount != l.modCount)
            throw new ConcurrentModificationException();
    }

  modCount是在SubList子列表的構造函數中賦值的,其值等於生成子列表時原列表的修改次數。在生成子列表後再修改原列表,1.modCount必然比modCount大1,再也不相等,因而拋出併發修改異常。

  SubList的其餘方法也會檢查修改計數器。對於子列表操做,子列表的modCount老是跟隨原列表進行更新。

  能夠在生成子列表後,經過 Collections.unmodifiableList 設置原列表爲只讀狀態,在後續的操做中,對原列表只進行讀操做,對子列表進行讀寫操做。防護式編程就是教咱們如此作的。

1     public static void main(String[] args) {
2         List<Integer> initData = Collections.nCopies(100, 0);
3 
4         List<Integer> list = new ArrayList<>(initData);
5         List<Integer> subList = list.subList(20, 30);
6         //設置list爲只讀狀態
7         list = Collections.unmodifiableList(list);
8     }

集合運算

並集

 1     public static void main(String[] args) {
 2         
 3         List<String> list1 = new ArrayList<>();
 4         list1.add("A");
 5         list1.add("B");
 6 
 7         List<String> list2 = new ArrayList<>();
 8         list2.add("A");
 9         list2.add("B");
10         
11         list1.addAll(list2);
12     }

交集

1         list1.retainAll(list2);

  注意:retainAll方法會刪除list1中沒有在list2中出現的元素

差集

  全部屬於A但不屬於B的元素組成的集合,叫作A與B的差集。

無重複的並集

1         //去除重複元素
2         list2.removeAll(list1);
3         //取並集
4         list1.addAll(list2);

使用shuffle打亂列表

    Collections.shuffle(list1);

  shuffle方法能夠用在一下方面:

  • 程序的「假裝」上

    如:遊戲中打怪,修行,寶物分配的分配策略

  • 抽獎程序中
  • 安全傳輸方面

    發送端發送一組數據,先隨機打亂順序,加密發送;接收端解密後自行排序便可。

相關文章
相關標籤/搜索