4.3常用對象API-集合框架(Collection,Map,泛型,集合工具類)

1.容器:數據封裝(數組,StringBuilder,對象,集合)

數值很多,用數組存。
數組很多,用二維數組存。

數據很多,用對象存。
對象有很多,有集合存。

數組是固定長度,集合是可變長度。

容器

2.集合:
1.基本內容

1.集合類的由來:
    對象用於封裝特有數據,對象多了需要存儲,如果對象的個數不確定。
    就使用集合容器進行存儲。
2.集合特點:
    1.用於存儲對象的容器。
    2.集合的長度是可變的。
    3.集合中不可以存儲基本數據類型值。
3.集合容器因爲內部的數據結果不同,有多種具體容器。
不斷向上抽取,就形成了集合框架。
4.集合框架的頂層是Collection根接口,包含共性內容:
集合在工具包java.util裏
        1.添加
            boolean add(Object obj);
            boolean addAll(Collection coll);
        2.刪除
            boolean remove(Object obj);
            boolean removeAll(Collection coll);  //將兩個集合中的相同元素從調用removeAll的集合中刪除
            void clear();  清空
        3.判斷
            boolean contains(Object obj);   //集合是否包含對象
            boolean containsAll(Collection coll); //集合是否包含集合
            boolean isEmpty();  判斷集合中是否有元素
        4.獲取
            int size();
            Iterator iterator(); 取出元素的方式:迭代器。
            該對象必須依賴於具體容器,因爲每一個容器的數據結構不同。
            所以該迭代器對象是在容器中進行內部實現的。
            對於容器使用者而言,具體的實現不重要,只要通過容器獲取到該實現的迭代器的對象即可。
            也就是iterator()方法。

            Iterator接口就是對所有的Collection容器進行元素取出的公共接口。


        5.其他:
            boolean retainAll(Collection coll); //取兩個集合的交集,和removeAll功能相反
            Object[] toArray(); 將集合轉成數組

2.集合框架的構成圖
集合框架的構成圖

3.List集合接口:有序

特點:
1.存入和取出的順序一致,元素都有索引
2.元素可重複

特有的常見方法:
1.添加:
    void add(index,element);
    void addAll(index, collection);
2.刪除:
    Object remove(index);
3.修改:
    Object set(intdex,element);
4.獲取:
    Object get(index);   //list特有的取出方式之一
    int indexOf(Object);
    int lastIndexOf(Object);
    List subList(from,to);

ListIterator接口:Iterator子接口,存在增刪改查方法

解決在使用迭代器操作集合的同時,又在使用集合本身操作集合,而出現的異常。

List list = new ArrayList();
list.add("abc1");
list.add("abc2");

/*
Iterator it = list.lterator();
while(it.hasNext()){
    Object obj = it.next();
    if(obj.equals("abc2")){
        list.add("abc3");     //在迭代器過程中,不用使用集合操作元素,容易出現異常。
    }   
}*/

ListIterator it = list.listIterator(); 
        //它可以實現在迭代過程中完成對元素的增刪改查。
        //注意只有list集合具備該迭代功能
while(it.hasNext()){
    Object obj = it.next();
    if(obj.equals("abc2")){
        it.add("abc3");  //正確處理
    }   
}

/*減少內存寫法:
for(Iterator it = coll.iterator();it.hasNext();){
    System.out.println(it.next);
}
*/

List接口常用的實現類:Vector, ArrayList, LinkedList

1.Vector:   內部是數組數據結構,是同步的。(數組100%延長,基本不用了)增刪,查詢都很慢。
2.ArrayList:內部是數組數據結構,是不同步的。替代了Vector。數組50%延長。查詢的速度快(空間是連續的)。增刪慢(一動而動全身)

3.LinkedList:內部是鏈表數據結構,是不同步的。增刪元素的速度很快,查詢慢(空間不是連續的)。元素也是有編號的的,只是不連續。
LinkedList:

addFirst();
addLast();
jdk1.6
offerFirst();
offerLast();

getFirst();  //獲取但不移除,如果鏈表爲空,拋出NoSuchElementException
getLast();
jdk1.6
peekFirst(); //獲取但不移除,如果鏈表爲空,返回Null,可以做判斷
peekLast();

removeFirst();//獲取並移除,如果鏈表爲空,拋出NoSuchElementException
removeLast();
jdk1.6
pollFirst(); //獲取並移除,如果鏈表爲空,返回Null,可以做判斷
pollLast();

Vector接口中獲取元素的方法 :
Enumeration elements();返回一個枚舉類型對象。
API:
接口 Enumeration的功能與 Iterator 接口的功能是重複的。此外,Iterator 接口添加了一個可選的移除操作,並使用較短的方法名。新的實現應該優先考慮使用 Iterator 接口而不是 Enumeration 接口。

    Vector v = new Vector();
    v.addElement("abc1");
    v.addElement("abc1");

/*
    Enumeration en = v.elements();
    while(en.hasMoreElement()){
        System.out.println(en.nextElement());
    }*/


    Iterator it = it.iterator();
    while(it.hasNext()){
        System.out.println(it.next());
    }

LinkedList :

LinkeList link = new LinkeList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");

System.out.println(link.getFirst());  //獲取第一個但不刪除
System.out.println(link.removeFirst());  //獲取第一個並刪除

while(!link.isEmpty()){
    System.out.println(link.removeFirst());  //不用迭代器輸出對象,但是會刪除集合對象。
}


Iterator it = link.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}

//LinkedList集合練習題:

/*使用LinkedList模擬一個堆棧或者隊列數據結構。

堆棧:先進後出 First In Last Out FIFO

隊列:先進先出 First In First Out FIFO

應該描述這樣一個容器,給使用者提供一個容器對象完成這兩種結構中的一種。

*/
//工具類
class DuiLie{
private LinkedList link;

public DuiLie(){
    link = new LinkedList();
}

public void myAdd(Object obj){
    link.addLast(obj);
}

public Object myGet(){
    return link.removeFirst();
}

pubilc boolean isNull(){
    return link.isEmpty();
}
}

ArrayList集合存儲自定義對象:

public class ArrayListTest {

    public static void main(String[] args) {

        ArrayList al = new ArrayList();
        al.add(new Person("list1",21));//public boolean add(Object obj),所以後面需要強轉
        al.add(new Person("list2",22));
        al.add(new Person("list3",23));
        al.add(new Person("list4",24));

        Iterator it = al.iterator();
        while(it.hasNext()){
//          System.out.println(((Person)it.next()).getName()+ " : "+ ((Person) it.next()).getAge());
            Person p = (Person) it.next();    //強轉
            System.out.println(p.getName() + " : " + p.getAge());
        }

//al.add(5);   //自動裝箱

    }

}

//ArrayList集合練習題:
ArrayList集合在判斷元素是否相同,使用的是元素的equals()方法,只需覆蓋equals()方法即可。
而hashSet則需要覆蓋hashCode()方法和equals()方法。

數據結構不一樣,對元素的判斷依據也不一樣。

//定義功能去除ArrayList中的重複元素。

    //存儲字符串的情況:
    public static  ArrayList getSingleElement(ArrayList al){
        //1.定義一個臨時容器
        ArrayList temp = new ArrayList();

        //2.迭代al集合
        Iterator it = al.iterator();
        while(it.hasNext()){
            Object obj = (Object) it.next();

        //3.判斷被迭代的元素是否在臨時容器中存在
            if(!temp.contains(obj))
                temp.add(obj);
        }       
        return temp;
    }

    //存儲自定義對象的情況:在自定義類中要重寫equals()方法
    public static  ArrayList getSingleElement(ArrayList al){
        //1.定義一個臨時容器
        ArrayList temp = new ArrayList();

        //2.迭代al集合
        Iterator it = al.iterator();
        while(it.hasNext()){
            Person p = (Person) it.next();

        //3.判斷被迭代的元素是否在臨時容器中存在
            if(!temp.contains(p))   //比較的原理是調用了object的equals方法。
                temp.add(p);
        }

        return temp;
    }

    public boolean equals(Object obj) {
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

4.Set集合接口:不重複

特點:
1.元素不能重複
2.無序(有可能“有序”)

set接口中的方法和Collection一致。

|--HashSet:內部數據結構是哈希表(實際上是一個 HashMap 實例) ,是不同步的。
    是通過對象的hashcode和equals方法來判斷對象唯一性。
    如果對象的hashcode值不同,那麼不用判斷equals方法,直接存儲到哈希表中。
    如果對象的hashcode值相同,那麼要再次判斷對象的equals方法是否爲true。
        如果爲true,視爲相同元素,不存。如果爲false,視爲不同元素,就進行存儲。

    記住,如果元素要存儲到HashSet集合中,必須覆蓋hashCode方法和equals方法。
    一般情況下,如果定義的類會產生很多對象,比如人,學生,書,通常都需要覆蓋equals方法,hashcode方法,建立對象判斷是否相同的依據。

|--TreeSet:可以對set集合中的元素進行排序。是不同步的。
            判斷元素唯一性的方式:就是根據比較方法的返回結果是否是0,是0,就是相同元素,不存。

        ①TreeSet對元素進行排序的方式一:
            讓元素自身具備比較功能,元素需要實現comparable接口。覆蓋compareTo方法。

        如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。
        ②可以使用TreeSet集合的第二種排序方式二:
            讓集合自身具備比較功能。在集合創建對象時完成。定義一個類實現Comparator接口,覆蓋compare方法。將該類的對象作爲參數傳遞給TreeSet集合的構造函數。

TreeSet集合內部數據結構:二叉樹(紅黑樹)(每次插入元素前都會默認記錄折中元素的位置,採用二分查找提高效率)
TreeSet集合內部結構_二叉樹

HashSet集合存儲自定義對象:

public class HashSetTest {

    public static void main(String[] args) {

        HashSet hs = new HashSet();

        hs.add(new Person("list1",24));
        hs.add(new Person("list2",25));
        hs.add(new Person("list3",26));
        hs.add(new Person("list2",25));


        Iterator it = hs.iterator();

        while(it.hasNext()){
            Person p = (Person) it.next();
            System.out.println(p.getName()+" : "+ p.getAge());
        }
    }

}


public class Person {
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }


    @Override
    public int hashCode() {
//      System.out.println(this+ ".......hashcode");
        return name.hashCode() + age*38;  //減少哈希值相同概率
    }

    @Override
    public boolean equals(Object obj) {
//      System.out.println(this+ "....equals..." + obj);

        if(this == obj)
            return ture;
        if(obj instanceof Person)
            throw new ClassCastException("類型錯誤");
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.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 String toString()
    {
        return name +": "+age;
    }

}

LinkedHashSet:由哈希表和鏈表實現。有序,怎麼存怎麼取。(注意和排序區別)。
所以判斷用什麼集合,無關有無順序。需要唯一性用set集合,不需要就用list集合。

HashSet hs = new LinkedHashSet();
hs.add("abc1");
hs.add("abc2");
hs.add("abc3");

Iterator it = hs.iterator();
while(it.hasNext()){
    System.out.println(it.next());    //順序輸出
}

TreeSet集合對元素進行排序的方式一:存儲自定義對象時需要實現 Comparable接口,複寫compareTo()方法。

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet<Person> ts =new TreeSet<Person>();

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator<Person> it = ts.iterator();
            while(it.hasNext()){
                Person p =it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }

//          demo1(ts);

    }

    public static void demo1(TreeSet ts) {
        ts.add("abc");    //字符串本身就重寫了compareTo方法。
        ts.add("zaa");
        ts.add("aa");
        ts.add("nba");
        ts.add("dwe");

        Iterator it = ts.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

}


public class Person implements Comparable<Person>{   //實現Comparable接口
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(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;
    }

    @Override
    public int compareTo(Person p) {    //重寫compareTo方法。按年齡排序
    //  Person p = (Person)o;       //元素比較傳入一個參數

        int temp =  this.age - p.age;
        return temp==0?this.name.compareTo(p.name):temp;
    }

}

比較器Comparator:更常用
(如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。)
TreeSet集合對元素進行排序的方式二:讓集合自身具備比較功能。在集合創建對象時完成。
定義一個類實現Comparator接口,覆蓋compare方法。將該類的對象作爲參數傳遞給TreeSet集合的構造函數。

TreeSet有序化:如果compare返回值固定是正數,則會按存入順序輸出,如果是負數,則逆序輸出。

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet ts =new TreeSet(new ComparatorByName());    //使用比較器自定義排序功能

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator it = ts.iterator();
            while(it.hasNext()){
                Person p =(Person) it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }       
    }

public class ComparatorByName implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {   //集合自身比較需要傳2個參數
        Person p1 = (Person)o1;
        Person p2 = (Person)o2;

        int temp = p1.getName().compareTo(p2.getName());
        return temp ==0?p1.getAge() - p2.getAge():temp;
    }

}

3.Map集合:和Collection一樣的頂層接口。

Map:一次添加一對元素。Collection一次添加一個元素。
    Map也稱爲雙列集合,Collection稱爲單列集合。
    其實Map集合中存儲的就是鍵值對。
    Map集合中必須保證 鍵 的唯一性。

常用方法:
1.添加
    value put(key, value); 返回前一個和key關聯的值,如果沒有返回null
2.刪除
    void clear(); 清空map集合
    value remove(key); 根據指定的鍵刪除這個鍵值對
3.判斷
    boolean containKey(key);
    boolean containValue(value);
    boolean isEmpty();
4.獲取
    value get(key);通過鍵獲取值。如果沒有該鍵返回null。
                    當然可以通過返回null,來判斷是否包含指定鍵。
    int size(); 獲取鍵值對的個數。

取出map中的所有元素:

①keySet()方法:返回所包含的鍵的set視圖。返回set集合。

public class MapDemo {

    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<Integer,String>();
        method(map);
    }

    public static void method(Map<Integer,String> map){
        map.put(8, "wangwu");
        map.put(3, "lisan");
        map.put(2, "sunhao");
        map.put(6, "tiantian");

        //取出map中的所有元素
        //原理:通過keyset方法獲取map中所有的鍵所在的set集合,再通過set的迭代器獲取到每一個鍵,
        //再對每一個鍵通過map集合的get方法獲取其對應的值即可

        Set<Integer> keySet = map.keySet();
        Iterator<Integer> it = keySet.iterator();
        while(it.hasNext()){
            Integer key = it.next();
            String value = map.get(key);
            System.out.println(key+":"+value);
        }

    }
}

keySet方法演示

②entrySet()方法:返回所包含的映射關係的set視圖。返回set集合。

/*
    通過Map轉換成set就可以迭代。
    通過entrySet方法 將鍵和值的映射關係作爲對象存儲到了Set集合中,而這個映射關係的類型就是Map.Entry類型。
 */ 

        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

        Iterator<Map.Entry<Integer, String>> it = entrySet.iterator();
        while(it.hasNext()){
            Map.Entry<Integer, String> me = it.next();

            System.out.println(me.getKey()+":"+me.getValue());

        }

entrySet()演示
③values():返回此映射中包含的值的 Collection 視圖。

Collection<String> values = map.values();

Iterator<String> it = values.iterator();
        while(it.hasNext()){            
            System.out.println(it.next());      
        }

Map集合的常見子類對象:1.0時單列集合就是Vector,雙列集合就是Hashtable。

1.Hashtable:內部機構是哈希表,是同步的。不允許null作爲鍵和值。
        類Properties:用來存儲鍵值對型的配置文件的信息。可以和IO技術相結合。
2.HashMap:內部結構是哈希表,是不同步的。允許null作爲鍵和值。
3.TreeMap:內部結構是二叉樹,是不同步的。可以對Map集合中的鍵進行排序。

LinkedHashMap:由哈希表和鏈表實現。有序,怎麼存怎麼取。(注意和排序區別)。

Map集合練習:

/*
 * 練習:
 * "fdgavcbsacdfs"獲取該字符串中嗎,每一個字母出現的次數。
 * 要求打印的結果是:a(2)b(1)...
 * 
 * 對於結果的分析發現,字母和次數之間存在映射關係,而且這種關係很多。
 * 很多就需要存儲,能存儲映射關係的容器有數組和Map集合。
 * 
 * 關係中沒有一方是有序編號,所以用Map集合。
 * 因爲保證唯一性的一方具有順序a,b...,所以使用TreeMap集合。
 * 
 *這個集合最終存儲的是字母和次數的對應關係。
 *
 *1.因爲操作的是字符串中的字母,所以先將字符串變成字符數組。
 *2.遍歷字符數組,用每一個字母作爲鍵去查Map集合這個表。
 *如果該字母鍵不存在,就將該字母作爲鍵,1作爲值存儲到map集合中。
 *如果該字母鍵存在,就將該字母鍵的值取出並+1,再將該字母和+1後的值存儲到map集合中。鍵相同值會覆蓋。
 *3.遍歷結束,map集合就記錄了所有字母的次數。
 */

public class Test {

    public static void main(String[] args) {
            String str = "f   --2dgavcbsacdfs";
            String s = getCharCount(str);

            System.out.println(s);
         }

         public static String getCharCount(String str){
             //將字符串變成字符數組
             char[] chs = str.toCharArray();

             //定義map集合表
             Map<Character,Integer> map = new TreeMap<Character, Integer>();

             for (int i = 0; i < chs.length; i++) {
                if(!(chs[i]>='a' && chs[i] <='z' || chs[i]>='A' && chs[i] <='Z'))
                    continue;

                 //將數組中的字符作爲鍵去查map表。
                 Integer value = map.get(chs[i]);

                 int count = 1;   //簡化代碼

                 //判斷值是否爲null
                 if(value!=null)
                 {
                     count = value + 1;
                 }
                 map.put(chs[i],count);
                /* 
                    if(value ==null){
                     map.put(chs[i],1);
                 }
                 else{
                     map.put(chs[i], value+1);
                 }*/

            }
             //return map.toString();
             return mapToString(map);
         }

        private static String mapToString(Map<Character, Integer> map) {

            StringBuilder sb = new StringBuilder();

            Iterator<Character> it = map.keySet().iterator();
            while(it.hasNext()){
                Character key = it.next();
                Integer value = map.get(key);

                sb.append(key +"("+ value +")");

            }

            return sb.toString();
        }

}


Map在有映射關係時,可以優先考慮。
在查表法中的應用較爲多見。

4.泛型:jdk1.5出現的安全機制。編譯時期的安全技術。

好處:
1.將運行時期的問題ClassCastException轉到了編譯時期。
2.避免了強制轉換的麻煩。

<>:
什麼時候用:當操作的引用數據類型不確定的時候,就是用<>。將操作的引用數據類型傳入即可。
其實<>就是一個用於接收具體引用數據類型的參數範圍。

在程序中,只要用到了帶有<>的類或接口,就要明確傳入的具體引用數據類型。

泛型的 擦除和補償:

泛型技術是給編譯器使用的技術,用於編譯時期,確保了類型的安全。

擦除:運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱爲泛型的擦除。
    爲什麼擦除:爲了兼容運行的類加載器。

補償:在運行時,通過獲取元素的類型進行轉換動作。不用使用者再強制轉換。

泛型在集合中的應用:

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet<Person> ts =new TreeSet<Person>();

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator<Person> it = ts.iterator();
            while(it.hasNext()){
                Person p =it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }   
    }

public class Person implements Comparable<Person>{   //實現Comparable接口
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(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;
    }


    public int compareTo(Person p) {    //重寫compareTo方法。按年齡排序
    //  Person p = (Person)o;       //元素比較傳入一個參數

        int temp =  this.age - p.age;
        return temp==0?this.name.compareTo(p.name):temp;
    }

}

泛型類:

/*
 *在jdk1.5後,使用泛型來接收類中要操作的引用數據類型。
 *泛型類,什麼時候用:當類中的操作的引用數據類型不確定的時候,就使用泛型類表示。
 *
 */
public class Tool<Q>{
    private Q q;

    public Q getObject() {
        return q;
    }

    public void setObject(Q object) {
        this.q = object;
    }
}
/*public class Tool {
    private Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }


}*/


public class GenericDefineDemo3 {

    public static void main(String[] args) {

        Tool<Student> tool = new Tool<Student>();  //使用泛型類

        tool.setObject(new Student());   //如果傳入的對象不對,編譯報錯

        Student stu = tool.getObject();


/*      Tool tool = new Tool();

        tool.setObject(new Worker());

        Student stu = (Student)tool.getObject();*/
    }

}

泛型方法:<>放在修飾符的後面,返回值的前面。
一旦使用了泛型,只能使用Object的方法。

//將泛型定義在方法上

public <W> void show(W str){
        System.out.println("show:" + str);
    }

public <Q>void print(Q str){
        System.out.println("print:"+ str);
    }

/*
//當方法靜態時,不能訪問 類 上定義的泛型,
//如果靜態方法使用泛型,只能將泛型定義在方法上。

public static void method(Q str){
        System.out.println("method:"+ str);
    }

//正確的寫法:
public static<Y> void method(Y str){
        System.out.println("method:"+ str);
    }


*/


public static void main(String[] args) {

        Tool<String> tool = new Tool<String>();
        tool.show("abc");  //自動選擇參數類型
        tool.print(new Integer(4)); //報錯,print方法是根據Tool類的泛型來確定
    }

泛型接口:

interface Inter<T>{
    public void show(T t);
}

class InterImpl implements Inter<String>{
    public void show(String str){
        System.out.println("show :" + str);
    }
}

class InterImpl2 implements Inter<Q>{
    public void show(Q q){
        System.out.println("show :" + q);
    }
}



public static void main(String[] args){
    InterImpl in = new InterImpl();
    in.show("haha");

    InterImpl2<Integer> in2 = new InterImpl2<Integer>();
    in2.shoe(5);
}

泛型的高級應用:泛型的限定

泛型的通配符:?未知類型

public class GenericDemo4 {

    public static void main(String[] args) {

        ArrayList<String> al = new ArrayList<String>();
        al.add("abc");
        al.add("hehe");

        ArrayList<Integer> al2 = new ArrayList<Integer>();
        al2.add(55);
        al2.add(67);

        printCollection(al);
        printCollection(al2);

    }

    /**
     * 迭代並打印集合中的元素
     * @param al
     */
    //泛型的通配符?

    private static  void printCollection(Collection<?> al) {
        Iterator<?> it = al.iterator();

        while(it.hasNext()){
    //      T t = it.next();
            System.out.println(it.next().toString());
        }
    }

    /*
    private static <T> void printCollection(Collection<T> al) {
        Iterator<T> it = al.iterator();

        while(it.hasNext()){
            T t = it.next();
            System.out.println(t.toString());
        }
    }*/

}

泛型的限定:? ( 就是 ? extends Object)

/*
    ?extends E :接收E類型或者E的子類型對象。上限。
    ?super E: 接收E類型或者E類型的父類型。下限。
*/
private static  void printCollection(Collection<? extends Person> al) {
        Iterator<? extends Person> it = al.iterator();

        while(it.hasNext()){
    //      T t = it.next();
            Person p = it.next();
            System.out.println(p.toString());
        }
    }

泛型限定在集合中的應用:

上限的體現:Collection中的addAll()方法。
boolean addAll(Collection<? extends E> c) 
    將指定 collection 中的所有元素都添加到此 collection 中(可選操作)。 

原因:如果不限定,則所有的對象都可以添加,這時在取出的時候需要強制轉換,容易出現問題。

一般在存儲元素的時候,都使用上限。因爲這樣取出都是按照上限類型來運算的,不會出現類型安全隱患。

下限的體現:TreeSet中的構造方法:
TreeSet(Comparator<? super E> comparator) 
       構造一個新的空 TreeSet,它根據指定比較器進行排序。

通常對集合中的元素進行取出操作時,可以用下限。用得較少。


通配符?的體現:Collection中的containsAll()方法。
 boolean containsAll(Collection<?> c) 
       如果此 collection 包含指定 collection 中的所有元素,則返回 true。 

因爲equals方法比較兩邊可以是任意對象。

5.集合查閱的技巧:

需要唯一麼?
需要:Set
    需要指定順序麼?
        需要:TreeSet
        不需要:HashSet
            但是想要一個和存儲一致的順序:LinkedHashSet
不需要:List
    需要頻繁增刪麼?
        需要:LinkedList
        不需要:ArrayList

如何記住每一個容器的結果和所屬體系?
後綴名就是該集合所屬的體系:
List:
    ArrayList
    LinkedList

Set:
    HashSet
    TreeSet

前綴名就是該集合的數據結構:
Array:就要想到數組,想到查詢快,有角標
Link:就要想到鏈表,想到增刪快,想到add/get/remove + first/last的方法
Hash:就要想到哈希表,想到唯一性,想到元素需要覆蓋hashcode方法和equals方法。
tree:就要想到二叉樹,想到排序,想到兩個接口Comparable,Comparator。

通常這些常用的集合都是不同步的。

6.集合框架工具類:
Collections: 操作集合的工具類
1.排序:

/*
static <T extends Comparable<? super T>> void sort(List<T> list) 
        根據元素的自然順序 對指定列表按升序進行排序。 
static <T> void sort(List<T> list, Comparator<? super T> c) 
        根據指定比較器產生的順序對指定列表進行排序。 
static void swap(List<?> list, int i, int j) 
        在指定列表的指定位置處交換元素。
*/



public class CollectionsDemo {

    public static void main(String[] args) {

        /*
         * Collections:是集合框架的工具類。
         * 裏面的方法都是靜態的。
         */
        demo_1();
    }

    public static void demo_1(){
        List<String> list = new ArrayList<String>();

        list.add("abc");
        list.add("cba");
        list.add("aa");
        list.add("zzz");
        list.add("cba");  //有重複的,不能用TreeSet
        list.add("nbaa");
        System.out.println(list);

        //對list集合進行指定順序的排序。

        Collections.sort(list);
        System.out.println(list);
        Collections.sort(list,new ComparatorByLength());
        System.out.println(list);


        //源碼實現:
        mySort(list);
        System.out.println(list);
        mySort1(list,new ComparatorByLength());
        System.out.println(list);
    }

    public static <T> void mySort1(List<T> list, Comparator<? super T> comp){
        for (int i = 0; i < list.size()-1; i++) {
            for (int j = i+1; j < list.size(); j++) {
                if(comp.compare(list.get(i), list.get(j))>0){
//                  T temp = list.get(i);
//                  list.set(i, list.get(j));
//                  list.set(j, temp);

                    Collections.swap(list, i, j);
                }
            }
        }
    }

    public static<T extends Comparable<? super T>> void mySort(List<T > list){
        for (int i = 0; i < list.size()-1; i++) {
            for (int j = i+1; j < list.size(); j++) {
                if(list.get(i).compareTo(list.get(j))>0){
//                  T temp = list.get(i);
//                  list.set(i, list.get(j));
//                  list.set(j, temp);

                    Collections.swap(list, i, j);
                }
            }
        }
    }

}

2.折半,最值,逆序,替換,置換,集合和枚舉的轉換,非同步集合轉同步集合。

折半: 先有序化,然後查找。根據有序化分爲兩種方式:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 需要自己先排序
          使用二分搜索法搜索指定列表,以獲得指定對象。 
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) 
          使用二分搜索法搜索指定列表,以獲得指定對象。 

最值:
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 不需要自己排序
          根據元素的自然順序,返回給定 collection 的最大元素。 
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 
          根據指定比較器產生的順序,返回給定 collection 的最大元素。 

逆序:
static <T> Comparator<T> reverseOrder() 
        返回一個比較器,它強行逆轉實現了 Comparable 接口的對象 collection 的自然順序。 
static <T> Comparator<T> reverseOrder(Comparator<T> cmp) 
        返回一個比較器,它強行逆轉指定比較器的順序。 
static void reverse(List<?> list) 
        反轉指定List列表中元素的順序。 

例子:TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); //String自己實現了Comparable 接口
原理:TreeSet<String> ts = new TreeSet<String>(new Comparator<String>{
    public int compare(String o1,String o2){
        int temp = o2.compareTo(o2);
        return temp;
}
});


替換:
    static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
          使用另一個值替換列表中出現的所有某一指定值。 
     static <T> void fill(List<? super T> list, T obj) 
          使用指定元素替換指定列表中的所有元素。 
置換:
     static void shuffle(List<?> list) 
          使用默認隨機源對指定列表進行置換。 
     static void shuffle(List<?> list, Random rnd) 
          使用指定的隨機源對指定列表進行置換。

集合和枚舉的轉換:
    static <T> ArrayList<T> list(Enumeration<T> e) 
          返回一個數組列表,它按返回順序包含指定枚舉返回的元素。 
    static <T> Enumeration<T> enumeration(Collection<T> c) 
          返回一個指定 collection 上的枚舉。  

非同步集合轉同步集合:
static <T> List<T> synchronizedList(List<T> list) 
          返回指定列表支持的同步(線程安全的)列表。 
static <T> Set<T> synchronizedSet(Set<T> s) 
          返回指定 set 支持的同步(線程安全的)set。 
static <T> Collection<T> synchronizedCollection(Collection<T> c) 
          返回指定 collection 支持的同步(線程安全的)collection。 
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
          返回由指定映射支持的同步(線程安全的)映射。      

給非同步的集合加鎖 原理:

List list = new ArrayList();   //非同步的
list = MyCollection.synList(list);   //返回一個同步的list

class MyCollection{
    public List synList(List list){
        return new MyList(list);
    }

 private class MyList implements List{
    private List list;
    private static final Object lock = new Object();
    MyList(List list){
        this.list = list;
    }

    public boolean add(Object obj){
        synchronized(lock){
            return list.add(obj);
        }
    }

    public boolean remove(Object obj){
        synchronized(lock){
            return list.remove(obj);
        }
    }
}
}

Arrays:操作數組的集合工具類

1.折半:包括byte,char,double,float,int,short,long,Object,T泛型數組。
static int binarySearch(byte[] a, byte key) 
          使用二分搜索法來搜索指定的 byte 型數組,以獲得指定的值。 
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key) 
          使用二分搜索法來搜索指定的 byte 型數組的範圍,以獲得指定的值。 
...
static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) 
          使用二分搜索法來搜索指定數組,以獲得指定對象。 
static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) 
          使用二分搜索法來搜索指定數組的範圍,以獲得指定對象。        

2.複製指定數組:包括boolean,byte,char,double,float,int,short,long,T泛型數組。
static boolean[] copyOf(boolean[] original, int newLength) 
          複製指定的數組,截取或用 false 填充(如有必要),以使副本具有指定的長度。  

3.複製指定範圍指定數組:包括boolean,byte,char,double,float,int,short,long,T泛型數組。
static boolean[] copyOfRange(boolean[] original, int from, int to) 
          將指定數組的指定範圍複製到一個新數組。 

4.判斷兩個數組是否相等(是指內容全部相同):包括boolean,byte,char,double,float,int,short,long,Object數組。
static boolean equals(boolean[] a, boolean[] a2) 
        如果兩個指定的 boolean 型數組彼此相等,則返回 true5.重置:包括boolean,byte,char,double,float,int,short,long,Object數組。
static void fill(boolean[] a, boolean val) 
         將指定的 boolean 值分配給指定 boolean 型數組的每個元素。
static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) 
         將指定的 boolean 值分配給指定 boolean 型數組指定範圍中的每個元素。    

6.哈希碼:包括boolean,byte,char,double,float,int,short,long,Object數組。
static int hashCode(boolean[] a) 
          基於指定數組的內容返回哈希碼。  

7.排序:包括boolean,byte,char,double,float,int,short,long,Object,T泛型數組。
static void sort(byte[] a) 
         對指定的 byte 型數組按數字升序進行排序。                         
static void sort(byte[] a, int fromIndex, int toIndex) 
          對指定 byte 型數組的指定範圍按數字升序進行排序。
...
static <T> void sort(T[] a, Comparator<? super T> c) 
          根據指定比較器產生的順序對指定對象數組進行排序。 
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) 
          根據指定比較器產生的順序對指定對象數組的指定範圍進行排序。    

8.字符串表示:包括boolean,byte,char,double,float,int,short,long,Object數組。
static String toString(boolean[] a) 
          返回指定數組內容的字符串表示形式。
原理:
    public static void main(String[] args) {

        /*
         * Arrays:集合框架的工具類。裏面的方法都是靜態的。
         */
        int[] arr = {3,1,2,5,6,3};
        System.out.println(Arrays.toString(arr));
    }

    //toString的經典實現。
    public static String myToString(int[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {    //中間省略判斷,提高了效率
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    } 

9.其他:
static boolean deepEquals(Object[] a1, Object[] a2) 
          如果兩個指定數組彼此是深層相等 的,則返回 true。除了比較數組的元素對象,還比較對象的內容。      
10.將數組轉成List集合: Arrays-asList方法

static <T> List<T> asList(T... a) 
          返回一個受指定數組支持的固定大小的列表。 

原理: 
/*
    重點:List asList(數組)    將數組轉成集合。

    好處:可以使用集合的方法操作數組中的元素。
    注意:
    ①數組的長度是固定的,所以對於集合的增刪方法是不可以使用的。否則會發生java.lang.UnsupportedOperationException。

    ②如果數組中的元素是對象,那麼轉成集合時,直接將數組中的元素作爲集合中的元素進行存儲。
      如果數組中的元素是基本數據類型數值,那麼會將該數組作爲集合中的元素進行存儲。
      int [] arr = {13,25,65};
      List<int[]> list = Arrays.asList(arr);

    原因:想知道數組是否包含某元素。
         想知道元素在數組中的位置。
    但是數組集合工具類中沒有這些方法。
*/
public static void main(String[] args) {    

        String[] arr = {"abc","haha","xixi"};
        boolean b = myContains(arr, "haha");
        System.out.println(b);

        List<String> list = Arrays.asList(arr);//轉換成列表
        boolean b1 = list.contains("haha");
        System.out.println(b1);
    // list.add("a");  //錯誤操作                   
    }

public static boolean myContains(String[] arr, String key){
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].equals(key))
                return true;
        }
        return false;
    }
11.將集合轉成數組: Collection接口-toArray方法
 Object[] toArray() 
       返回包含此 collection 中所有元素的數組。 
 T[] toArray(T[] a) 
       返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。 

原理:

/*
集合轉換數組:
     使用Collection接口中的toArray方法。

好處:可以對集合中的元素操作的方法進行限定。不允許對其進行增刪。

*/     

public static void main(String[] args) {

        List<String> list = new ArrayList<String>();
        list.add("abc1");
        list.add("abc2");
        list.add("abc3");

        /*
            Object[] toArray() 
                    返回包含此 collection 中所有元素的數組。 

         */
        Object[] arr1 = list.toArray();
        System.out.println(Arrays.toString(arr1));

        /*
            T[] toArray(T[] a) 
                    返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。

        注意:
            該方法需要傳入一個指定類型的數組。
            如果長度小於集合的size,那麼該方法會創建一個同類型並和集合相同size的數組。
            如果長度大於集合的size,那麼該方法會使用指定的數組,存儲集合中的元素,其他位置默認null。

            建議,最後長度就指定爲,集合的size.

          */
        String[] arr = list.toArray(new String[list.size()]);
        System.out.println(Arrays.toString(arr));
    }

7.JDK1.5特性

①.JDK1.5給Collection接口找了個父接口Iterable:

實現這個接口允許對象成爲 “foreach” 語句的目標。

foreach語句:底層還是實現的迭代。
格式:
for(類型 變量: Collection集合|數組)
{
}

舉例:
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Iterator<String> it = list.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
*/
foreach(String s:list){
    System.out.println(s);
}
傳統for和高級for的區別:

foreach侷限性:
必須有遍歷的目標(數組或Collection單列集合),一般只用於遍歷,不會對元素進行過多的操作。是用來簡化書寫的。

傳統for循環相對foreach的優勢:
可以定義控制循環的條件和增量。

使用場景:
對數組的遍歷如果僅僅是獲取數組中的元素,可以使用foerach。
如果要對數組的角標進行操作建議使用傳統for
對於map集合,不能直接使用foreach遍歷。但是可以將map轉換成單列的set,就可以用了。

例子:
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("小米", 2000);
        map.put("蘋果", 5000);
        map.put("三星", 4000);

//entrySet:     
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //map變成set集合
        for (Map.Entry<String, Integer> entry : entrySet) {                 //foreach遍歷
            System.out.println(entry.getKey() + ":" +entry.getValue());
        }

//keySet:
for(String key : map.keySet()){
    Integer value = map.get(key);
    System.out.println(key + ": " + value);
}

//傳統迭代:     
        Iterator<Map.Entry<String,Integer>> it= map.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<String, Integer> me = it.next();
            System.out.println(me.getKey() + ":" + me.getValue());
        }
    }

2.函數可變參數:int …
內部執行的動作:創建並將參數封裝成數組。

其實就是一個數組,但是接收的是數組的元素。
自動將這些元素封裝成數組。簡化了調用者的書寫。

注意事項:可變參數類型必須定義在參數列表的結尾處。

//求多個正數的和

public static void main(String[] args){
    int sum = add(2,5,6,6);
    System.out.println("sum= " + sum);

    int sum1 = add(2,5,6);
    System.out.println("sum1= " + sum1);

}

public static int add(int... arr){  //int... 和int[]等效,簡化書寫。調用傳參的時候不需要建立數組,直接傳值。
    int sum = 0;
    for(int i:arr)
        sum+=i;
    return sum;

}

3.靜態導入:導入類的靜態成員,方便程序簡化書寫。
因爲靜態成員不需要對象,直接使用,所以需要在程序加載前進行靜態導入。

import static java.util.Collections.sort;
import static java.util.Collections.max;
或者:
import static java.util.Collections.*;

    sort(list);
    max(list);

import static java.lang.System.*;

    out.println("123");  //out是靜態的,println不是靜態的。out對應一個對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章