Java基礎(10)— 集合框架

集合框架

  • 集合能夠看做是一種容器,用來存儲對象信息。全部集合類都位於java.util包下,但支持多線程的集合類位於java.util.concurrent包下。java

  • 數組與集合的區別node

    • 數組的長度是固定的。集合的長度是可變的
    • 數組中存儲的是同一類型的元素,能夠存儲基本數據類型值。集合存儲的都是對象,並且對象的類型能夠不一致。在開發中通常當對象多的時候,使用集合進行存儲。

Collection家族

  • Collection 接口數組

    截圖

    序號 接口描述
    1 Collection 接口
    Collection 是最基本的集合接口。一個 Collection 表明一組 Object ,即 Collection 元素
    Java不提供直接繼承Collection的類,只提供繼承於它的子接口(如List,Set)
    Collection接口存儲一組不惟一,無序的對象
    2 List 接口
    List 是一個有序的 Collection,此接口可以精準的控制每一個元素插入的位置,能夠經過索引來訪問 List 中的元素
    第一個元素索引爲0,並且容許有相同的元素
    List 接口存儲一組不惟一,有序(插入順序)的對象
    3 Set 接口
    Set 具備與 Collection 徹底同樣的接口,只是行爲上不一樣,Set 不保存重複的元素
    4 SortedSet 接口
    繼承於 Set 的子接口,可是它保存的是惟一有序的集合
    5 Queue 接口
    Queue 就是隊列,隊列的特色是先進先出,一般,不容許隨機訪問元素。
  • Set 與 List 的區別安全

    • Set 接口實例存儲的是無序的,不重複的數據。List 接口實例存儲的是有序的,可重複的元素
    • Set 檢索效率低下,刪除和插入效率高,插入和刪除不會引發元素位置改變 <實現類有HashSet,TreeSet>
    • List和數組相似,能夠動態增加,根據實際存儲的數據的長度自動增加List的長度。查找元素效率高,插入刪除效率低,由於會引發其餘元素位置改變 <實現類有ArrayList,LinkedList,Vector>
  • Collection 實現類(經常使用集合類)數據結構

    序號 類描述
    1 List
    |➡ArrayList
    該類實現了可變大小的數組,爲隨機訪問和遍歷元素提供了更好的性能
    線程不安全的(異步),多線程狀況下不要使用。
    ArrayList 擴容長度 50%,插入刪除效率低。
    底層爲數組結構
    2 List
    |➡Vector
    該類與ArrayList 類很是類似
    線程安全的(同步),多線程狀況下,該類容許默認擴容長度100%
    底層爲數組結構
    3 List/Queue
    |➡LinkedList
    該類沒有同步方法,若是多線程同時訪問要給List,則必須本身實現訪問同步
    解決方法就是建立List時創造一個同步的List = Collections.synchronizedList(newLinkedList(...));
    底層爲雙向鏈表結構
    4 Queue
    |➡ArrayQueue
    該類是一個循環隊列,繼承了AbstractList 抽象類
    底層爲數組結構
    5 Set
    |➡HashSet
    該類不容許出現重複元素,不保證集合中元素的順序,容許包含值爲Null的元素,但最多一個
    底層爲哈希表結構(一個HashMap實例)由數組+鏈表/紅黑樹構成(鏈表長度大於8切換爲紅黑樹)
    6 Set
    |➡TreeSet
    該類實現排序等功能
    底層爲紅黑樹結構
    7 Set
    |➡HashSet
    |➡LinkedHashSet
    該類不只保證了元素的惟一,同時元素存放是由順序的
    底層爲鏈表+哈希表結構
  • 特別說明多線程

    • JDK 1.8以前,哈希表是由 數組+鏈表 實現,即便用鏈表處理衝突,同一hash值的鏈表都存儲在一個鏈表裏。可是當位於一個桶中的元素較多,即hash值相等的元素較多時,經過key值依次查找的效率較低。框架

    • 而 JDK 1.8,哈希表存儲採用數組+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換爲紅黑樹,這樣大大減小了查找時間。異步

      截圖

  • 代碼表現ide

    public class LinkedListDemo{ 
        public static void main(String[] args) { 
            LinkedList<String> link = new LinkedList<String>(); 
            //添加元素 
            link.addFirst("abc1"); 
            link.addFirst("abc2"); 
            link.addFirst("abc3"); 
            System.out.println(link); 
            // 獲取元素 
            System.out.println(link.getFirst()); 
            System.out.println(link.getLast()); 
            // 刪除元素 
            System.out.println(link.removeFirst()); 
            System.out.println(link.removeLast()); 
            while (!link.isEmpty()) { 
                //判斷集合是否爲空 
                System.out.println(link.pop()); //彈出集合中的棧頂元素(彈棧) 
            }
            System.out.println(link); 
        } 
    }
    /*
    輸出結果:
        [abc3, abc2, abc1]
        abc3
        abc1
        abc3
        abc1
        abc2
        []
    */
    public class HashSetDemo { 
        public static void main(String[] args) { 
            //建立 Set集合 
            HashSet<String> set = new HashSet<String>();
            //添加元素 
            set.add(new String("cba")); 
            set.add("abc"); 
            set.add("bac"); 
            set.add("cba"); 
            //遍歷 
            for (String name : set) { 
                System.out.println(name); 
            } 
            System.out.println("==========")
            //建立集合對象 該集合中存儲 Student類型對象
            HashSet<Student> stuSet = new HashSet<Student>(); 
            //存儲 
            Student stu = new Student("于謙", 43); 
            stuSet.add(stu); 
            stuSet.add(new Student("郭德綱", 44)); 
            stuSet.add(new Student("于謙", 43)); 
            stuSet.add(new Student("郭麒麟", 23)); 
            stuSet.add(stu); 
            for (Student stu2 : stuSet) { 
                System.out.println(stu2); 
            }
        } 
    }
    /*
    輸出結果:
    	cba
    	abc
    	bac
    	==========
    	Student [name=郭德綱, age=44] 
    	Student [name=于謙, age=43] 
    	Student [name=郭麒麟, age=23]
    tips:
    	根據結果咱們發現字符串「cba」與Student [name=于謙, age=43]只存儲了一個,也就是說重複的元素set集合不存儲
    */
    public class LinkedHashSetDemo { 
        public static void main(String[] args) { 
            Set<String> set = new LinkedHashSet<String>(); 
            set.add("bbb"); 
            set.add("aaa"); 
            set.add("abc"); 
            set.add("bbc"); 
            Iterator<String> it = set.iterator(); 
            while (it.hasNext()) { 
                System.out.println(it.next()); 
            } 
        } 
    }
    /*
    運行結果
       	bbb
        aaa
        abc
        bbc
    */

Collections工具

  • java.utils.Collections 是集合工具類,用來對集合進行操做工具

    • public static boolean addAll(Collection c, T... elements) ,往集合中添加一些元素
    • public static void shuffle(List<?> list) 打亂順序 ,打亂集合順序
    • public static void sort(List list) ,將集合中元素按照默認規則排序
    • public static void sort(List list,Comparator<? super T> ) ,將集合中元素按照指定規則排序
    public class CollectionsDemo {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList<Integer>(); 
            //原來寫法 
            //list.add(12); 
            //list.add(14); 
            //list.add(15); 
            //list.add(1000); 
            //採用工具類 完成 往集合中添加元素 
            Collections.addAll(list, 5, 222, 1,2); 
            System.out.println(list); 
            //排序方法 
            Collections.sort(list); 
            System.out.println(list); 
        } 
    }
    /*
    運行結果: 
        [5, 222, 1, 2] 
        [1, 2, 5, 222]
    */
    • public static void sort(List list, Comparator<? super T> ) ,將集合中元素按照指定規則排序
  • Comparator比較器

    public class CollectionsDemo3 { 
        public static void main(String[] args) { 
            ArrayList<String> list = new ArrayList<String>(); 
            list.add("cba"); 
            list.add("aba");
    		list.add("sba"); 
            list.add("nba"); 
            //排序方法 按照第一個單詞的降序 
            Collections.sort(list, new Comparator<String>() {
                @Override public int compare(String o1, String o2) {
                    /*
                        兩個對象比較的結果有三種:大於,等於,小於。
                        	若是要按照升序排序,則 o1 小於 o2
                        	若是要按照降序排序,則 o1 小於 o2
                    */
                    return o2.charAt(0) - o1.charAt(0); 
                } 
            }); System.out.println(list); 
        } 
    }
    
    public int compare(String o1, String o2)
    • Comparable 強行對實現它的每一個類的對象進行總體排序。這種排序被稱爲類的天然排序,類的compareTo方法被稱爲它的天然比較方法。只能在類中實現compareTo()一次,不能常常修改類的代碼實現本身想要的排序。實現此接口的對象列表(和數組)能夠經過Collections.sort(和Arrays.sort)進行自動排序,對象能夠用做有序映射中的鍵或有序集合中的元素,無需指定比較器。
    • Comparator 強行對某個對象進行總體排序。能夠將Comparator 傳遞給sort方法(如Collections.sort或 Arrays.sort),從而容許在排序順序上實現精確控制。還可使用Comparator來控制某些數據結構(若有序set或有序映射)的順序,或者爲那些沒有天然順序的對象collection提供排序。
    • 練習例子
    //實體
    public class Student{ 
        private String name; private int age; 
        public Student() { }
        public Student(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; 
        }
        @Override 
        public String toString() {
            return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; 
        } 
    }
    //測試類
    public class Demo { 
        public static void main(String[] args) { 
            // 建立四個學生對象 存儲到集合中 
            ArrayList<Student> list = new ArrayList<Student>(); 
            list.add(new Student("rose",18)); 
            list.add(new Student("jack",16)); 
            list.add(new Student("abc",16)); 
            list.add(new Student("ace",17)); 
            list.add(new Student("mark",16)); 
            /*
            讓學生 按照年齡排序 升序 
            */ 
            // Collections.sort(list);//要求 該list中元素類型 必須實現比較器Comparable接口 
            for (Student student : list) { 
                System.out.println(student); 
            } 
        } 
    }
    /*
    發現
    	當咱們調用Collections.sort()方法的時候程序報錯
    緣由
    	若是想要集合中的元素完成排序,那麼必需要實現比較器Comparable接口、
    */
    public class Student implements Comparable<Student>{ 
        .... 
        @Override 
        public int compareTo(Student o) { 
            return this.age-o.age;//升序 
        } 
    }
    /*
    運行結果
    	Student{name='jack', age=16} 
    	Student{name='abc', age=16} 
    	Student{name='mark', age=16} 
    	Student{name='ace', age=17} 
    	Student{name='rose', age=18}
    */
    • 拓展,若是想在方法中獨立使用定義規則能夠採用Collections.sort(List list,Comparetor c)
    Collections.sort(list, new Comparator<Student>() { 
    	@Override 
        public int compare(Student o1, Student o2) { 
            return o2.getAge()-o1.getAge();//以學生的年齡降序 
        } 
    });
    /*
    上列代碼,效果與上邊輸出同樣
    下列代碼,爲多規則斷定
    */
    Collections.sort(list, new Comparator<Student>() {
        @Override 
        public int compare(Student o1, Student o2) { 
            // 年齡降序 
            int result = o2.getAge()-o1.getAge();
            //年齡降序 
            if(result==0){//第一個規則判斷完了 下一個規則 姓名的首字母 升序 
                result = o1.getName().charAt(0)-o2.getName().charAt(0); 
            }
            return result; 
        } 
    });

Map家族

  • Map接口,存儲一組鍵值對對象,提供key/value的映射

    序號 描述類
    1 Map
    |➡HashMap
    該類是一個散列表,它存儲的內容是鍵值對(key/value)映射
    根據鍵的HashCode值存儲數據,具備很快的訪問速度,最多記錄一條爲Null的鍵,不支持線程同步
    底層爲哈希表結構(數組+鏈表/紅黑樹)
    2 Map
    |➡HashMap
    |➡LinkedHashMap
    底層爲 鏈表 + 哈希表
  • 經常使用方法

    • public V put(K key, V value),把指定的鍵與指定的值添加到Map集合中
    • public V remove(Object key),把指定的鍵 所對應的鍵值對元素 在Map集合中刪除,返回被刪除元素的
    • public V get(Object key) ,根據指定的鍵在Map集合中獲取對應的值
    • public Set keySet(),獲取Map集合中全部的鍵,存儲到Set集合中
    • public Set<Map.Entry<K,V>> entrySet(),獲取到Map集合中全部的鍵值對對象的集合(Set集合)
    public class MapDemo { 
        public static void main(String[] args) { 
            //建立 map對象 
            HashMap<String, String> map = new HashMap<String, String>(); 
            //添加元素到集合 
            map.put("黃曉明", "楊穎"); 
            map.put("文章", "馬伊琍");
            map.put("鄧超", "孫儷"); 
            System.out.println(map); 
            //String remove(String key)
            System.out.println(map.remove("鄧超")); 
            System.out.println(map); 
            // 想要查看 黃曉明的媳婦 是誰 
            System.out.println(map.get("黃曉明")); 
            System.out.println(map.get("鄧超"));
        }
    }
    /*
    tips: 
    使用put方法時,若指定的鍵(key)在集合中沒有,則沒有這個鍵對應的值,返回null,並把指定的鍵值添加到集合中
    若指定的鍵(key)在集合中存在,則返回值爲集合中鍵對應的值(該值爲替換前的值),並把指定鍵所對應的值,替換成指定的新值
    */
  • Map遍歷鍵找值

    public class MapDemo01 { 
        /*
            1.獲取Map中全部的鍵,因爲鍵是惟一的,因此返回一個Set集合存儲全部的鍵。方法提示: keyset() 
            2.遍歷鍵的Set集合,獲得每個鍵。 
            3.根據鍵,獲取鍵所對應的值。方法提示: get(K key) 
        */
        public static void main(String[] args) { 
            //建立Map集合對象 
            HashMap<String, String> map = new HashMap<String,String>(); 
            //添加元素到集合 
            map.put("胡歌", "霍建華"); 
            map.put("郭德綱", "于謙"); 
            map.put("薛之謙", "大張偉"); 
            //獲取全部的鍵 獲取鍵集
            Set<String> keys = map.keySet();
    		// 遍歷鍵集 獲得 每個鍵 
            for (String key : keys) { 
                //key 就是鍵 //獲取對應值 
                String value = map.get(key); 
                System.out.println(key+"的CP是:"+value);
            }
        } 
    }
  • Entry鍵值對對象以及遍歷

    /*
    Map中存放的是兩種對象 key(鍵)/value(值),它們在在Map中是一一對應關係。一組K/V又稱作Map中的一個Entry(項),Entry將鍵值對的對應關係封裝成了對象,即鍵值對對象。這樣咱們在遍歷Map集合時,就能夠從每個鍵值對(Entry)對象中獲取對應的鍵與對應的值。
    方法:
    	public K getKey() :獲取Entry對象中的鍵。 
    	public V getValue() :獲取Entry對象中的值。
    操做步驟:
    	1. 獲取Map集合中,全部的鍵值對(Entry)對象,以Set集合形式返回。方法提示: map.entrySet() 。 
    	2. 遍歷包含鍵值對(Entry)對象的Set集合,獲得每個鍵值對(Entry)對象。
    */
    public class MapDemo02 { 
        public static void main(String[] args) { 
            // 建立Map集合對象 
            HashMap<String, String> map = new HashMap<String,String>(); 
            // 添加元素到集合 
            map.put("胡歌", "霍建華"); 
            map.put("郭德綱", "于謙"); 
            map.put("薛之謙", "大張偉"); 
            // 獲取 全部的 entry對象 
            entrySet Set<Entry<String,String>> entrySet = map.entrySet(); 
            // 遍歷獲得每個entry對象 
            for (Entry<String, String> entry : entrySet) { 
                // 解析 
                String key = entry.getKey(); 
                String value = entry.getValue(); 
                System.out.println(key+"的CP是:"+value); 
            } 
        }
    }
    /*
    tips:Map集合不能直接使用迭代器或者foreach進行遍歷。可是轉成Set以後就可使用了。
    */
  • HashMap存儲自定義類型鍵值,使用map.keySet()方法

    /*
    每位學生(姓名,年齡)都有本身的家庭住址。則將學生對象和家庭住址存儲到 map集合中。學生做爲鍵, 家庭住址做爲值
    */
    public class Student {
        private String name; 
        private int age; 
        public Student() { }
        public Student(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 o) { 
            if (this == o) 
                return true; 
            if (o == null || getClass() != o.getClass()) 
                return false; 
            Student student = (Student) o; 
            return age == student.age && Objects.equals(name, student.name); 
        }
        @Override 
        public int hashCode() { 
            return Objects.hash(name, age); 
        }
    }
    /*map.keySet()方法*/
    public class HashMapTest { 
        public static void main(String[] args) { 
            //1,建立Hashmap集合對象。
            Map<Student,String>map = new HashMap<Student,String>(); 
            //2,添加元素。
            map.put(newStudent("lisi",28), "上海"); 
            map.put(newStudent("wangwu",22), "北京"); 
            map.put(newStudent("zhaoliu",24), "成都");
            map.put(newStudent("zhouqi",25), "廣州");
        	map.put(newStudent("wangwu",22), "南京"); 
            //3,取出元素。鍵找值方式 
            Set<Student> keySet = map.keySet(); 
            for(Student key: keySet){ 
                Stringvalue = map.get(key);
                System.out.println(key.toString()+"....."+value);
            } 
        } 
    }
  • Java 9 ,添加了幾重集合工廠方法,更方便建立少許元素的集合

    public class HelloJDK9 { 
        public static void main(String[] args) { 
            Set<String> str1=Set.of("a","b","c"); 
            //str1.add("c");這裏編譯的時候不會錯,可是執行的時候會報錯,由於是不可變的集合 
            System.out.println(str1); 
            Map<String,Integer> str2=Map.of("a",1,"b",2); 
            System.out.println(str2); 
            List<String> str3=List.of("a","b"); 
            System.out.println(str3); 
        } 
    }
    /*
    兩點注意:
        1.of()方法只是Map,List,Set這三個接口的靜態方法,其父類接口和子類實現並無這類方法,好比 HashSet,ArrayList等待; 
        2.返回的集合是不可變的;
    */

常見的數據結構

數據存儲的經常使用結構有:棧、隊列、數組、鏈表和紅黑樹。咱們分別來了解一下:

  • 棧(stack) ,又稱堆棧,它是運算受限的線性表,其限制是僅容許在標的一端進行插入和刪除操做,不容許在其餘任何位置進行添加、查找、刪除等操做。

  • 簡單的說,採用該結構的集合,對元素的存取有以下的特色

    • 先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,而後才能彈出下面的子彈。
    • 棧的入口、出口的都是棧的頂端位置
  • 這裏兩個名詞須要注意

    • 壓棧,就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
    • 彈棧,就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

隊列

  • 隊列(queue),簡稱隊,它同堆棧同樣,也是一種運算受限的線性表,其限制是僅容許在表的一端進行插入,而在表的另外一端進行刪除。
  • 簡單的說,採用該結構的集合,對元素的存取有以下的特色
    • 先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,小火車過山洞,車頭先進去,車尾後進去;車頭先出來,車尾後出來。
    • 隊列的入口、出口各佔一側

數組

  • 數組(Array),是有序的元素序列,數組是在內存中開闢一段連續的空間,並在此空間存放元素。就像是一排出租屋,有100個房間,從001到100每一個房間都有固定編號,經過編號就能夠快速找到租房子的人。

  • 簡單的說,採用該結構的集合,對元素的存取有以下的特色

    • 查找元素快:經過索引,能夠快速訪問指定位置的元素
    • 增刪元素慢,指定索引位置增長元素 須要建立一個新數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,複製到新數組對應索引的位置。指定索引位置刪除元素,須要建立一個新數組,把原數組元素根據索引,複製到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中

鏈表

  • 鏈表(linked list),由一系列結點node組成(鏈表中每個元素稱爲結點),結點能夠在運行時動態生成。每一個結點包括兩個部分:一個是存儲數據元素的數據域,另外一個是存儲下一個結點地址的指針域。咱們常說的鏈表結構有單向鏈表與雙向鏈表,那麼這裏給你們介紹的是單向鏈表。

  • 簡單的說,採用該結構的集合,對元素的存取有以下的特色

    • 多個結點之間,經過地址進行鏈接。例如,多我的手拉手,每一個人使用本身的右手拉住下我的的左手,依次類推,這樣多我的就連在一塊兒了。
    • 查找元素慢,想查找某個元素,須要經過鏈接的節點,依次向後查找指定元素
    • 增刪元素快,增長元素,只須要修改鏈接下個元素的地址便可。刪除元素,只須要修改鏈接下個元素的地址便可

紅黑樹

  • 二叉樹(binary tree) 是每一個結點不超過2的有序樹(tree)。簡單的理解,就是一種相似於咱們生活中樹的結構,只不過每一個結點上都最多隻能有兩個子結點。二叉樹是每一個節點最多有兩個子樹的樹結構。頂上的叫根結點,兩邊被稱做"左子樹"和"右子樹"

  • 咱們要說的是二叉樹的一種比較有意思的叫作紅黑樹,紅黑樹自己就是一顆二叉查找樹,將節點插入後,該樹仍然是一顆二叉查找樹。也就意味着,樹的鍵值仍然是有序的

  • 紅黑樹的約束

    • 節點能夠是紅色的或者黑色的
    • 根節點是黑色的
    • 葉子節點(特指空節點)是黑色的
    • 每一個紅色節點的子節點都是黑色的
    • 任何一個節點到其每個葉子節點的全部路徑上黑色節點數相同
  • 紅黑樹的特色,速度特別快,趨近平衡樹,查找葉子元素最少和最屢次數很少於二倍

相關文章
相關標籤/搜索