面向對象語言對事物的體現都是以對象的形式,所以為了方便對多個對象的操作,Java就提供了集合類。
集合只用于存儲對象,集合長度是可變的,集合可以存儲不同類型的對象。
Collection 層次結構中的根接口。Collection 表示一組對象,這些對象也稱為 collection 的元素。一些 collection 允許有重復的元素,而另一些則不允許。一些 collection 是有序的,而另一些則是無序的。
有序的 collection(也稱為序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,并搜索列表中的元素。與 set 不同,列表通常允許重復的元素。
底層數據結構是數組,查詢快,增刪慢,線程不安全,效率高。
底層數據結構是數組,查詢快,增刪慢,線程安全,效率低。
底層數據結構是鏈表,查詢慢,增刪快,線程不安全,效率高
方法聲明 | 功能描述 |
---|---|
public void addFirst(E e) | 將指定元素插入此列表的開頭 |
public voidaddLast(E e) | 將指定元素添加到此列表的結尾 |
public E getFirst() | 將指定元素添加到此列表的結尾 |
public E getLast() | 獲取集合的最后一個元素 |
public E removeFirst() | 刪除集合的第一個元素 |
public E removeLast() | 刪除最后一個元素 |
代碼示例:使用LinkedList來模擬一個棧數據結構
package cn.itcast;import java.util.LinkedList;/* *使用LinkedList模擬棧數據結構的集合,并測試 *1、棧的特點先進后出 *2、 LinkedList的特有添加功能addFirst() */class MyStack { PRivate LinkedList link; public MyStack() { link = new LinkedList(); } public void add(Object obj) { // 將指定元素插入此列表的開頭 link.addFirst(obj); } public Object get() { // 移除并返回此列表的第一個元素。 // return link.getFirst(); return link.removeFirst(); } public boolean isEmpty() { return link.isEmpty(); }}/* * MyStack的測試 */public class MyStackDemo { public static void main(String[] args) { // 創建集合對象 MyStack ms = new MyStack(); // 添加元素 ms.add("hello"); ms.add("world"); ms.add("java"); ms.add("android"); ms.add("javase"); while (!ms.isEmpty()) { System.out.println(ms.get()); } }}運行結果:
一個不包含重復元素的 collection,無序。
哈希表確定元素是否相同
1、 判斷的是兩個元素的哈希值是否相同。 如果相同,再判斷兩個對象的內容是否相同。
2、 判斷哈希值相同,其實判斷的是對象的HashCode方法。判斷內容相同,用的是equals方法。
HashSet存儲元素保證唯一性的代碼及圖解:
運行結果:
元素有序唯一,由鏈表保證元素有序,由哈希表保證元素唯一。
使用元素的自然順序對元素進行排序,或者根據創建 set 時提供的 Comparator 進行排序,具體取決于使用的構造方法。
TreeSet是如何保證元素的排序和唯一性 底層數據結構是紅黑樹(紅黑樹是一種自平衡的二叉樹)
TreeSet判斷元素唯一性的方式 就是根據比較方法的返回結果是否是0,是0,就是相同元素,不存。
TreeSet對元素進行排序的方式一 讓元素自身具備比較功能,元素就需要實現Comparable接口,覆蓋compareTo方法。 如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。怎么辦?
可以使用TreeSet集合第二種排序方式 讓集合自身具備比較功能,定義一個類實現Comparator接口,覆蓋compare方法。將該類對象作為參數傳遞給TreeSet集合的構造函數。
TreeSet存儲元素自然排序和唯一的圖解:
運行結果:
迭代:是取出集合中元素的一種方式。
而每一個容器的數據結構不同,所以取出的動作細節也不一樣。但是都具有共性內容: 判斷和取出。那么就可以將這些共性抽取。那么這些內部類都符合一個規則(或者說都抽取出來一個規則)。該規則就是Iterator。通過一個對外提供的方法:iterator();,來獲取集合的取出對象。因為Collection中有iterator方法,所以每一個子類集合對象都具備迭代器。
迭代的常見操作
PS:在迭代時循環中next調用一次,就要hasNext判斷一次。
并發修改異常,原因:迭代器依賴于集合存在,修改集合元素而迭代器卻不知道。
解決方法:
A:迭代器迭代元素,迭代器修改。因為Iterator沒有添加功能,所以使用其子接口ListIterator,元素在迭代元素的后面添加。 B:集合遍歷元素,集合修改元素(普通for和get(index)結合),元素在最后添加
package cn.itcast;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.ListIterator;/* * 問題:有一個集合,如下,請問,我想判斷里面有沒有"world"這個元素,如果有,我就添加一個"javaee"元素,請寫代碼實現。 * * ConcurrentModificationException:當方法檢測到對象的并發修改,但不允許這種修改時,拋出此異常。 * 產生的原因: * 迭代器是依賴于集合而存在的,在判斷成功后,集合的中新添加了元素,而迭代器卻不知道,所以就報錯了,這個錯叫并發修改異常。 * 其實這個問題描述的是:迭代器遍歷元素的時候,通過集合是不能修改元素的。 * 如何解決呢? * A:迭代器迭代元素,迭代器修改元素,元素是跟在剛才迭代的元素后面的。 * B:集合遍歷元素,集合修改元素(普通for),元素在最后添加的。 */public class ListIteratorDemo { public static void main(String[] args) { // 創建List集合對象 List list = new ArrayList(); // 添加元素 list.add("hello"); list.add("world"); list.add("java"); // 迭代器遍歷 // Iterator it = list.iterator(); // while (it.hasNext()) { // String s = (String) it.next(); // if ("world".equals(s)) { // list.add("javaee"); // } // } // 方式1:迭代器迭代元素,迭代器修改元素 // 而Iterator迭代器卻沒有添加功能,所以我們使用其子接口ListIterator ListIterator lit = list.listIterator(); while (lit.hasNext()) { String s = (String) lit.next(); if ("world".equals(s)) { lit.add("javaee"); } } // 方式2:集合遍歷元素,集合修改元素(普通for) for (int x = 0; x < list.size(); x++) { String s = (String) list.get(x); if ("world".equals(s)) { list.add("javaee"); } } System.out.println("list:" + list); }}將鍵映射到值的對象,一個映射不能包含重復的鍵,每個鍵最多只能映射到一個值。其實Map集合中存儲的就是鍵值對。map集合中必須保證鍵的唯一性。
Map接口和Collection接口的不同
Map是雙列的,Collection是單列的Map的鍵唯一,Collection的子體系Set是唯一的Map集合的數據結構值針對鍵有效,跟值無關Collection集合的數據結構是針對元素有效Map常用的子類:
Hashtable:內部結構是哈希表,是同步的。不允許null作為鍵,null作為值。Properties:用來存儲鍵值對型的配置文件的信息,可以和IO技術相結合。HashMap:內部結構式哈希表,不是同步的。允許null作為鍵,null作為值。TreeMap:內部結構式二叉樹,不是同步的。可以對Map結合中的鍵進行排序。HashSet實現Set接口,由哈希表(實際上是一個HashMap實例)支持。方式1:根據鍵找值。獲取所有鍵的集合,遍歷鍵的集合,獲取到每一個鍵,根據鍵找值。
package cn.itcast;import java.util.HashMap;import java.util.Map;import java.util.Set;/* * Map集合的遍歷。 * Map -- 夫妻對 * 思路: * A:把所有的丈夫給集中起來。 * B:遍歷丈夫的集合,獲取得到每一個丈夫。 * C:讓丈夫去找自己的妻子。 * * 轉換: * A:獲取所有的鍵 * B:遍歷鍵的集合,獲取得到每一個鍵 * C:根據鍵去找值 */public class MapDemo { public static void main(String[] args) { // 創建集合對象 Map<String, String> map = new HashMap<String, String>(); // 創建元素并添加到集合 map.put("楊過", "小龍女"); map.put("郭靖", "黃蓉"); map.put("楊康", "穆念慈"); map.put("陳玄風", "梅超風"); // 遍歷 // 獲取所有的鍵 Set<String> set = map.keySet(); // 遍歷鍵的集合,獲取得到每一個鍵 for (String key : set) { // 根據鍵去找值 String value = map.get(key); System.out.println(key + "---" + value); } }}方式2:根據鍵值對對象找鍵和值。
獲取所有鍵值對對象的集合遍歷鍵值對對象的集合,獲取到每一個鍵值對對象根據鍵值對對象找鍵和值package cn.itcast;import java.util.HashMap;import java.util.Map;import java.util.Set;/* * Map集合的遍歷。 * Map -- 夫妻對 * * 思路: * A:獲取所有結婚證的集合 * B:遍歷結婚證的集合,得到每一個結婚證 * C:根據結婚證獲取丈夫和妻子 * * 轉換: * A:獲取所有鍵值對對象的集合 * B:遍歷鍵值對對象的集合,得到每一個鍵值對對象 * C:根據鍵值對對象獲取鍵和值 * * 這里面最麻煩的就是鍵值對對象如何表示呢? * 看看我們開始的一個方法: * Set<Map.Entry<K,V>> entrySet():返回的是鍵值對對象的集合 */public class MapDemo { public static void main(String[] args) { // 創建集合對象 Map<String, String> map = new HashMap<String, String>(); // 創建元素并添加到集合 map.put("楊過", "小龍女"); map.put("郭靖", "黃蓉"); map.put("楊康", "穆念慈"); map.put("陳玄風", "梅超風"); // 獲取所有鍵值對對象的集合 Set<Map.Entry<String, String>> set = map.entrySet(); // 遍歷鍵值對對象的集合,得到每一個鍵值對對象 for (Map.Entry<String, String> me : set) { // 根據鍵值對對象獲取鍵和值 String key = me.getKey(); String value = me.getValue(); System.out.println(key + "---" + value); } }}3、HashMap類概述
鍵是哈希表結構,可以保證鍵的唯一性
4、LinkedHashMap類概述
Map 接口的哈希表和鏈接列表實現,具有可預知的迭代順序。
5、TreeMap類概述
鍵是紅黑樹結構,可以保證鍵的排序和唯一性,自然排序,比較器排序。
6、Map集合的應用及擴展
package cn.itcast;import java.util.Scanner;import java.util.Set;import java.util.TreeMap;/* * 需求 :"aababcabcdabcde",獲取字符串中每一個字母出現的次數要求結果:a(5)b(4)c(3)d(2)e(1) * * 分析: * A:定義一個字符串(可以改進為鍵盤錄入) * B:定義一個TreeMap集合 * 鍵:Character * 值:Integer * C:把字符串轉換為字符數組 * D:遍歷字符數組,得到每一個字符 * E:拿剛才得到的字符作為鍵到集合中去找值,看返回值 * 是null:說明該鍵不存在,就把該字符作為鍵,1作為值存儲 * 不是null:說明該鍵存在,就把值加1,然后重寫存儲該鍵和值 * F:定義字符串緩沖區變量 * G:遍歷集合,得到鍵和值,進行按照要求拼接 * H:把字符串緩沖區轉換為字符串輸出 * * 錄入:linqingxia * 結果:result:a(1)g(1)i(3)l(1)n(2)q(1)x(1) */public class TreeMapDemo { public static void main(String[] args) { // 定義一個字符串(可以改進為鍵盤錄入) Scanner sc = new Scanner(System.in); System.out.println("請輸入一個字符串:"); String line = sc.nextLine(); // 定義一個TreeMap集合 TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>(); // 把字符串轉換為字符數組 char[] chs = line.toCharArray(); // 遍歷字符數組,得到每一個字符 for (char ch : chs) { // 拿剛才得到的字符作為鍵到集合中去找值,看返回值 Integer i = tm.get(ch); // 是null:說明該鍵不存在,就把該字符作為鍵,1作為值存儲 if (i == null) { tm.put(ch, 1); } else { // 不是null:說明該鍵存在,就把值加1,然后重寫存儲該鍵和值 i++; tm.put(ch, i); } } // 定義字符串緩沖區變量 StringBuilder sb = new StringBuilder(); // 遍歷集合,得到鍵和值,進行按照要求拼接 Set<Character> set = tm.keySet(); for (Character key : set) { Integer value = tm.get(key); sb.append(key).append("(").append(value).append(")"); } // 把字符串緩沖區轉換為字符串輸出 String result = sb.toString(); System.out.println("result:" + result); }}示例2:在很多項目中,應用比較多的是一對多的映射關系,這就可以通過嵌套的形式將多個映射定義到一個大的集合中,并將大的集合分級處理,形成一個體系。
package cn.itcast;import java.util.ArrayList;import java.util.HashMap;import java.util.Set;class Student { private String name; private int age; public Student() { super(); } 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; }}/* * 黑馬程序員 * bj 北京校區 * jc 基礎班 * 林青霞 27 * 風清揚 30 * jy 就業班 * 趙雅芝 28 * 武鑫 29 * sh 上海校區 * jc 基礎班 * 郭美美 20 * 犀利哥 22 * jy 就業班 * 羅玉鳳 21 * 馬征 23 * gz 廣州校區 * jc 基礎班 * 王力宏 30 * 李靜磊 32 * jy 就業班 * 郎朗 31 * 柳巖 33 * xa 西安校區 * jc 基礎班 * 范冰冰 27 * 劉意 30 * jy 就業班 * 李冰冰 28 * 張志豪 29 */public class HashMapDemo { public static void main(String[] args) { // 創建大集合 HashMap<String, HashMap<String, ArrayList<Student>>> czbkMap = new HashMap<String, HashMap<String, ArrayList<Student>>>(); // 北京校區數據 HashMap<String, ArrayList<Student>> bjCzbkMap = new HashMap<String, ArrayList<Student>>(); ArrayList<Student> array1 = new ArrayList<Student>(); Student s1 = new Student("林青霞", 27); Student s2 = new Student("風清揚", 30); array1.add(s1); array1.add(s2); ArrayList<Student> array2 = new ArrayList<Student>(); Student s3 = new Student("趙雅芝", 28); Student s4 = new Student("武鑫", 29); array2.add(s3); array2.add(s4); bjCzbkMap.put("基礎班", array1); bjCzbkMap.put("就業班", array2); czbkMap.put("北京校區", bjCzbkMap); // 西安校區數據 HashMap<String, ArrayList<Student>> xaCzbkMap = new HashMap<String, ArrayList<Student>>(); ArrayList<Student> array3 = new ArrayList<Student>(); Student s5 = new Student("范冰冰", 27); Student s6 = new Student("劉意", 30); array3.add(s5); array3.add(s6); ArrayList<Student> array4 = new ArrayList<Student>(); Student s7 = new Student("李冰冰", 28); Student s8 = new Student("張志豪", 29); array4.add(s7); array4.add(s8); xaCzbkMap.put("基礎班", array3); xaCzbkMap.put("就業班", array4); czbkMap.put("西安校區", xaCzbkMap); // 遍歷集合 Set<String> czbkMapSet = czbkMap.keySet(); for (String czbkMapKey : czbkMapSet) { System.out.println(czbkMapKey); HashMap<String, ArrayList<Student>> czbkMapValue = czbkMap .get(czbkMapKey); Set<String> czbkMapValueSet = czbkMapValue.keySet(); for (String czbkMapValueKey : czbkMapValueSet) { System.out.println("/t" + czbkMapValueKey); ArrayList<Student> czbkMapValueValue = czbkMapValue .get(czbkMapValueKey); for (Student s : czbkMapValueValue) { System.out.println("/t/t" + s.getName() + "---" + s.getAge()); } } } }}運行結果:
代碼示例:模擬斗地主洗牌和發牌
package cn.itcast_04;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.TreeSet;/* * 思路: * A:創建一個HashMap集合 * B:創建一個ArrayList集合 * C:創建花色數組和點數數組 * D:從0開始往HashMap里面存儲編號,并存儲對應的牌 * 同時往ArrayList里面存儲編號即可。 * E:洗牌(洗的是編號) * F:發牌(發的也是編號,為了保證編號是排序的,就創建TreeSet集合接收) * G:看牌(遍歷TreeSet集合,獲取編號,到HashMap集合找對應的牌) */public class PokerDemo { public static void main(String[] args) { // 創建一個HashMap集合 HashMap<Integer, String> hm = new HashMap<Integer, String>(); // 創建一個ArrayList集合 ArrayList<Integer> array = new ArrayList<Integer>(); // 創建花色數組和點數數組 // 定義一個花色數組 String[] colors = { "?", "?", "?", "?" }; // 定義一個點數數組 String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2", }; // 從0開始往HashMap里面存儲編號,并存儲對應的牌,同時往ArrayList里面存儲編號即可。 int index = 0; for (String number : numbers) { for (String color : colors) { String poker = color.concat(number); hm.put(index, poker); array.add(index); index++; } } hm.put(index, "小王"); array.add(index); index++; hm.put(index, "大王"); array.add(index); // 洗牌(洗的是編號) Collections.shuffle(array); // 發牌(發的也是編號,為了保證編號是排序的,就創建TreeSet集合接收) TreeSet<Integer> fengQingYang = new TreeSet<Integer>(); TreeSet<Integer> linQingXia = new TreeSet<Integer>(); TreeSet<Integer> liuYi = new TreeSet<Integer>(); TreeSet<Integer> dipai = new TreeSet<Integer>(); for (int x = 0; x < array.size(); x++) { if (x >= array.size() - 3) { diPai.add(array.get(x)); } else if (x % 3 == 0) { fengQingYang.add(array.get(x)); } else if (x % 3 == 1) { linQingXia.add(array.get(x)); } else if (x % 3 == 2) { liuYi.add(array.get(x)); } } // 看牌(遍歷TreeSet集合,獲取編號,到HashMap集合找對應的牌) lookPoker("風清揚", fengQingYang, hm); lookPoker("林青霞", linQingXia, hm); lookPoker("劉意", liuYi, hm); lookPoker("底牌", diPai, hm); } // 寫看牌的功能 public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) { System.out.print(name + "的牌是:"); for (Integer key : ts) { String value = hm.get(key); System.out.print(value + " "); } System.out.println(); }}運行結果:
新聞熱點
疑難解答