Set集合Set集合概述
java.util.Set介面也稱為Set集合,凡是實現Set介面的類都是Set集合。Set集合的特點是集合中的元素沒有索引,元素不能重複,並且元素的存取無順序。Set集合的常用實現類有java.util.HashSet、java.util.LinkedHashSet和LinkedHashSet
Set集合沒有在Collection介面上新增方法,直接是繼承java.util.Collection介面。Set介面的遍歷只能透過迭代器和增強for迴圈兩種方式遍歷。
HashSet集合java.util.HashSet是Set集合的預設實現,包含了Set集合無序、不重複、無索引的特點
/** * Set集合元素的特點 無索引、存取無順序、不重複 */ @Test public void testHashSetFeature(){ Set<String> stringSet=new HashSet<>(); stringSet.add("跟光磊學Java開發"); stringSet.add("跟光磊學前端開發"); stringSet.add("跟光磊學大資料開發"); stringSet.add("跟光磊學Python開發"); //雖然 跟光磊學Go與區塊鏈開發 重複新增,但是隻會儲存一個 stringSet.add("跟光磊學Go與區塊鏈開發"); stringSet.add("跟光磊學Go與區塊鏈開發"); System.out.println(stringSet); }
程式執行結果
那麼HashSet保證元素唯一是如何實現的?HaseSet使用雜湊表實現
JDK8以前使用資料加連結串列實現雜湊表JDK8以後如果連結串列元素個數沒有超過8個,使用陣列加連結串列。如果連結串列元素超過8個,使用陣列+連結串列+紅黑樹實現。HashSet保證元素唯一是依賴hashCode()和equals()方法,這兩個方法是java.lang.Object,因此所有的物件都有這兩個方法。hasCode()方法是根據物件的地址值計算雜湊值,而equals()方法是比較兩個物件的地址值是否相同。
當HashSet集合儲存元素時呼叫元素的hashCode()方法計算元素的雜湊值然後判斷該雜湊值對應的位置上有沒有相同雜湊值的元素如果該位置上沒有相同雜湊值的元素,那麼就直接儲存如果該位置上有相同雜湊值的元素,那麼就會產生雜湊衝突如果產生了雜湊衝突,那麼就會呼叫該元素的equals()方法與該位置的元素進行一一比較如果該位置上任意一個元素與該元素相等,那麼就不儲存如果該位置上沒有一個元素與該元素相等,那麼就儲存因此在使用HaseSet儲存自定義類(例如這裡的Employee物件)的物件時,需要同時重寫父類Object的equals()和hashCode()方法
/** * 重寫java.lang.Object的equals()方法 * @param object * @return * @see Object#equals(Object) */ @Override public boolean equals(Object object) { //如果兩個物件的地址相等則返回true if(this==object){ return true; } //方法的引數為空或者型別不一樣返回false if(null==object||this.getClass()!=object.getClass()){ return false; } Employee targetEmployee= (Employee) object; //判斷如果員工編號一樣就表示同一個員工 return Objects.equals(this.getEmployeeNo(),targetEmployee.getEmployeeNo()); } @Override public int hashCode() { //根據屬性計算雜湊值 return Objects.hash(employeeNo, name, age); }
重寫equals()和hashCode()方法以後,才能保證使用Set儲存資料時不重複
LinkedHashSet集合java.util.LinkedHashSet集合底層資料結構是連結串列加雜湊表實現,保證資料的儲存有序,但是不重複、無索引。其中使用雜湊表實現了資料的唯一性,使用連結串列保證了資料的有序
/** * LinkedList 元素儲存有序、無索引、不重複 * 由雜湊表保證元素唯一 * 由連結串列保證元素儲存有序 */ @Test public void testLinkedHashSet(){ Set<String> stringSet=new LinkedHashSet<>(); stringSet.add("跟光磊學Java開發"); stringSet.add("跟光磊學前端開發"); stringSet.add("跟光磊學大資料開發"); stringSet.add("跟光磊學Python開發"); //雖然 跟光磊學Go與區塊鏈開發 重複新增,但是隻會儲存一個 stringSet.add("跟光磊學Go與區塊鏈開發"); stringSet.add("跟光磊學Go與區塊鏈開發"); System.out.println(stringSet); }
程式執行結果
TreeSet集合java.util.TreeSet集合的底層依賴TreeMap,資料結構是紅黑樹實現,保證資料唯一,無索引,使用元素的自然排序對元素進行排序,或者根據建立TreeSet時提供的Comparator比較器進行排序,具體取決於使用的構造方法。
/** * TreeSet 元素可排序、無索引、不重複 * 預設按照自然排序 預設升序排序 * 這裡要求要求String實現Comparable介面的compareTo()方法 */ @Test public void testTreeSetDefaultConstructor(){ Set<String> stringSet=new TreeSet<>(); stringSet.add("跟光磊學Java開發"); stringSet.add("跟光磊學前端開發"); stringSet.add("跟光磊學大資料開發"); stringSet.add("跟光磊學Python開發"); //雖然 跟光磊學Go與區塊鏈開發 重複新增,但是隻會儲存一個 stringSet.add("跟光磊學Go與區塊鏈開發"); stringSet.add("跟光磊學Go與區塊鏈開發"); System.out.println(stringSet); }
程式執行結果
TreeSet按照Comparator指定排序規則
/** * TreeSet 元素可排序、無索引、不重複 * 按照Comparator指定排序規則 * */ @Test public void testTreeSetComparatorConstructor(){ Set<String> stringSet=new TreeSet<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { //按照字串的長度降序排序 return o2.length()-o1.length(); } }); stringSet.add("跟光磊學Java開發"); stringSet.add("跟光磊學前端開發"); stringSet.add("跟光磊學大資料開發"); stringSet.add("跟光磊學Python開發"); //雖然 跟光磊學Go與區塊鏈開發 重複新增,但是隻會儲存一個 stringSet.add("跟光磊學Go與區塊鏈開發"); stringSet.add("跟光磊學Go與區塊鏈開發"); System.out.println(stringSet); }
程式執行結果
Map集合Map集合概述Collection集合都是單列集合,即每次儲存單個元素(例如String,Employee),而Map和Collection不同,Map是雙列集合。java.util.Map<K,V>集合是雙列集合的頂級介面,其中K標識限制鍵的型別,V表示限制值得型別。其常用實現類有java.util.HashMap,java.util.LinkeHashMap,java.util.TreeMap。
Map以鍵值對的方式儲存兩個元素,查詢元素時透過鍵找值,因此鍵不能重複,如果鍵重複了,那麼鍵對應得值就會覆蓋。Map集合儲存自定義類的物件時為了保證鍵的唯一性需要重寫equals()和hashCode()方法,但是Map集合的值可以重複
Map集合常用和方法將指定的鍵與值新增到Map集合中 /** * hashMap 無序,鍵不重複 * * <p>V put(K key, V value):將指定的鍵與值新增到Map集合中 */ @Test public void testMapPut() { // 建立Map物件 限制鍵和值的型別都是String Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); System.out.println("往集合中新增四對鍵值對後" + map); System.out.println("Map集合中元素的個數"+map.size()); }
程式執行結果
把指定的鍵所對應的鍵值對元素在Map集合中刪除,返回被刪除的元素 /** * map.remove(Object key)把指定的鍵所對應的鍵值對元素在Map集合中刪除,返回被刪除的元素 */ @Test public void testMapRemove() { Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); System.out.println("往集合中新增四對鍵值對後" + map); String capital = map.remove("法國"); System.out.println("Map集合被刪除的元素的值是" + capital); System.out.println("刪除鍵值對後的集合" + map); }
程式執行結果
根據指定的鍵,在Map集合中獲取對應的值 /** * map.get(Object key)根據指定的鍵,在Map集合中獲取對應的值 */ @Test public void testMapGet() { Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); String capital = map.get("中國"); System.out.println("中國的首都是" + capital); //如果根據鍵找不到值會返回null capital = map.get("美國"); System.out.println("capital = " + capital); }
程式執行結果
4. 判斷該集合中是否有此鍵
/** * map.containsKey(Object) 判斷該集合中是否有此鍵 * map.containsValue(Object) 判斷該集合中是否有指定的值 */ @Test public void testMapContainsKeyContainsValue() { Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); boolean containsKey=map.containsKey("中國"); System.out.println("中國是否存在Map集合中"+containsKey); //鍵不存在返回false containsKey=map.containsKey("美國"); System.out.println("美國是否存在集合中"+containsKey); boolean containsValue=map.containsValue("首爾"); System.out.println("首爾是否存在Map的值中"+containsValue); }
程式執行結果
獲取Map集合所有的鍵,儲存到Set集合中 /** * map.keySet():獲取Map集合所有的鍵,儲存到Set集合中 * map.values():獲取所有的值儲存到Collection集合 */ @Test public void testMapKeySetValueCollection() { Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); Set<String> counties = map.keySet(); System.out.println("獲取Map集合的所有鍵"+counties); Collection<String> capitals=map.values(); System.out.println("獲取Map集合的所有值"+capitals); }
程式執行結果
獲取Map集合所有的鍵值對物件的Set集合/** * 鍵值對包裝後就是鍵值對物件,也就是Map.Entry<K,V> * Entry<K,V>是Map的內部介面,訪問時需要使用Map.Entry<K,V> */ @Test public void testMapEntrySet() { Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); //獲取集合所有的鍵值對 物件 Set<Map.Entry<String, String>> countriesCapitalsSet = map.entrySet(); System.out.println("集合所有鍵值對物件的數量是"+countriesCapitalsSet.size()); // 遍歷 Set<Map.Entry<String, String>> for (Map.Entry<String, String> countryCapitalEntry : countriesCapitalsSet) { //獲取Entry的Key String country=countryCapitalEntry.getKey(); //獲取Entry的Value String capital=countryCapitalEntry.getKey(); System.out.println(country+":"+capital); } }
程式執行結果
Map集合的遍歷方式/** * Map集合遍歷的兩種方式 */ @Test public void testMapIterator(){ Map<String, String> map = new HashMap<>(); map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); // 方式1 // 1.獲取所有的鍵 Set<String> keySet=map.keySet(); //2.遍歷所有的key for (String key : keySet) { //3.根據鍵找值 String value = map.get(key); System.out.println(key+":"+value); } System.out.println("******楚河******漢界******"); //方式2 //1. 獲取所有的鍵值對物件 Set<Map.Entry<String,String>> entries= map.entrySet(); //2.遍歷所有的鍵值對物件 for (Map.Entry<String, String> entry : entries) { //3.根據鍵值對物件獲取鍵和值 System.out.println(entry.getKey()+":"+entry.getValue()); } }
程式執行結果
HashMap在儲存自定義類的物件時,為了保證鍵的唯一性,也需要重寫equals()和hashCode()方法。
HashMap集合HasMap集合的鍵唯一,由雜湊表保證。鍵值對存取無序。
LinkedHashMap集合LinkedHashMap的鍵唯一,由雜湊表保證,鍵值對存取有序,由連結串列保證。
/** * LinkedHashMap 存取元素有序,鍵唯一 */ @Test public void testLinkedHashMap(){ Map<String, String> map = new LinkedHashMap<>(); map.put("中國", "深圳"); //覆蓋上面的元素 map.put("中國", "北京"); map.put("日本", "東京"); map.put("韓國", "首爾"); map.put("法國", "巴黎"); System.out.println("往集合中新增四對鍵值對後" + map); }
程式執行結果
TreeMap集合LinkedHashMap的鍵唯一,由雜湊表保證,鍵值對可排序,由紅黑樹保證。TreeMap集合的排序和TreeSet一樣,預設無參構造器由儲存的元素實現Comparable的compareTo()方法排序
/** * TreeMap 鍵唯一、可以指定鍵的排序方式 * 無參構造器是預設排序,即升序 */ @Test public void testTreeMapDefaultConstructor(){ Map<Integer,String> map=new TreeMap<>(); map.put(5,"伍"); map.put(3,"叄"); map.put(6,"陸"); map.put(9,"玖"); map.put(7,"柒"); System.out.println("往集合中新增5個元素預設升序後"+map); }
程式執行結果
除此以外還可以使用傳入Comparator介面的匿名實現自定義排序
/** * 按照指定規則對鍵進行排序 */ @Test public void testTreeMapComparatorConstructor(){ Map<Integer,String> map=new TreeMap<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1; } }); map.put(5,"伍"); map.put(3,"叄"); map.put(6,"陸"); map.put(9,"玖"); map.put(7,"柒"); System.out.println("往集合中新增5個元素降序排序後"+map); }
程式執行結果
Map集合案例:讀取鍵盤輸入字元並統計個數
實現思路
集合巢狀在進行業務開發時,經常會遇到集合巢狀的情況,集合巢狀就是集合的元素又是一個集合。
例如List巢狀List
/** List巢狀List */ @Test public void testListNestList() { List<String> asia = new ArrayList<>(); asia.add("China"); asia.add("Japan"); asia.add("Korea"); List<String> europe = new ArrayList<>(); europe.add("Britain"); europe.add("Germany"); europe.add("France"); // 將asia和europe兩個集合新增到allCountries集合中 List<List<String>> allCountries = new ArrayList<>(); allCountries.add(asia); allCountries.add(europe); //遍歷List<List<String>> for (List<String> allCountry : allCountries) { //遍歷 List<String> for (String country : allCountry) { System.out.println(country); } } }
程式執行結果
List巢狀Map
/** * List巢狀Map */ @Test public void testListNestMap() { Map<String, String> asia = new LinkedHashMap<>(); //覆蓋上面的元素 asia.put("中國", "北京"); asia.put("日本", "東京"); asia.put("韓國", "首爾"); Map<String, String> europe = new LinkedHashMap<>(); europe.put("英國", "倫敦"); europe.put("德國", "柏林"); europe.put("法國", "巴黎"); //使用List儲存asia和europe兩個Map集合 List<Map<String,String>> countryCapitalInfoList=new ArrayList<>(); countryCapitalInfoList.add(asia); countryCapitalInfoList.add(europe); int count=0; //遍歷List巢狀Map for (Map<String, String> countryCapitalInfo : countryCapitalInfoList) { Set<Map.Entry<String, String>> countryCapitalSet = countryCapitalInfo.entrySet(); for (Map.Entry<String, String> countryCapital: countryCapitalSet) { System.out.println(countryCapital.getKey()+":"+countryCapital.getValue()); } count++; if(count<=countryCapitalInfoList.size()-1){ System.out.println("楚河******漢界******"); } } }
程式執行結果
Map巢狀List
/** * Map巢狀List */ @Test public void testMapNestList() { Map<String,List<String>> map=new HashMap<>(); List<String> chinaOfCity=new ArrayList<>(); chinaOfCity.add("北京"); chinaOfCity.add("上海"); chinaOfCity.add("廣州"); chinaOfCity.add("深圳"); List<String> americanOfCity=new ArrayList<>(); americanOfCity.add("紐約"); americanOfCity.add("舊金山"); americanOfCity.add("芝加哥"); americanOfCity.add("底特律"); map.put("中國",chinaOfCity); map.put("美國",americanOfCity); //遍歷集合 Set<Map.Entry<String, List<String>>> countryOfCitySet = map.entrySet(); for (Map.Entry<String, List<String>> countryOfCity : countryOfCitySet) { System.out.println(countryOfCity.getKey()+":"+countryOfCity.getValue()); } }
程式執行結果
Map巢狀Map
/** * Map巢狀Map */ @Test public void testMapNestMap() { Map<String, String> asia = new LinkedHashMap<>(); //覆蓋上面的元素 asia.put("中國", "北京"); asia.put("日本", "東京"); asia.put("韓國", "首爾"); Map<String, String> europe = new LinkedHashMap<>(); europe.put("英國", "倫敦"); europe.put("德國", "柏林"); europe.put("法國", "巴黎"); Map<String,Map<String,String>> allCountryCapital=new HashMap(); allCountryCapital.put("asia",asia); allCountryCapital.put("europe",europe); //遍歷Map巢狀Map Set<Map.Entry<String, Map<String, String>>> allCountryCapitalEntrySet = allCountryCapital.entrySet(); for (Map.Entry<String, Map<String, String>> allCountryCapitalEntry : allCountryCapitalEntrySet) { System.out.println(allCountryCapitalEntry.getKey()+":"+allCountryCapitalEntry.getValue()); } }
程式執行結果