1. 程式人生 > >Java 基礎 -- 集合框架

Java 基礎 -- 集合框架

集合

1. Java集合框架

1.1 簡介

  • Java類庫中, 集合類的基本介面是: Collection介面和Map介面
  • Collection是元素集合, Map是鍵值對(key-value)
  • Collection介面有兩個基本兩個方法:
    • boolean add(E element); 向集合中新增元素
    • Iterator iterator(); 返回一個迭代器, 用於訪問集合中的物件
  • Iterator介面中有兩個常用方法:
    • E next(); 反覆呼叫可以逐個訪問集合中每個元素
    • boolean hasNext(); 判斷是否還有元素, 用在呼叫next() 方法之前
  • Map介面常用方法:
    • V put(K key, V value);
    • V get(K key);

1.2 Collection常用方法:

  • int size();
  • boolean isEmpty();
  • boolean contains(Object o);
  • boolean containsAll(Collection c);
  • boolean add(Object o);
  • boolean addAll(Collection

1.3 Iterator常用方法

  • boolean hasNext(); 判斷是否還存在可訪問的元素
  • E next(); 返回將要訪問的下一個物件, 如果已經到達集合尾部, 會丟擲: NoSuchElementException
  • void remove(); 刪除上次訪問的物件, 必須緊跟在next()方法後

2. 具體的集合: 主要是以下介面的實現

  • List: 序列
  • Set: 集
  • Map: 字典

2.1 常用的具體集合

  • ArrayList: 可以動態增長和縮減的索引序列
  • LinkedList: 一種可以在任何位置進行高效插入刪除的有序序列
  • HashSet: 沒有重複元素的無序集合
  • TreeSet: 一種有序集
  • HashMap: 一種儲存 鍵值對的資料結構
  • TreeMap: 一種有序排列的鍵值對錶

2.2 List的分析:

  • ArrayList: 基於陣列實現, 陣列存在索引, 所以查詢和更改效率高, 刪除和指定位置新增效率低
  • LinkedList: 基於雙向連結串列實現, 刪除和新增元素效率高, 查詢和更改效率低
  • Vector: 執行緒安全的ArrayList, 在同步操作上會耗費大量時間
  • 示例程式碼:
package cn.kuang.java_primary.collection;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

/**
 * 1. 簡單的建立兩個連結串列, 合併在一起
 *
 * 2. 從第二個連結串列每隔一個元素刪除一個元素
 *
 * 3. 繪製一個迭代器位置示意圖
 *
 * Created by 框 on 2018/6/18.
 */
public class LinkedListTest {

    public static void main(String[] args) {

        // create linkedList a and b
        List<String> a = new LinkedList<>();
        a.add("tom");
        a.add("bob");
        a.add("amy");

        List<String> b = new LinkedList<>();
        b.add("Carl");
        b.add("doug");
        b.add("france");
        b.add("eric");

        System.out.println(a);
        System.out.println(b);

        //merge the words from b into a
        ListIterator<String> aIter = a.listIterator();
        Iterator<String> bIter = b.iterator();

        while (bIter.hasNext()){
            if (aIter.hasNext()) aIter.next();
            aIter.add(bIter.next());
        }

        System.out.println(a);

        //remove every second word from b
        bIter = b.iterator();
        while (bIter.hasNext()){
            bIter.next();
            if (bIter.hasNext()){
                bIter.next();
                bIter.remove();
            }
        }

        System.out.println(b);

        //bulk operation : remove all words in b from a
        a.removeAll(b);
        System.out.println(a);

    }

}

2.3 Set的分析:

  • 不在意元素的順序, 可以快速查詢到元素
  • 散列表: 為每個物件呼叫HashCode返回一個雜湊碼,
  • 常用HashSet, 實現基於散列表的集
  • HashSet特點: 無序, 沒有重複元素
  • 示例程式碼:
package cn.kuang.java_primary.collection;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * 1. 從System.in讀取單詞
 *
 * 2. 把他們新增到HashSet中
 *
 * 3. 遍歷HashSet中的不同單詞
 *
 * 4. 打印出單詞數量
 *
 * Created by 框 on 2018/6/19.
 */
public class SetTest {
    public static void main(String[] args) {

        Set<String> words = new HashSet<>();
        long totalTime = 0;

        try(Scanner in = new Scanner(System.in);) {
            while (in.hasNext()){
                String word = in.next();
                long callTime = System.currentTimeMillis();
                words.add(word);
                callTime = System.currentTimeMillis() - callTime;
                totalTime += callTime;
            }
        }

        Iterator<String> iter = words.iterator();
        for (int i = 0; i < 20 && iter.hasNext(); i++) {
            System.out.println(iter.next());
        }

        System.out.println("......");
        System.out.println(words.size() + " distinct words. " + totalTime + " milliseconds");

    }
}
  • TreeSet: 是一個有序的集合, 使用紅黑樹實現
  • TreeSet特點: 新增元素比HashSet慢, 但是元素有序, 任意兩個元素必須是可比的
  • 示例程式碼:
package cn.kuang.java_primary.collection.pojo;

import java.util.Objects;

/**
 * Item 實體類
 *
 * Created by 框 on 2018/6/19.
 */
public class Item implements Comparable<Item> {

    private String description;
    private int partNumber;

    /**
     * 有參構造器
     * @param description description
     * @param partNumber partNumber
     */
    public Item(String description, int partNumber) {
        this.description = description;
        this.partNumber = partNumber;
    }

    /**
     * Gets of description
     * @return description
     */
    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "Item[" + "description='" + description + '\'' + ", partNumber=" + partNumber + ']';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Item)) return false;

        Item item = (Item) o;

        if (partNumber != item.partNumber) return false;
        return description != null ? description.equals(item.description) : item.description == null;
    }

    @Override
    public int hashCode() {
        return Objects.hash(description, partNumber);
    }

    @Override
    public int compareTo(Item o) {
        int diff = Integer.compare(partNumber, o.partNumber);
        return diff != 0 ? diff : description.compareTo(o.description);
    }
}

--------------------------------------------------------------------------
package cn.kuang.java_primary.collection;

import cn.kuang.java_primary.collection.pojo.Item;

import java.util.Comparator;
import java.util.NavigableSet;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 * 1. 建立兩個Item物件的樹集
 *
 * 2. 第一個按照部件編號排序: Item物件的預設順序
 *
 * 3. 第二個通過一個定製的比較器按照描述資訊排序
 *
 * Created by 框 on 2018/6/19.
 */
public class TreeSetTest {
    public static void main(String[] args) {
        SortedSet<Item> parts = new TreeSet<>();
        parts.add(new Item("Toaster", 1234));
        parts.add(new Item("Widget", 4562));
        parts.add(new Item("Modem", 9912));
        System.out.println(parts);

        NavigableSet<Item> sortByDescription = new TreeSet<>(Comparator.comparing(Item::getDescription));

        sortByDescription.addAll(parts);
        System.out.println(sortByDescription);

    }
}

3. 對映/字典

3.1 基本對映操作

  • Java類庫提供了兩個通用的實現: HashMap和TreeMap,
  • HashMap對鍵進行雜湊, TreeMap用鍵的整體順序對元素進行排序
  • 使用HashMap: 不需要按照排列順序訪問鍵
  • 示例程式碼:
package cn.kuang.java_primary.collection.pojo;

/**
 * Employee實體類
 * Created by 框 on 2018/6/19.
 */
public class Employee {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return  name ;
    }

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }
}
---------------------------------------------------------
package cn.kuang.java_primary.collection;

import cn.kuang.java_primary.collection.pojo.Employee;

import java.util.HashMap;
import java.util.Map;

/**
 * 1. 展示對映的操作過程
 *
 * 2. 將鍵值對新增到對映中
 *
 * 3. 刪除一個鍵, 對應的值也會刪除
 *
 * 4. 修改和檢視某個值
 *
 * Created by 框 on 2018/6/19.
 */
public class MapTest {
    public static void main(String[] args) {
        Map<String, Employee> staff = new HashMap<>();
        staff.put("114-25-5412", new Employee("Amy qw"));
        staff.put("114-25-3234", new Employee("RQs dfg"));
        staff.put("114-25-5818", new Employee("EAQ aas"));
        staff.put("114-25-9172", new Employee("Asd asd"));

        //print staff
        System.out.println(staff);

        //remove an entity by key
        staff.remove("114-25-9172");

        //replace an entity
        staff.put("114-25-3234", new Employee("Alice"));

        //get entity by key
        System.out.println(staff.get("114-25-5818"));

        //iterate through all entities
        staff.forEach(
                (k, v) -> System.out.println("key: " + k + " , value = " + v)
        );

    }
}

3.2 Map常用方法

  • V get();
  • dafault V getOrDefault(Object key, V defaultValue);
  • V put(K key, V value);
  • void putAll(Map

4. 一些其他的問題

4.1 HashMap的排序問題

已知一個HashMap<Integer, User>, 
User有name, age屬性, 
編寫一個方法實現HashMap的排序, 
引數為: HashMap<Integer, User>, 
返回排好序的HashMap, 
排序規則: 按照User的age進行倒序排序, 不得拆散鍵值對
--------------------------------------Class User-----------------------
package cn.kuang.java_primary.collection.pojo;

/**
 * HashMapSort的pojo
 * Created by 框 on 2018/6/19.
 */
public class User {
    private String name;
    private int 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 User() {
    }

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

    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

--------------------------------------Class sort-----------------------
package cn.kuang.java_primary.collection;

import cn.kuang.java_primary.collection.pojo.User;

import java.util.*;

/**
 * 已知一個HashMap<Integer, User>,
 *
 * User有name, age屬性,
 *
 * 編寫一個方法實現HashMap的排序,
 *
 * 引數為: HashMap<Integer, User>,
 *
 * 返回排好序的HashMap,
 *
 * 排序規則: 按照User的age進行倒序排序, 不得拆散鍵值對
 *
 * *************************************************************
 * 思路:
 * 使用HashMap的子類: LinkedHashMap, 他是Map結構, 也是連結串列結構
 *
 * 返回LInkedHashMap即可, 還滿足面向介面程式設計
 *
 * 排序演算法使用JDK中有的API
 *
 * Created by 框 on 2018/6/19.
 */
public class HashMapSort {

    public static void main(String[] args) {
        HashMap<Integer, User> hashMap = new HashMap<>();
        hashMap.put(1, new User("張三", 17));
        hashMap.put(2, new User("李四", 18));
        hashMap.put(4, new User("王二", 19));
        hashMap.put(3, new User("麻子", 20));
        System.out.println("排序前: "+hashMap);

        System.out.println("排序後: "+sortHashMapByUserAge(hashMap));

    }


    public static HashMap<Integer, User> sortHashMapByUserAge(HashMap<Integer, User> map) {
        //首先拿到Map的鍵值對幾個
        Set<Map.Entry<Integer, User>> entrySet = map.entrySet();

        //吧set轉為List集合, 方便實用sort方法
        List<Map.Entry<Integer, User>> list = new ArrayList<>(entrySet);

        //Collections的sort方法進行排序
        Collections.sort(list, new Comparator<Map.Entry<Integer, User>>() {
            @Override
            public int compare(Map.Entry<Integer, User> o1, Map.Entry<Integer, User> o2) {
                //根據User的age進行排序 倒序
                return o2.getValue().getAge() - o1.getValue().getAge();
            }
        });

        //建立一個LinkedHashMap
        LinkedHashMap<Integer, User> linkedHashMap = new LinkedHashMap<>();
        //吧list的資料存進去
        for (Map.Entry<Integer, User> entry : list){
            linkedHashMap.put(entry.getKey(), entry.getValue());
        }
        return linkedHashMap;
    }
}

4.2 集合的安全性問題

  1. Vector 和 HashTable是執行緒安全的, 他們的和新方法都添加了 synchronized關鍵字
  2. ArrayList, HashSet, HashMap是執行緒不安全的
  3. 可以使用以下方法來是的他們變成執行緒安全的
    • Collections.synchronizedCollection(c);
    • Collections.synchronizedList(list);
    • Collections.synchronizedMap(map);
    • Collections.synchronizedSet(set);

4.3 ArrayList內部實現

  • 基於Object陣列實現
  • 提供了空構造器和兩個帶參構造器
    • 構造器1: 引數為int n, 對n進行判斷, 如果n合法, 返回一個長度為 n 的ArrayList
    • 構造器2: 引數為一個Collection, 對collection進行判斷, collection不為空, 則吧這個collection轉為陣列a, 並賦值成成員變數array, 陣列長度作為成員變數size
  • 提供add方法兩個:
    • 首先判斷新增元素後collection容量是否還夠, 不夠就擴容, 夠就新增
    • 未指定位置: 將新元素新增到collection的末尾
    • 指定位置: 判斷位置是否合法, 合法將新元素新增到指定位置, 其後的元素位置往後挪1
  • remove:按照索引來刪除:
    • 首先判斷索引位置是否合法, 合法則將該索引的元素刪除
    • 之後的整體往前移動一位, 將最後一個元素設定為null, 不然容易記憶體洩漏

4.4 併發集合和普通集合問題

  • 併發集合: 常見的:
    • ConcurrentHashMap, ConcurrentLinkedQueue, ConcurrentLinkedDeque
    • 都是位於 java.util.concurrent下,
  • Java中有普通集合, 同步集合, 併發集合
  • 普通集合效率高但是不能保證執行緒安全, 併發可靠
  • 同步集合僅僅新增同步鎖, 嚴重犧牲了效能, 併發效率低
  • 併發集合既保證多執行緒的安全, 又提高了併發效率

4.5 List三個子類特點

  • ArrayList 底層結構是陣列,底層查詢快,增刪慢。
  • LinkedList 底層結構是連結串列型的,增刪快,查詢慢。
  • voctor 底層結構是陣列執行緒安全的,增刪慢,查詢慢。

4.6 List, Map, Set的區別

  • 結構特點:
    • List和set 都是儲存單列資料的集合, Map是儲存鍵值的雙列資料集合
    • List有序可重複, Set無序不重複(位置又元素HashCode決定, 所以不能重複), Map, Key值必須唯一
  • 實現類:
    • List有三個實現類:
      • ArrayList, 陣列實現, 查詢更改快, 增刪慢
      • LinkedList, 雙向連結串列實現, 查詢更改慢, 增刪快
      • Vector, 執行緒安全, 效率低
    • Set實現類:
      • HashSet, 底層是HashMap實現,
      • LinkedHashSet, 繼承HashSet, 基於LinkedHashMap實現
    • Map實現類:
      • HashMap, 執行緒不安全, 高效, 鍵值都支援null
      • HashTable, 執行緒安全, 不高效, 鍵值都不支援null
      • LinkedHashMap, 是HashMap的子類, 儲存插入的順序

4.7 HashMap和HashTable區別

  • HashMap, 執行緒不安全, 高效, 鍵值都支援null
  • HashTable, 執行緒安全, 不高效, 鍵值都不支援null

4.8 陣列和連結串列的適用場景

  • ArrayList 和Vector 使用了陣列的實現,可以認為ArrayList 或者Vector 封裝了對內部陣列的操作,比如向陣列中新增,刪除,插入新的元素或者資料的擴充套件和重定向。
  • LinkedList 使用了迴圈雙向連結串列資料結構。與基於陣列的ArrayList 相比,這是兩種截然不同的實現技術,這也決定了它們將適用於完全不同的工作場景
  • 陣列應用場景:資料比較少;經常做的運算是按序號訪問資料元素;陣列更容易實現,任何高階語言都支援的線性表較穩定。
  • 連結串列應用場景:對線性表的長度或者規模難以估計;頻繁做插入刪除操作;構建動態性比較強的線性表。

4.9 List a = new ArrayList(); 和 ArrayList a = new ArrayList的區別

  • List list = new ArrayList();這句建立了一個ArrayList 的物件後把上溯到了List。此時它是一個List 物件了,有些ArrayList 有但是List 沒有的屬性和方法,它就不能再用了。
  • 而ArrayList list=new ArrayList();建立一物件則保留了ArrayList 的所有屬性。所以需要用到ArrayList 獨有的方法的時候不能用前者。
package cn.kuang.java_primary.collection;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 使用佇列模擬堆疊結構
 *
 * 佇列a, b
 *
 * 思路:
 *
 * 1. 入棧: a, b佇列為空, 將 "a, b, c, d, e" 先放入a中, a中為:  "a, b, c, d, e"
 *
 * 2. 出棧: 將a中元素倒序放入b佇列中, 再將b出列
 *
 * Created by 框 on 2018/6/19.
 */
public class SimulationStack {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>(); //a 佇列
        Queue<String> queue2=new LinkedList<>(); //b佇列
        ArrayList<String> a=new ArrayList<>(); //arrylist集合是中間引數

        //往a佇列新增元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        System.out.print("進棧:");

        //a佇列依次加入list集合之中
        for(String q : queue){
            a.add(q);
            System.out.print(q);
        }

        //以倒序的方法取出(a佇列依次加入list集合)之中的值,加入b對列
        for(int i=a.size()-1;i>=0;i--){
            queue2.offer(a.get(i));
        }

        //打印出棧佇列
        System.out.println("");
        System.out.print("出棧:");

        for (String q : queue2) {
            System.out.print(q);
        }
    }
}

4.12 遍歷問題

Map遍歷:
package cn.kuang.java_primary.collection;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Map 的遍歷方法
 *
 * 1. 獲取所有key來按照key遍歷
 *
 * 2. 通過Map.entrySet使用iterator遍歷key和value
 *
 * 3. 通過Map.entrySet遍歷key和value,推薦,尤其是容量大時
 *
 * 4. 通過Map.values()遍歷所有的value,但不能遍歷key
 *
 * Created by 框 on 2018/6/19.
 */
public class MapTranverse {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "tom");
        map.put(2, "noOn");
        map.put(3, "wha");
        map.put(4, "oepra");
        map.put(5, "power");
        map.put(6, "eric");
        map.put(7, "alic");
        map.put(8, "qwe");

        //1. 獲取key來遍歷
        for (Integer in : map.keySet()) {
            String str = map.get(in);
            System.out.println(in+ " : " +str);
        }

        System.out.println("...................................");

        //2. 通過Map.entrySet使用iterator遍歷key和value
        Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<Integer, String> entry = it.next();
            System.out.println(entry.getKey()+" : "+entry.getValue());
        }

        System.out.println("...................................");

        //3. 通過Map.entrySet遍歷key和value,推薦,尤其是容量大時
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }

        System.out.println("...................................");

        //4. 通過Map.values()遍歷所有的value,但不能遍歷key
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }

    }
}
List遍歷
package cn.kuang.java_primary.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 主要使用for迴圈和迭代器
 *
 * Created by 框 on 2018/6/19.
 */
public class ListTraverse {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList();

        list.add(6);
        list.add(12);
        list.add(4);
        list.add(2);
        list.add(3);
        list.add(5);

        //1. for迴圈遍歷
        for(Iterator iterator = list.iterator(); iterator.hasNext();){
            int i = (Integer) iterator.next();
            System.out.println(i);
        }

        System.out.println(".......................");

        //2. 迭代器
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        System.out.println(".......................");

        //3. 增強for迴圈
        for (int i : list) {
            System.out.println(i);
        }


    }
}