Java基礎-5.collection、list、set、map

一、 集合

1.集合的由來

數組長度是固定,當添加的元素超過了數組的長度時需要對數組重新定義,java內部提供了集合類,能存儲任意對象,長度是可以改變的,隨着元素的增加而增加,隨着元素的減少而減少。

2.數組和集合的區別

區別1

數組既可以存儲基本數據類型,又可以存儲引用數據類型,基本數據類型存儲的是值,引用數據類型存儲的是地址值。

集合只能存儲引用數據類型(對象),集合中也可以存儲基本數據類型,但是在存儲的時候會自動裝箱變成對象。

區別2

數組長度是固定的,不能自動增長。

集合的長度的是可變的,可以根據元素的增加而增長。

3.數組和集合什麼時候用

如果元素個數是固定的推薦用數組。如果元素個數不是固定的推薦用集合。集合在增長的時候,是將原來的集合丟棄掉,再重新創建一個集合,將原來的元素拷貝到新的集合中,所以從內存角度考慮,比較浪費空間。

二、Collection

collection包含list和set,體系結構如下:

List:ArrayList(數組)、linkedlist(鏈表)、vector(數組)(按順序,可重複,存取一致)

Set:hashset(哈希算法)、treeset(二叉樹算法)、linkedhashset(不按順序,不可重複,存取不一致)

1.Collection的方法

boolean add(E e)

boolean remove(Object o)

void clear()

boolean contains(Object o)

boolean isEmpty()

int size()

2.Collection All方法

bolean addAll(Collection c) 

bolean removeAll(Collection c) ,刪除的是和C的交集

bolean containsAll(Collection c),判斷是否包含C的全部

bolean retainAll(Collection c) 取交集,返回的如果還是集合本身,是false,否則是true

三、集合遍歷

1.轉成數組遍歷

可以實現集合的遍歷:toArray()

Collection coll = new ArrayList();

coll.add(new Student("張三",23));

coll.add(new Student("李四",24));

coll.add(new Student("王五",25));

coll.add(new Student("趙六",26));

object[] arr = coll.toArray();   //將集合轉換成數組

for (int i = 0; i < arr.length; i++) {

    Student s = (Student)arr[i];  //強轉成Student

    System.out.println(s.getName() + "," + s.getAge());

}

2.迭代器遍歷

集合是用來存儲元素,存儲的元素需要查看,那麼就需要迭代(遍歷)

Collection c = new ArrayList();

c.add("a");

c.add("b");

c.add("c");

c.add("d");

Iterator it = c.iterator();        //獲取迭代器的引用

while(it.hasNext()) {          //集合中的迭代方法(遍歷)

    System.out.println(it.next());

}

四、Collection存儲對象並遍歷

1.自定義對象遍歷

Collection存儲自定義對象並用迭代器遍歷

Collection c = new ArrayList();

c.add(new Student("張三",23));

c.add(new Student("李四",24));

c.add(new Student("王五",25));

for(Iterator it = c.iterator();it.hasNext();) {

    Student s = (Student)it.next();

    System.out.println(s.getName() + "," + s.getAge());

}

Iterator it = c.iterator();       //獲取迭代器

while(it.hasNext()) {         //判斷集合中是否有元素

   Student s = (Student)it.next();

    System.out.println(s.getName() + "," + s.getAge());

}

2.迭代器原理

迭代器是對集合進行遍歷,而每一個集合內部的存儲結構都是不同的,每一個集合存和取都是不一樣,那麼就需要在每一個類中定義hasNext()和next()方法,這樣做是可以的,但是會讓整個集合體系過於臃腫,迭代器是將這樣的方法向上抽取出接口,然後在每個類的內部,定義自己迭代方式,這樣做的好處有二,第一,規定了整個集合體系的遍歷方式都是hasNext()和next()方法,第二,代碼有底層內部實現,使用者不用管怎麼實現的,會用即可。

3.併發修改異常

一個集合判斷裏面有沒有"world"這個元素,如果有,就添加一個"javaee"元素。

List list = new ArrayList();

list.add("world");

list.add("d");

list.add("e");

Iterator it = list.iterator();

while(it.hasNext()) {

    String str = (String)it.next();

    if(str.equals("world")) {

        list.add("javaee");  //這裏會拋出CncurrentMdificatinExceptin併發修改異常,因爲生成迭代器的時候,只告訴迭代器總共給了幾個元素,所以迭代器本身不知道。

    }

}

解決方法:ListIterator

ListIterator lit = list.listIterator(); //在遍歷的過程中添加元素,可以用ListIterator中的add方法

while(lit.hasNext()) {

    String str = (String)lit.next();

    if(str.equals("world")) {

        lit.add("javaee");  

        //list.add("javaee");

    }

}

對於ListIterator

bolean hasNext()是否有下一個

bolean hasPrevious()是否有前一個

object next()返回下一個元素

object previous();返回上一個元素

五、ArrayList

void add(int index,E element)

E remove(int index)

E get(int index)

E set(int index,E element)

通過size()和get()方法結合使用遍歷。

List list = new ArrayList();

list.add(new Student("張三",18));

list.add(new Student("李四",18));

for(int i = 0; i < list.size(); i++) {

    Student s = (Student)list.get(i);

    System.out.println(s.getName() + "," + s.getAge());

}

六、Vector

public void addElement(E bj)

public E elementAt(int index)

public Enumeratin elements()

Vector的迭代

Vector v = new Vector();                //創建集合對象,List的子類

v.addElement("a");

v.addElement("b");

v.addElement("c");

Enumeration en = v.elements();          //獲取枚舉

while(en.hasMoreElements()) {           //判斷集合中是否有元素

    System.out.println(en.nextElement());//獲取集合中的元素

}

ArrayList:底層數據結構是數組,查詢快,增刪慢。線程不安全,效率高。

Vector:底層數據結構是數組,查詢快,增刪慢。線程安全,效率低。

              Vector相對ArrayList查詢慢(線程安全的)

              Vector相對LinkedList增刪慢(數組結構)

七、LinkedList

LinkedList:底層數據結構是鏈表,查詢慢,增刪快。線程不安全,效率高。

ArrayList和LinkedList的區別

    ArrayList底層是數組結果,查詢和修改快,增刪慢

    LinkedList底層是鏈表結構的,增和刪比較快,查詢和修改慢

共同點:都是線程不安全的

查詢多用ArrayList,增刪多用LinkedList,如果都多ArrayList。

1.LinkedList的特有功能

public void addFirst(E e)及addLast(E e),打印出來的順序是逆序的,因爲不斷在頭加入新元素

public E getFirst()及getLast()

public E removeFirst()及public E removeLast()

public E get(int index);

pubic void add(E e) 順序添加

2.棧和隊列數據結構

棧:先進後出,隊列:先進先出

用LinkedList模擬棧數據結構的集合

public class Stack {

    private LinkedList list = new LinkedList();     //創建LinkedList對象

    public void in(Object obj) {

        list.addLast(obj);               //封裝addLast()方法

    }

    public Object out() {

        return list.removeLast();         //封裝removeLast()方法

    }

    public boolean isEmpty() {

        return list.isEmpty();             //封裝isEmpty()方法

    }

}

八、泛型

1.泛型概述

通過Object轉型問題引入,早期的Object類型可以接收任意的對象類型,但是在實際的使用中,會有類型轉換的問題。也就存在隱患,所以Java提供了泛型來解決這個安全問題。泛型提高安全性(將運行期的錯誤轉換到編譯期),省去強轉的麻煩。例如,如果ArrayList中存儲了true、11、person對象,當想調用person對象時,必須通過轉型,而true無法轉型成person,所以運行期間就會出錯,所以使用泛型將ArrayList限定爲只能輸入person對象。

ArrayList<Person> list = new ArrayList<>();

<>中放的必須是引用數據類型,前後的泛型必須一致,或者後面的泛型可以省略不寫(1.7的新特性菱形泛型)。

2.泛型類

把泛型定義在類上,public class 類名<泛型類型1,…>

泛型類的使用,下面的例子可以將tool類比ArrayList進行理解。當調用Tool類,傳入的Q是car對象,則下面所有的Q都會是car對象。

public class Tool<Q> {

         private Q q;

         public Q getObj() {

            return q;

        }

         public void setObj(Q q) {

            this.q = q;

        }

      #public <泛型類型> 返回類型 方法名(泛型類型 變量名)

       public<T> void show(T t) { //方法泛型最好與類的泛型一致

            System.out.println(t); //如果不一致,需要在方法上聲明該泛型

       }

       //這裏加T,相當於方法有了一個自己的泛型

        public static<W> void print(W w) { //靜態方法必須聲明自己的泛型

             System.out.println(w);

       }這裏是因爲static時候對象並沒有創立,所以並沒有類聲明的泛型

}

3.泛型接口

把泛型定義在接口上:public interface 接口名<泛型類型>

interface Inter<T> {

      public void show(T t);

}

/*class Demo implements Inter<String> { //推薦用這種

        @Override

         public void show(String t) {

                System.out.println(t);

        }

}*/

 

class Demo<T> implements Inter<T> { //沒有必要在實現接口的時候給自己類加泛型,建議用上面的方式

         @Override

         public void show(T t) {

                 System.out.println(t);

         }

}

4.泛型高級之通配符

<?>,任意類型,如果沒有明確,那麼就是Object以及任意的Java類了

? extends E,向下限定,E及其子類

ArrayList<Person> list1 = new ArrayList<>();

list1.add(new Person("張三", 23));

list1.add(new Person("李四", 24));

list1.add(new Person("王五", 25));

ArrayList<Student> list2 = new ArrayList<>();

list2.add(new Student("趙六", 26));

list2.add(new Student("周七", 27));

list1.addAll(list2);因爲student繼承了person,所以可以放進去。

? super E,向上限定,E及其父類

5.可變參數

定義方法的時候不知道該定義多少個參數。

修飾符 返回值類型 方法名(數據類型… 變量名){}

這裏的變量其實是一個數組,如果一個方法有可變參數,並且有多個參數,那麼,可變參數肯定是最後一個,否則都會把後面的參數放在可變數組裏面。

6.增強for循環

for(元素數據類型 變量 : 數組或者Collection集合) {

            使用變量即可,該變量就是元素

}

7.三種迭代的能否刪除

普通for循環,可以刪除,但是索引要--

迭代器,可以刪除,但是必須使用迭代器自身的remove方法,否則會出現併發修改異常

增強for循環不能刪除,因爲底層採用迭代器

九、Arrays工具類

1.asList()

Arrays工具類的asList(T..a)方法的使用,將數組轉換成集合,可以通過集合的方法操作數組,用的就是可變參數。數組轉換成集合,不能對數組進行增加刪除,但是可以使用集合的其他方法。

int[] arr = {11,22,33,44,55};

List<int[]> list = Arrays.asList(arr); 基本數據類型的數組轉換成集合,會將整個數組當作一個對象轉換,容易理解,因爲集合本來存儲的就都是對象。

System.out.println(list); //打印出來的是地址值

如果非要轉換成集合,則需要採用引用數據類型

Integer[] arr = {11,22,33,44,55}; //將數組轉換成集合,數組必須是引用數據類型

List<Integer> list = Arrays.asList(arr);

System.out.println(list);//打印出的是各個元素

2. toArray

Collection中toArray(T[ ] a)泛型版的集合轉數組

ArrayList<String> list = new ArrayList<>();

list.add("a");

list.add("b");

list.add("c");

String[] arr = list.toArray(new String[10]);

當集合轉換數組時,數組長度如果是小於等於集合的size時,轉換後的數組長度等於集合的size,如果數組的長度大於了size,分配的數組長度就和你指定的長度一樣。

十、HashSet

1.存儲對象

HashSet<String> hs = new HashSet<>();

boolean b1 = hs.add("a");

boolean b2 = hs.add("a");   //當存儲不成功的時候,返回false,不能存重複的

System.out.println(b1);

System.out.println(b2);

for(String s : hs) {

      System.out.println(s);

}

2.存儲自定義對象

HashSet<Person> hs = new HashSet<>();

hs.add(new Person("張三", 23));

hs.add(new Person("張三", 23));

hs.add(new Person("李四", 23));

要重寫hashCode()和equals()方法,否則會全部存進去

public int hashCode() {

  int h = hash;

  if (h == 0 && value.length > 0) {

     char val[] = value;

     for (int i = 0; i < value.length; i++) {

           h = 31 * h + val[i];

     }

     hash = h;

  }

  return h;

}

3.HashSet保證元素唯一性的原理

使用Set集合都是需要去掉重複元素的, 如果在存儲的時候逐個equals()比較,效率較低,哈希算法提高了去重複的效率, 降低了使用equals()方法的次數,當HashSet調用add()方法存儲對象的時候,先調用對象的hashCode()方法得到一個哈希值,然後在集合中查找是否有哈希值相同的對象,如果沒有哈希值相同的對象就直接存入集合,如果有哈希值相同的對象, 就和哈希值相同的對象逐個進行equals()比較,比較結果爲false就存入,true則不存。

類中必須重寫hashCode()和equals()方法。

hashCode():屬性相同的對象返回值必須相同, 屬性不同的返回值儘量不同(提高效率)

equals():屬性相同返回true, 屬性不同返回false,返回false的時候存儲

4.LinkedHashSet 

可以保證怎麼存就怎麼取

5.將集合中的重複元素去掉

public static void getSingle(List<String> list) {

    LinkedHashSet<String> lhs = new LinkedHashSet<>();

    lhs.addAll(list);            //將list集合中的所有元素添加到lhs

    list.clear();                   //清空原集合

    list.addAll(lhs);            //將去除重複的元素添回到list中

}

十一、TreeSet

TreeSet是用來排序的,可以指定一個順序,對象存入之後會按照指定的順序排列。

1.自然順序(Comparable)

public class Person implements Comparable<Person>自定義對象需要實現接口,實現Comparable必須要重寫compareTo()方法。

TreeSet類的add()方法中會把存入的對象提升爲Comparable類型,調用對象的compareTo()方法和集合中的對象比較,根據compareTo()方法返回的結果進行存儲

2.比較器順序(Comparator)

創建TreeSet的時候可以制定一個Comparator

TreeSet<String> ts = new TreeSet<>(new CompareByLength());需要傳入比較器

class CompareByLength implements Comparator<String>{

       @Override

       public int compare(String o1, String o2) {

               int i = o1.length()-o2.length();

               return i==0? o1.compareTo(o2):i;

       }

}

如果傳入了Comparator的子類對象,那麼TreeSet就會按照比較器中的順序排序,add()方法內部會自動調用Comparator接口中compare()方法排序。調用的第一個參數是傳入的

3.兩種方式的區別

TreeSet構造函數什麼都不傳,默認按照類中Comparable的順序(沒有就報錯ClassCastException),TreeSet如果傳入Comparator,就優先按照Comparator。

十二、集合框架練習

1.練習

在一個集合中存儲了無序並且重複的字符串,定義一個方法,讓其有序(字典順序),而且還不能去除重複

    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();

        list.add("ccc");

        list.add("ccc");

        list.add("aaa");

        list.add("aaa");

        list.add("bbb");

        list.add("ddd");

        list.add("ddd");

        sort(list); //自定義方法

        System.out.println(list);

    } 

       // Collections.sort(list); 一句話搞定

    public static void sort(List<String> list) {

        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {       //定義比較器(new Comparator(){}是Comparator的子類對象)

            @Override

            public int compare(String s1, String s2) {      //重寫compare方法

                int num = s1.compareTo(s2);              //比較內容

                return num == 0 ? 1 : num;   //如果內容一樣返回一個不爲0的數字即可

            }

        });

        ts.addAll(list);                   //將list集合中的所有元素添加到ts中

        list.clear();                                          //清空list

        list.addAll(ts);           //將ts中排序並保留重複的結果在添加到list中

    }

2.練習

從鍵盤接收一個字符串,程序對其中所有字符進行排序,例如鍵盤輸入:helloitcast,程序打印:acehillostt

Scanner sc = new Scanner(System.in);            //創建鍵盤錄入對象

System.out.println("請輸入一行字符串:");

String line = sc.nextLine();                    //將鍵盤錄入的字符串存儲在line中

char[] arr = line.toCharArray();                //將字符串轉換成字符數組

TreeSet<Character> ts = new TreeSet<>(new Comparator<Character>() {

    @Override

    public int compare(Character c1, Character c2) {

        //int num = c1.compareTo(c2);

        int num = c1 - c2;                  //自動拆箱

        return num == 0 ? 1 : num;

    }

});

for(char c : arr) {

    ts.add(c);

}

for(Character ch : ts) {

    System.out.print(ch);

}

3.練習

程序啓動後,可以從鍵盤輸入接收多個整數, 直到輸入quit時結束輸入,把所有輸入的整數倒序排列打印。

Scanner sc = new Scanner(System.in); //創建鍵盤錄入對象 

System.out.println("請輸入:"); 

TreeSet ts = new TreeSet<>(new Comparator() {//將比較器傳給TreeSet的構造方法

        @Override

        public int compare(Integer i1, Integer i2) {

            //int num = i2 - i1;                    //自動拆箱

            int num = i2.compareTo(i1);

            return num == 0 ? 1 : num;

        }

    });

    while(true) {

        String line = sc.nextLine();  //將鍵盤錄入的字符串存儲在line中

        if("quit".equals(line))   //如果字符串常量和變量比較,常量放前面,這樣不會出現空指針異常,變量裏面可能存儲null

            break;

        try {

            int num = Integer.parseInt(line);       //將數字字符串轉換成數字

            ts.add(num);

        } catch (Exception e) {

            System.out.println("您錄入的數據有誤,請輸入一個整數");

        }

    }

    for (Integer i : ts) {                      //遍歷TreeSet集合

        System.out.println(i);

    }

十三、Map

將鍵映射到值的對象,一個映射不能包含重複的鍵,每個鍵最多隻能映射到一個值。存儲自定義對象時候要重寫hashcode和equal方法。

Map是雙列的,Collection是單列的

Map的鍵唯一,Collection的子體系Set是唯一的

Map集合的數據結構值針對鍵有效,跟值無關;Collection集合的數據結構是針對元素有效

Set底層依賴的是map,只不過是value不顯示

Hashmap

LinkedHashMap:底層是鏈表實現的可以保證怎麼存就怎麼取

TreeMap:類需要實現comparable接口,並實現compareTo方法,或者可以傳入比較器comparator,並重寫compare方法。

1.Map方法

添加功能

V put(K key,V value):添加元素。如果鍵是第一次存儲,就直接存儲元素,返回null,如果鍵不是第一次存在,就用值把以前的值替換掉,返回以前的值

刪除功能

void clear():移除所有的鍵值對元素

V remove(Object key):根據鍵刪除鍵值對元素,並把值返回

判斷功能

boolean containsKey(Object key):判斷集合是否包含指定的鍵

boolean containsValue(Object value):判斷集合是否包含指定的值

boolean isEmpty():集合是否爲空

獲取功能

Set<Map.Entry<K,V>> entrySet():所有鍵值對對象

V get(Object key):根據鍵獲取值

Set keySet():獲取集合中所有鍵的集合

Collection values():獲取集合中所有值的集合

長度功能

int size():返回集合中的鍵值對的個數

2.遍歷

HashMap<String, Integer> hm = new HashMap<>();

hm.put("張三", 23);

hm.put("李四", 24);

hm.put("王五", 25);

hm.put("趙六", 26);

/*Set<String> keySet = hm.keySet();         //獲取集合中所有的鍵

Iterator<String> it = keySet.iterator();    //獲取迭代器

while(it.hasNext()) {                       //判斷單列集合中是否有元素

    String key = it.next();                 //獲取集合中的每一個元素,其實就是雙列集合中的鍵

    Integer value = hm.get(key);            //根據鍵獲取值

    System.out.println(key + "=" + value);  //打印鍵值對

}*/

for(String key : hm.keySet()) {             //增強for循環迭代雙列集合第一種方式

    System.out.println(key + "=" + hm.get(key));

}

或者

for(Entry<String,Integer> en : hm.entrySet()) {

    System.out.println(en.getKey() + "=" + en.getValue());

}

3.HashMap和Hashtable的區別

Hashtable是JDK1.0版本出現的,是線程安全的,效率低,HashMap是JDK1.2版本出現的,是線程不安全的,效率高。Hashtable不可以存儲null鍵和null值,HashMap可以存儲null鍵和null值。

4.Collections

針對集合操作的工具類

public static <T> void sort(List<T> list)

public static <T> int binarySearch(List<?> list,T key)

public static <T> T max(Collection<?> coll)

public static void reverse(List<?> list)

public static void shuffle(List<?> list)

十四、泛型邊界

1.? super E

固定下邊界

class CompareByAge implements Comparator<Student> {

     @Override

     public int compare(Student s1, Student s2) {

         int num = s1.getAge() - s2.getAge();

         return num == 0 ? s1.getName().compareTo(s2.getName()) : num;

     }

}

2.? extends E

固定上邊界

public static void demo1() {

ArrayList<Student> list1 = new ArrayList<>();

list1.add(new Student("張三", 23));

list1.add(new Student("李四", 24));

ArrayList<BaseStudent> list2 = new ArrayList<>();

list2.add(new BaseStudent("王五", 25));

list2.add(new BaseStudent("趙六", 26));

list1.addAll(list2);

Basestudent繼承了student,但是basestudent也可以放進去比較

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