API之Collection集合

package com.rayNotes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.TreeSet;

import org.junit.Test;
/**
 * 原文地址:http://blog.csdn.net/yerenyuan_pku/article/details/53915999
 * 集合入門綜述
 * @author ray
 *
 */

@SuppressWarnings({"unchecked","rawtypes"})
public class CollectionTest {
    /**
     * TODO 6、 set接口
     *      - Set集合不允許存儲重複元素,而且不保證元素是有序的
     *          (存入和取出的順序有可能一致[有序],也有可能不一致[無序])。 
     *      - Set集合的功能和Collection的是一致的,
     *          所以Set集合取出的方法只要一個,那就是迭代器。
                * - HashSet
                *       此類實現Set接口,由哈希表(實際上是一個HashMap實例)支持。
                *       它不保證set的迭代順序,特別是它不保證該順序恆久不變。
                *       此類允許使用null元素。
                *  總結:
                *   - 保證元素唯一性的方式依賴於hashCode()與equals()方法
                *   - HashSet集合不能保證元素的迭代順序與元素存儲順序相同
                *  
                * - LinkedHashSet
                *       具有可預知迭代順序的Set接口的哈希表和鏈接列表實現。
                *       此實現與HashSet的不同之外在於,後者維護着一個運行於所有條目的雙重鏈接列表。
                *  總結:
                *   - 一個特殊的Set集合,而且是有序的,底層是一個雙向鏈表
                *  
                * - TreeSet 
                *       - Set接口中常用的類
                *       - TreeSet是線程不同步的,可以對Set集合中的元素進行排序。
                *           底層數據結構是二叉樹(也叫紅黑樹),
                *           保證元素唯一性的依據是:compareTo()方法return 0。
                *   - TreeSet對集合中的元素進行排序的方式有兩種
                *       - TreeSet排序的第一種方式:
                *       !!! --讓元素自身具備比較性。
                *                   元素需要實現 **Comparable接口,覆蓋compareTo()方法**。
                *                   這種方式也稱爲元素的自然順序,或者也叫默認順序。
                *       - TreeSet排序的第二種方式: 《 比較器更爲靈活,自然排序通常都作爲元素的默認排序。》
                *                   當元素自身不具備比較性時,或者具備的比較性不是所需要的,
                *                   這時就需要讓集合自身具備比較性。在集合一初始化時,就具備比較方式.
                *               即:
                *                   定義一個比較器實現Comparator接口,覆蓋compare方法,
                *                   將Comparator接口的對象作爲參數傳遞給TreeSet集合的構造函數。
                * 
     * 
     *  - Hashset是如何保證元素的唯一性的呢?
     *          —— 是通過元素的兩個方法,hashCode()和equals()來完成的。
     *              即元素必須覆蓋hashCode()和equals()方法,
     *              覆蓋hashCode()方法是爲了根據元素自身的特點確定哈希值,
     *              覆蓋equals()方法是爲了解決哈希值的衝突  
     *      
     */
    /**
     * 
     */
    @Test
    public void treeSetTest02() {
//      - 自定義比較器
//      當元素自身不具備比較性時,或者具備的比較性不是所需要的,這時就需要讓容器自身具備比較性。
//      定義一個比較器,將比較器對象作爲參數傳遞給TreeSet集合的構造函數。
      Set set1 = new TreeSet(new ComparatorByName());
      set1.add(new Student("b", 6)); 
      set1.add(new Student("e", 2));
      set1.add(new Student("d", 1));
      set1.add(new Student("c", 4));
      set1.add(new Student("a", 5));
      for (Iterator it = set1.iterator(); it.hasNext();) {
          Student stu = (Student) it.next();
          System.out.println(stu.getName() + "::" + stu.getAge());
      }
    }
    @Test
    public void treeSetTest01() {
//      - 讓元素自身具備比較性。
        Set set = new TreeSet();
        set.add(new Student("b", 6)); 
        set.add(new Student("e", 2));
        set.add(new Student("d", 1));
        set.add(new Student("c", 4));
        set.add(new Student("a", 5));
        set.add(new Student("e", 2));// 比較出來發現是0,不存
        set.add(new Student("b", 1));
//      3,只能用迭代器取出
        for (Iterator it = set.iterator(); it.hasNext();) {
            Student stu = (Student) it.next();
            System.out.println(stu.getName() + "::" + stu.getAge());
        }

    }
    @Test
    public void linkedHashSetTest01() {
        Set set = new LinkedHashSet();

        set.add("abc");
        set.add("def");
        set.add("ray");
//      set.forEach(action);
//      set.retainAll(set);//只保留交集,將c1和c2不同的元素從c1中刪除,保留c1中和c2相同的元素

/**
*   將一個順序執行的流轉變成一個併發的流只要調用 parallel()方法
*/
//      set.parallelStream();
/**
 *  TODO 可拆分迭代器Spliterator
         *  它和Iterator一樣也是用於遍歷數據源中的元素,但是他是爲並行執行而設計的。 
         *  java8 所有數據結構都實現了 這個接口, 一般情況不需要自己寫實現代碼。
         *  但是瞭解它的實現方式會讓你對並行流的工作原理有更深的瞭解。
 */ 
//      set.spliterator();
        for(Iterator it= set.iterator();it.hasNext();) {
            System.out.println(it.next());
        }
    }
    @Test
    public void hashSetTest01() {
        /**
         * 練習,往HashSet中存儲學生對象(姓名,年齡)。同姓名,同年齡視爲同一個人,不存。 

          - 解:HashSet中存放自定義類型元素時,
            需要重寫對象中的hashCode和equals方法,建立自己的比較方式,
            才能保證HashSet集合中的對象唯一。
         */
        // 1,創建容器對象
        Set set = new HashSet();
         // 2,存儲學生對象
        set.add(new Student("xiaoqiang", 20));
        set.add(new Student("wangcai", 27));
        set.add(new Student("xiaoming", 22));
        set.add(new Student("xiaoqiang", 20));
        set.add(new Student("daniu", 24));
        set.add(new Student("wangcai", 27));//如果Student 中不實現hash和equals方法,則無法判斷

        // 3,獲取所有學生
        for (Iterator it = set.iterator(); it.hasNext();) {
            Student stu = (Student) it.next();
            System.out.println(stu.getName() + "::" + stu.getAge());
        }

    }
    /**
     * - TODO 4、List集合存儲數據的結構
                 *  List接口下有很多個集合,它們存儲元素所採用的結構方式是不同的,
                 *  這樣就導致了這些集合有它們各自的特點,供給我們在不同的環境下進行使用。
     *  - 數據存儲的常用結構有:堆棧、隊列、數組、鏈表。我們分別來了解一下
     *      - 堆棧
                * - 先進後出
                * - 棧的入口、出口都是棧的頂端位置。 <!--茶杯堆棧-->
                * - 壓棧:就是存元素。
                    即把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
                * - 彈棧:取元素
                    即把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。
     *      - 隊列
                * - 先進先出
                * - 隊列的入口、出口各佔一側。<!--水管隊列-->
     *      - 數組
                * -     查找元素快 :通過索引可以快速訪問指定位置的元素 
                * - 增刪元素慢 : 
                *       - 指定索引位置增加元素:
                            需要創建一個新數組,將指定新元素存儲在指定索引位置,
                            再把原數組元素根據索引,複製到新數組對應索引的位置上。
                *       - 指定索引位置刪除元素:
                            需要創建一個新數組,把原數組元素根據索引,
                            複製到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中。
     *      - 鏈表
                * - 多個節點:<!--環環相扣-->
                *       (它是由兩部分組成,數據域[存儲的數值]+指針域[存儲的地址])之間,
                *       通過地址進行連接
                * - 查找元素慢:
                *       - 想查找某個元素,需要通過連接的節點,依次向後查找指定元素。
                * - 增刪元素快: 
                *       - 增加元素:只需要修改連接下個元素的地址即可。
                *       - 刪除元素:只需要修改連接下個元素的地址即可。
     *
     *  - TODO 5、List接口具體子類介紹
     *      - List
                 * - ArrayList:
                 *      底層的數據結構使用的是數組結構。
                 *      - 特點:查詢速度很快,但是增刪稍慢。
                 *          線程不同步,並且替換了Vector。          
                 * - LinkedList:
                 *      底層使用的是鏈表數據結構。
                 *      - 特點:增刪速度很快,查詢稍慢,
                 *          並且是線程不同步的。
                 * - Vector:
                 *      底層是數組數據結構,數組是可變長度的
                 *      (不斷的new新數組並將原數組元素複製到新數組中)。
                 *      - 特點:線程同步,增刪和查詢速度都慢!並且被ArrayList替代了。
                 *
     *      
     */
    @Test
    public void linkedListTest01() {
        /**
         *  - addFirst();→offerFirst(); 
                addLast();→offerLast();
            - getFirst();→peekFirst(); 
                getLast();→peekLast(); 
                - 獲取元素,但不刪除元素。如果集合中沒有元素,會返回null。
            - removeFirst();→pollFirst(); 
                removeLast();→pollLast(); 
                - 獲取元素,但是元素被刪除。如果集合中沒有元素,會返回null。
         */
        LinkedList link = new LinkedList();
        link.addFirst("abc1");
        link.add("abc2");
        link.addFirst("abc3");
        link.offerFirst("abc4");
        System.out.println(link);
//      獲取元素,但不刪除元素。如果集合中沒有元素,會返回null。
        System.out.println("link.peek():"+link.peek());
        System.out.println("link.peekFirst():"+link.peekFirst());
//       獲取元素,但是元素被刪除。如果集合中沒有元素,會返回null。
        System.out.println("link.removeFirst():"+link.removeFirst());
        System.out.println("link.removeFirst():"+link.removeFirst());
//      倒序  取出link中所有元素
        while (!link.isEmpty()) {
            System.out.print("----"+link.removeLast());
//          正序 取出
//          System.out.print("----"+link.removeFirst());
        }
    }
    @Test
    public void arrayListTest01() {
        List list = new ArrayList();
/**
 * 練習:定義功能,請除去ArrayList集合中的重複元素 */
        list.add("abc1");
        list.add("abc4");
        list.add("abc2");
        list.add("abc1");
        list.add("abc4");
        list.add("abc4");
        list.add("abc2");
        list.add("abc1");
        list.add("abc4");
        list.add("abc2");
        System.out.println(list);
        singleElement(list);// 
        System.out.println(list);
    }
    /**
     * 練習 :去除ArrayList集合中重複的自定義元素。
     *          將自定義對象作爲元素存到ArrayList集合中,
     *          並去除重複元素。比如:存人對象。同姓名同年齡,視爲同一個人,爲重複元素。*/
    @Test
    public void arrayListTest02() {
        Person p1 = new Person("fir",1);
        Person p2 = new Person("sec",2);
        Person p3 = new Person("list1",13);
        Person p4 = new Person("list1",13);

        List list = new ArrayList();
        list.add(p1);
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        System.out.println(list);
//   remove底層用的也是equals()
        list.remove(new Person("fir",1));
        singleElement(list);

        System.out.println(list);
    }
    private void singleElement(List list) {
        List itlist = new ArrayList();

        for(ListIterator it = list.listIterator();it.hasNext();) {
            Object obj = (Object) it.next();
//          通過arrayListTest02()可以看出 contains()底層用的是equals()
            if(!itlist.contains(obj))
                itlist.add(obj);
        }   
        list.clear();
        list.addAll(itlist);
    }

    /**
     * - 3、List接口
     *      - 它是一個元素存取有序的集合。注意:有序指的是存入的順序和取出的順序一致。
     *              例如,存元素的順序是11、22、33,那麼集合中元素的存儲就是按照11、22、33的順序完成的。
     *      - 它是一個帶有索引的集合。
     *              通過索引就可以精確的操作集合中的元素(與數組的索引是一個道理)。
     *      - 集合中可以有重複的元素。
     *              通過元素的equals方法,來比較是否爲重複的元素。
     *  
     *      - List接口中的特有方法上,
     *          它的特有方法都是圍繞索引定義的。
     */
    @Test
    public void listTest() {
        List al = new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java03");
        //在指定位置添加元素
        al.add(1, "java09");
        sop(al);
        //修改元素
        al.set(2, "java007");
        //刪除指定位置的元素
        al.remove(0);
        //通過角標獲取元素
        al.get(1);
        //返回列表中第一次出現的指定元素的索引
        System.out.println("al.indexOf(\"java03\") : "+al.indexOf("java03"));
        sop(al);
        System.out.println("al="+al);
        //--------返回列表中指定的部分視圖------這個好用
        List sub = al.subList(1, 3);
        System.out.println("sub="+sub);
        /**
         * ------------------------------------蠻重要的-----------------------------------------
         * TODO 3、Iterator --- 再解釋
         * -  在迭代時,只能用迭代器的方法操作元素,
                 * 可是Iterator的方法是有限的,
                 * 只能對元素進行判斷,取出,刪除的操作,
                 * 如果想要其他的操作如添加,修改等,
                 * 就需要使用其子接口listIterator——List集合特有的迭代器(listIterator)。
                 * 該接口只能通過List集合的listIterator()獲取。
         */
        for(ListIterator it = al.listIterator();it.hasNext();) {
            Object obj = it.next();
            if(obj.equals("java007")) {
                it.add("abcdefg");
            }
        }
        System.out.println("al="+al);
    }

    /**
     *  - TODO 2、Iterator
     *      Iterator的初步解釋:
             * 把取出方式定義在了集合的內部,
             * 這樣取出方式就可以直接訪問集合內部的元素,
             * 那麼取出方式就被定義成了內部類。
             * 而每一個容器的數據結構不同,所以取出的動作細節也不一樣,
             * 但是都有共性內容——判斷和取出,
             * 那麼可以將這些共性抽取,那麼這些內部類都符合一個規則,該規則就是Iterator。 
     *  - 如何獲取集合的取出對象呢? 
     *      - 答:通過一個對外提供的方法:
     *      Iterator iterator()
     *      ——獲取集合中元素上迭代功能的迭代器對象
     *      (迭代器:具備着迭代功能的對象,而迭代器對象不需要new,直接通過iterator()方法獲取即可)。
     *  - Iterator接口中有如下2個重要方法:
     *      - boolean hasNext():
     *          看看還有沒有被獲取到的元素。
     *          如果沒有就返回false,如果有就返回true。
     *      - E next():
     *          獲取當前光標下的元素,然後再將光標往下移一行。光標初始值是0,
     *          而且每調用一次next()方法,光標都會往下移一行。 
     */

    @Test
    public void iteratorTest() {

        Collection al = new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java03");
        al.add("java04");
        // 開發時,這樣寫
        for(
        Iterator it = al.iterator();it.hasNext();) {
                it.remove();
                System.out.println(it.next());

//              al.add("errorTest");
                /**
                 * - 運行上述代碼發生了異常——java.util.ConcurrentModificationException
                 *          [併發修改異常],這是什麼原因呢?
                 * - 原因:
                 *      - 在使用迭代器或者增強for循環遍歷集合的時候,
                 *          再調用集合的方法修改集合的長度(添加和刪除 ),
                 *          就會發生併發修改異常。
                 *  也可以這樣說:
                 *      - 在迭代過程中,使用了集合的方法對元素進行操作,
                 *          會導致迭代器並不知道集合中的變化,容易引發數據的不確定性。
                 *      - 注意:併發修改異常是由next()方法拋出的。 
                 */
        }
    }
    /**
     * - 1、 集合中的最大接口——Collection
     */

    @Test
    public void test01() {
        Collection c1 = new ArrayList();
        /**
         *  注意:
         *  - add方法的參數類型是Object,以便於接受任意類型。
         *  - 集合中存儲的都是對象的引用(地址)。
         */
        c1.add("abc1");
        c1.add("abc2");
        c1.add("abc3");
        /**
         *  - 注意:刪除元素會改變集合的長度
         */
        c1.remove("abc2");
//      coll.clear();//清空集合
        System.out.println("abc1是否存在:"+c1.contains("abc1"));
        System.out.println("集合是否爲空?"+c1.isEmpty());


        /**
         * - Collection接口中帶all的方法。兩個集合的互相操作
         */
        Collection c2 = new ArrayList();
        c2.add("abc2");
        c2.add("abc3");
        c2.add("abc5");

        c1.addAll(c2);//往coll中添加coll2
        boolean b = c1.containsAll(c2);
        System.out.println("b = " + b);
        c1.removeAll(c2);//移除交集
        c1.retainAll(c2); //只保留交集,將c1和c2不同的元素從c1中刪除,保留c1中和c2相同的元素
    }
    @SuppressWarnings("unused")
    private void sop(Collection c) {
        for(Iterator it = c.iterator();it.hasNext();) {
            System.out.println(it.next());
        }
    }
}

class Person{
    /**
     * 對Person類進行描述,將數據封裝到人對象中。
        定義容器對象,將多個Person對象存儲到集合中。
        去除同姓名同年齡的Person對象(重複元素)。
        取出集合中的Person對象
     */
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {this.name = name;this.age = age;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}

    public boolean equals(Object obj) {
        if(obj==this) {return true;}
        if(!(obj instanceof Person)) {throw new ClassCastException("classtypeError");}
        Person p = (Person) obj;
        return (this.name.equals(p.name)) && (this.getAge()==p.getAge());
    }
    @Override
    public String toString() {
        return "Person [name="+name+", age="+age+"]";
    }
}

// 滿足TreeSet的exercise,實現Comparable接口,實現compareTo()方法 
@SuppressWarnings("rawtypes")
class Student  implements Comparable{
    /**
     * 練習,往HashSet中存儲學生對象(姓名,年齡)。同姓名,同年齡視爲同一個人,不存。 
        解:HashSet中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,
            建立自己的比較方式,才能保證HashSet集合中的對象唯一。
     */
    private String name;
    private int age;
    public Student() {}
    public Student(String name, int age) {super();this.name = name;this.age = age;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
     /** 覆蓋hashCode方法,根據對象自身的特點定義哈希值。
       */
        public int hashCode() {
            final int NUMBER = 13;
            return name.hashCode() + age * NUMBER; // 儘量減小哈希衝突
        }
        /**
         * 還需要定義對象自身判斷內容相同的依據,覆蓋equals()方法。
         */
        public boolean equals(Object obj) {
            if (this == obj) {return true;}
            if (!(obj instanceof Student)) {
                throw new ClassCastException("類型錯誤");
            }
            Student stu = (Student) obj;
            return this.name.equals(stu.name) && this.age == stu.age;
        }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    @Override
    public int compareTo(Object o) {
        Student stu = (Student) o; 
         // 驗證TreeSet集合的add()方法調用了compareTo()方法
//        System.out.println("Student.compareTo():"+this.name + ":" + this.age + "......" + stu.name + ":" + stu.age) ;
//      if(this.age > stu.age)
//          return 1;
//      if(this.age < stu.age)
//          return -1;

        //升序排序,如果 需要降序,加個負號
        return this.age-stu.age==0 ? this.name.compareTo(stu.name) : this.age-stu.age;
    }
}
/**
 * TODO 自定義一個比較器,用來對學生對象按照姓名進行升序排序
 * @author li ayun
 *
 */
class ComparatorByName  /*extends Object 繼承Object類覆蓋了equals()方法*/ implements Comparator  {
    @Override
    public int compare(Object o1, Object o2) {
        Student s1 = (Student) o1;
        Student s2 = (Student) o2;
        int temp = s1.getName().compareTo(s2.getName());
        return temp == 0 ? s1.getAge() - s2.getAge() : temp;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章