提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔
文章目錄
- 01、Java 集合框架概述
- 1.1、集合框架與數組的對比及概述
- 1.2、集合框架涉及到的API
- 02、Collection接口方法
- 2.1、Collection接口中的常用方法1
- 2.2、Collection接口中的常用方法2
- 2.3、Collection接口中的常用方法3
- 2.4、Collection接口中的常用方法4
- 03、Iterator迭代器接口
- 3.1、使用Iterator遍歷Collection
- 3.2、迭代器Iterator的執行原理
- 3.3、Iterator遍歷集合的兩種錯誤寫法
- 3.4、Iterator迭代器remove()的使用
- 3.5、新特性foreach循環遍歷集合或數組
- 04、Collection子接口之一:List接口
- 4.1、List接口常用實現類的對比
- 4.2、ArrayList的源碼分析
- 4.3、LinkedList的源碼分析
- 4.4、Vector的源碼分析
- 4.5、List接口中的常用方法測試
- 4.6、List的一個面試小題
- 05、Collection子接口之二:Set接口
- 5.1、Set接口實現類的對比
- 5.2、Set的無序性與不可重復性的理解
- 5.3、HashSet中元素的添加過程
- 5.4、關于hashCode()和equals()的重寫
- 5.4.1、重寫hashCode() 方法的基本原則
- 5.4.2、重寫equals() 方法的基本原則
- 5.4.3、Eclipse/IDEA工具里hashCode()的重寫
- 5.5、LinkedHashSet的使用
- 5.6、TreeSet的自然排序
- 5.7、TreeSet的定制排序
- 5.8、TreeSet的課后練習
- 5.9、Set課后兩道面試題
- 06、Map接口
- 6.1、Map接口及其多個實現類的對比
- 6.2、Map中存儲的key-value的特點
- 6.3、Map實現類之一:HashMap
- 6.4、HashMap的底層實現原理
- 6.4.1、HashMap在JDK7中的底層實現原理
- 6.4.2、HashMap在JDK8中的底層實現原理
- 6.7、LinkedHashMap的底層實現原理(了解)
- 6.8、Map中的常用方法1
- 6.9、Map中的常用方法2
- 6.10、TreeMap兩種添加方式的使用
- 6.12、Hashtable
- 6.13、Properties處理屬性文件
- 07、Collections工具類
- 7.1、Collections工具類常用方法的測試
01、Java 集合框架概述
1.1、集合框架與數組的對比及概述
/*** 一、集合的框架** 1.集合、數組都是對多個數據進行存儲操作的結構,簡稱Java容器。* 說明;此時的存儲,主要是指能存層面的存儲,不涉及到持久化的存儲(.txt,.jpg,.avi,數據庫中)** 2.1數組在存儲多個數據封面的特點:* 》一旦初始化以后,它的長度就確定了。* 》數組一旦定義好,它的數據類型也就確定了。我們就只能操作指定類型的數據了。* 比如:String[] arr;int[] str;* 2.2數組在存儲多個數據方面的特點:* 》一旦初始化以后,其長度就不可修改。* 》數組中提供的方法非常有限,對于添加、刪除、插入數據等操作,非常不便,同時效率不高。* 》獲取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用* 》數組存儲數據的特點:有序、可重復。對于無序、不可重復的需求,不能滿足。**/
1.2、集合框架涉及到的API
- Java 集合可分為Collection 和Map 兩種體系
- Collection接口:單列數據,定義了存取一組對象的方法的集合
- List:元素有序、可重復的集合
- Set:元素無序、不可重復的集合
- Map接口:雙列數據,保存具有映射關系“key-value對”的集合
- Collection接口:單列數據,定義了存取一組對象的方法的集合
1、Collection接口繼承樹
2、Map接口繼承樹
/**** 二、集合框架* &---Collection接口:單列集合,用來存儲一個一個的對象* &---List接口:存儲有序的、可重復的數據。 -->“動態”數組* &---ArrayList、LinkedList、Vector** &---Set接口:存儲無序的、不可重復的數據 -->高中講的“集合”* &---HashSet、LinkedHashSet、TreeSet** &---Map接口:雙列集合,用來存儲一對(key - value)一對的數據 -->高中函數:y = f(x)* &---HashMap、LinkedHashMap、TreeMap、Hashtable、Properties**/
02、Collection接口方法
- Collection 接口是List、Set 和Queue 接口的父接口,該接口里定義的方法既可用于操作Set 集合,也可用于操作List 和Queue 集合。
- JDK不提供此接口的任何直接實現,而是提供更具體的子接口(如:Set和List)實現。
- 在Java5 之前,Java 集合會丟失容器中所有對象的數據類型,把所有對象都當成Object 類型處理;從JDK 5.0 增加了泛型以后,Java 集合可以記住容器中對象的數據類型。
2.1、Collection接口中的常用方法1
- 添加
- add(Objec tobj)
- addAll(Collection coll)
- 獲取有效元素的個數
- int size()
- 清空集合
- void clear()
- 是否是空集合
- boolean isEmpty()
- 是否包含某個元素
- boolean contains(Object obj):是通過元素的equals方法來判斷是否是同一個對象
- boolean containsAll(Collection c):也是調用元素的equals方法來比較的。拿兩個集合的元素挨個比較。
- 刪除
- boolean remove(Object obj) :通過元素的equals方法判斷是否是要刪除的那個元素。只會刪除找到的第一個元素
- boolean removeAll(Collection coll):取當前集合的差集
- 取兩個集合的交集
- boolean retainAll(Collection c):把交集的結果存在當前集合中,不影響c
- 集合是否相等
- boolean equals(Object obj)
- 轉成對象數組
- Object[] toArray()
- 獲取集合對象的哈希值
- hashCode()
- 遍歷
- iterator():返回迭代器對象,用于集合遍歷
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;/**** 三、Collection接口中的方法的使用**/
public class CollectionTest {@Testpublic void test1(){Collection coll = new ArrayList();//add(Object e):將元素e添加到集合coll中coll.add("AA");coll.add("BB");coll.add(123); //自動裝箱coll.add(new Date());//size():獲取添加的元素的個數System.out.println(coll.size()); //4//addAll(Collection coll1):將coll1集合中的元素添加到當前的集合中Collection coll1 = new ArrayList();coll1.add(456);coll1.add("CC");coll.addAll(coll1);System.out.println(coll.size()); //6System.out.println(coll);//clear():清空集合元素coll.clear();//isEmpty():判斷當前集合是否為空System.out.println(coll.isEmpty());}
}
2.2、Collection接口中的常用方法2
1、Person類
import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("Person equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age &&Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
2、測試類
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;/*** Collection接口中聲明的方法的測試** 結論:* 向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals().*/
public class CollectinoTest {@Testpublic void test(){Collection coll = new ArrayList();coll.add(123);coll.add(456);// Person p = new Person("Jerry",20);
// coll.add(p);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//1.contains(Object obj):判斷當前集合中是否包含obj//我們在判斷時會調用obj對象所在類的equals()。boolean contains = coll.contains(123);System.out.println(contains);System.out.println(coll.contains(new String("Tam")));
// System.out.println(coll.contains(p));//trueSystem.out.println(coll.contains(new Person("Jerry",20)));//false -->true//2.containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在于當前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));}}
2.3、Collection接口中的常用方法3
1、Person類
import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("Person equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age &&Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
2、測試類
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;/*** Collection接口中聲明的方法的測試** 結論:* 向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals().**/
public class CollectinoTest {@Testpublic void test2(){//3.remove(Object obj):從當前集合中移除obj元素。Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);coll.remove(1234);System.out.println(coll);coll.remove(new Person("Jerry",20));System.out.println(coll);//4. removeAll(Collection coll1):差集:從當前集合中移除coll1中所有的元素。Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);}@Testpublic void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//5.retainAll(Collection coll1):交集:獲取當前集合和coll1集合的交集,并返回給當前集合
// Collection coll1 = Arrays.asList(123,456,789);
// coll.retainAll(coll1);
// System.out.println(coll);//6.equals(Object obj):要想返回true,需要當前集合和形參集合的元素都相同。Collection coll1 = new ArrayList();coll1.add(456);coll1.add(123);coll1.add(new Person("Jerry",20));coll1.add(new String("Tom"));coll1.add(false);System.out.println(coll.equals(coll1));}}
2.4、Collection接口中的常用方法4
1、Person類
import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("Person equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age &&Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;/*** Collection接口中聲明的方法的測試** 結論:* 向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals().**/
public class CollectinoTest {@Testpublic void test4(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//7.hashCode():返回當前對象的哈希值System.out.println(coll.hashCode());//8.集合 --->數組:toArray()Object[] arr = coll.toArray();for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}//拓展:數組 --->集合:調用Arrays類的靜態方法asList()List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});System.out.println(list);List arr1 = Arrays.asList(123, 456);System.out.println(arr1);//[123, 456]List arr2 = Arrays.asList(new int[]{123, 456});System.out.println(arr2.size());//1List arr3 = Arrays.asList(new Integer[]{123, 456});System.out.println(arr3.size());//2//9.iterator():返回Iterator接口的實例,用于遍歷集合元素。放在IteratorTest.java中測試}
}
03、Iterator迭代器接口
- Iterator對象稱為迭代器(設計模式的一種),主要用于遍歷Collection 集合中的元素。
- GOF給迭代器模式的定義為:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。迭代器模式,就是為容器而生。類似于“公交車上的售票員”、“火車上的乘務員”、“空姐”。
- Collection接口繼承了java.lang.Iterable接口,該接口有一個iterator()方法,那么所有實現了Collection接口的集合類都有一個iterator()方法,用以返回一個實現了Iterator接口的對象。
- Iterator 僅用于遍歷集合,Iterator本身并不提供承裝對象的能力。如果需要創建Iterator 對象,則必須有一個被迭代的集合。
- 集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認游標都在集合的第一個元素之前。
3.1、使用Iterator遍歷Collection
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍歷操作,使用迭代器Iterator接口* 內部的方法:hasNext()和 next()**/
public class IteratorTest {@Testpublic void test(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);Iterator iterator = coll.iterator();//方式一:
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// //報異常:NoSuchElementException
// //因為:在調用it.next()方法之前必須要調用it.hasNext()進行檢測。若不調用,且下一條記錄無效,直接調用it.next()會拋出NoSuchElementException異常。
// System.out.println(iterator.next());//方式二:不推薦
// for(int i = 0;i < coll.size();i++){
// System.out.println(iterator.next());
// }//方式三:推薦while(iterator.hasNext()){System.out.println(iterator.next());}}
}
3.2、迭代器Iterator的執行原理
3.3、Iterator遍歷集合的兩種錯誤寫法
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍歷操作,使用迭代器Iterator接口* 1.內部的方法:hasNext()和 next()* 2.集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認游標都在集合的第一個元素之前。*/
public class IteratorTest {@Testpublic void test2(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//錯誤方式一:
// Iterator iterator = coll.iterator();
// while(iterator.next() != null){
// System.out.println(iterator.next());
// }//錯誤方式二://集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認游標都在集合的第一個元素之前。while(coll.iterator().hasNext()){System.out.println(coll.iterator().next());}}
}
3.4、Iterator迭代器remove()的使用
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍歷操作,使用迭代器Iterator接口* 1.內部的方法:hasNext()和 next()* 2.集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認游標都在集合的第一個元素之前。* 3.內部定義了remove(),可以在遍歷的時候,刪除集合中的元素。此方法不同于集合直接調用remove()*/
public class IteratorTest {//測試Iterator中的remove()方法@Testpublic void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//刪除集合中”Tom”//如果還未調用next()或在上一次調用 next 方法之后已經調用了 remove 方法,// 再調用remove都會報IllegalStateException。Iterator iterator = coll.iterator();while(iterator.hasNext()){
// iterator.remove();Object obj = iterator.next();if("Tom".equals(obj)){iterator.remove();
// iterator.remove(); }}//遍歷集合iterator = coll.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
注意:
- Iterator可以刪除集合的元素,但是是遍歷過程中通過迭代器對象的remove方法,不是集合對象的remove方法。
- 如果還未調用next()或在上一次調用next方法之后已經調用了remove方法,再調用remove都會報IllegalStateException。
3.5、新特性foreach循環遍歷集合或數組
- Java 5.0 提供了foreach循環迭代訪問Collection和數組。
- 遍歷操作不需獲取Collection或數組的長度,無需使用索引訪問元素。
- 遍歷集合的底層調用Iterator完成操作。
- foreach還可以用來遍歷數組。
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;/*** jdk 5.0 新增了foreach循環,用于遍歷集合、數組**/
public class ForTest {@Testpublic void test(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//for(集合元素的類型 局部變量 : 集合對象),內部仍然調用了迭代器。for(Object obj : coll){System.out.println(obj);}}@Testpublic void test2(){int[] arr = new int[]{1,2,3,4,5,6};//for(數組元素的類型 局部變量 : 數組對象)for(int i : arr){System.out.println(i);}}//練習題@Testpublic void test3(){String[] arr = new String[]{"SS","KK","RR"};// //方式一:普通for賦值
// for(int i = 0;i < arr.length;i++){
// arr[i] = "HH";
// }//方式二:增強for循環for(String s : arr){s = "HH";}for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}}
}
04、Collection子接口之一:List接口
- 鑒于Java中數組用來存儲數據的局限性,我們通常使用List替代數組
- List集合類中元素有序、且可重復,集合中的每個元素都有其對應的順序索引。
- List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。
- JDK API中List接口的實現類常用的有:ArrayList、LinkedList和Vector。
4.1、List接口常用實現類的對比
/*** 1. List接口框架** |----Collection接口:單列集合,用來存儲一個一個的對象* |----List接口:存儲有序的、可重復的數據。 -->“動態”數組,替換原有的數組* |----ArrayList:作為List接口的主要實現類;線程不安全的,效率高;底層使用Object[] elementData存儲* |----LinkedList:對于頻繁的插入、刪除操作,使用此類效率比ArrayList高;底層使用雙向鏈表存儲* |----Vector:作為List接口的古老實現類;線程安全的,效率低;底層使用Object[] elementData存儲*** 面試題:比較ArrayList、LinkedList、Vector三者的異同?* 同:三個類都是實現了List接口,存儲數據的特點相同:存儲有序的、可重復的數據* 不同:見上**/
4.2、ArrayList的源碼分析
- ArrayList是List 接口的典型實現類、主要實現類
- 本質上,ArrayList是對象引用的一個”變長”數組
/** * 2.ArrayList的源碼分析:* 2.1 jdk 7情況下* ArrayList list = new ArrayList();//底層創建了長度是10的Object[]數組elementData* list.add(123);//elementData[0] = new Integer(123);* ...* list.add(11);//如果此次的添加導致底層elementData數組容量不夠,則擴容。* 默認情況下,擴容為原來的容量的1.5倍,同時需要將原有數組中的數據復制到新的數組中。** 結論:建議開發中使用帶參的構造器:ArrayList list = new ArrayList(int capacity)** 2.2 jdk 8中ArrayList的變化:* ArrayList list = new ArrayList();//底層Object[] elementData初始化為{}.并沒有創建長度為10的數組** list.add(123);//第一次調用add()時,底層才創建了長度10的數組,并將數據123添加到elementData[0]* ...* 后續的添加和擴容操作與jdk 7 無異。* 2.3 小結:jdk7中的ArrayList的對象的創建類似于單例的餓漢式,而jdk8中的ArrayList的對象* 的創建類似于單例的懶漢式,延遲了數組的創建,節省內存。* */
4.3、LinkedList的源碼分析
- 對于頻繁的插入或刪除元素的操作,建議使用LinkedList類,效率較高
- LinkedList:雙向鏈表,內部沒有聲明數組,而是定義了Node類型的first和last,用于記錄首末元素。同時,定義內部類Node,作為LinkedList中保存數據的基本結構。
/*** 3.LinkedList的源碼分析:* LinkedList list = new LinkedList(); 內部聲明了Node類型的first和last屬性,默認值為null* list.add(123);//將123封裝到Node中,創建了Node對象。** 其中,Node定義為:體現了LinkedList的雙向鏈表的說法* private static class Node<E> {* E item;* Node<E> next;* Node<E> prev;** Node(Node<E> prev, E element, Node<E> next) {* this.item = element;* this.next = next; //next變量記錄下一個元素的位置* this.prev = prev; //prev變量記錄前一個元素的位置* }* }*/
4.4、Vector的源碼分析
- Vector 是一個古老的集合,JDK1.0就有了。大多數操作與ArrayList相同,區別之處在于Vector是線程安全的。
- 在各種list中,最好把ArrayList作為缺省選擇。當插入、刪除頻繁時,使用LinkedList;Vector總是比ArrayList慢,所以盡量避免使用。
/** * 4.Vector的源碼分析:jdk7和jdk8中通過Vector()構造器創建對象時,底層都創建了長度為10的數組。* 在擴容方面,默認擴容為原來的數組長度的2倍。*/
4.5、List接口中的常用方法測試
List除了從Collection集合繼承的方法外,List 集合里添加了一些根據索引來操作集合元素的方法。
- void add(intindex, Object ele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素添加進來
- Object get(int index):獲取指定index位置的元素
- int indexOf(Object obj):返回obj在集合中首次出現的位置
- int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):設置指定index位置的元素為ele
- List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;/**** 5.List接口的常用方法*/
public class ListTest {/**** void add(int index, Object ele):在index位置插入ele元素* boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素添加進來* Object get(int index):獲取指定index位置的元素* int indexOf(Object obj):返回obj在集合中首次出現的位置* int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置* Object remove(int index):移除指定index位置的元素,并返回此元素* Object set(int index, Object ele):設置指定index位置的元素為ele* List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的子集合** 總結:常用方法* 增:add(Object obj)* 刪:remove(int index) / remove(Object obj)* 改:set(int index, Object ele)* 查:get(int index)* 插:add(int index, Object ele)* 長度:size()* 遍歷:① Iterator迭代器方式* ② 增強for循環* ③ 普通的循環**/@Testpublic void test3(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");//方式一:Iterator迭代器方式Iterator iterator = list.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("***************");//方式二:增強for循環for(Object obj : list){System.out.println(obj);}System.out.println("***************");//方式三:普通for循環for(int i = 0;i < list.size();i++){System.out.println(list.get(i));}}@Testpublic void tets2(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");list.add(new Person("Tom",12));list.add(456);//int indexOf(Object obj):返回obj在集合中首次出現的位置。如果不存在,返回-1.int index = list.indexOf(4567);System.out.println(index);//int lastIndexOf(Object obj):返回obj在當前集合中末次出現的位置。如果不存在,返回-1.System.out.println(list.lastIndexOf(456));//Object remove(int index):移除指定index位置的元素,并返回此元素Object obj = list.remove(0);System.out.println(obj);System.out.println(list);//Object set(int index, Object ele):設置指定index位置的元素為elelist.set(1,"CC");System.out.println(list);//List subList(int fromIndex, int toIndex):返回從fromIndex到toIndex位置的左閉右開區間的子集合List subList = list.subList(2, 4);System.out.println(subList);System.out.println(list);}@Testpublic void test(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");list.add(new Person("Tom",12));list.add(456);System.out.println(list);//void add(int index, Object ele):在index位置插入ele元素list.add(1,"BB");System.out.println(list);//boolean addAll(int index, Collection eles):從index位置開始將eles中的所有元素添加進來List list1 = Arrays.asList(1, 2, 3);list.addAll(list1);
// list.add(list1);System.out.println(list.size());//9//Object get(int index):獲取指定index位置的元素System.out.println(list.get(2));}
}
4.6、List的一個面試小題
1、面試題1
請問ArrayList/LinkedList/Vector的異同?談談你的理解?ArrayList底層是什么?擴容機制?Vector和ArrayList的最大區別?
/*** 請問ArrayList/LinkedList/Vector的異同?談談你的理解?* ArrayList底層是什么?擴容機制?Vector和ArrayList的最大區別?* * ArrayList和LinkedList的異同二者都線程不安全,相對線程安全的Vector,執行效率高。* 此外,ArrayList是實現了基于動態數組的數據結構,LinkedList基于鏈表的數據結構。* 對于隨機訪問get和set,ArrayList覺得優于LinkedList,因為LinkedList要移動指針。* 對于新增和刪除操作add(特指插入)和remove,LinkedList比較占優勢,因為ArrayList要移動數據。* * ArrayList和Vector的區別Vector和ArrayList幾乎是完全相同的,* 唯一的區別在于Vector是同步類(synchronized),屬于強同步類。* 因此開銷就比ArrayList要大,訪問要慢。正常情況下,* 大多數的Java程序員使用ArrayList而不是Vector,* 因為同步完全可以由程序員自己來控制。Vector每次擴容請求其大小的2倍空間,* 而ArrayList是1.5倍。Vector還有一個子類Stack。*/
2、面試題2
import org.junit.Test;import java.util.ArrayList;
import java.util.List;public class ListEver {/*** 區分List中remove(int index)和remove(Object obj)*/@Testpublic void testListRemove() {List list = new ArrayList();list.add(1);list.add(2);list.add(3);updateList(list);System.out.println(list);//}private void updateList(List list) {
// list.remove(2);list.remove(new Integer(2));}
}
05、Collection子接口之二:Set接口
- Set接口是Collection的子接口,set接口沒有提供額外的方法
- Set 集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個Set 集合中,則添加操作失敗。
- Set 判斷兩個對象是否相同不是使用== 運算符,而是根據equals() 方法
5.1、Set接口實現類的對比
/*** 1.Set接口的框架:* |----Collection接口:單列集合,用來存儲一個一個的對象* |----Set接口:存儲無序的、不可重復的數據 -->高中講的“集合”* |----HashSet:作為Set接口的主要實現類;線程不安全的;可以存儲null值* |----LinkedHashSet:作為HashSet的子類;遍歷其內部數據時,可以按照添加的順序遍歷* 對于頻繁的遍歷操作,LinkedHashSet效率高于HashSet.* |----TreeSet:可以按照添加對象的指定屬性,進行排序。*/
5.2、Set的無序性與不可重復性的理解
1、測試類
import org.junit.Test;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;/**** 1.Set接口中沒有定義額外的方法,使用的都是Collection中聲明過的方法。**/
public class SetTest {/*** 一、Set:存儲無序的、不可重復的數據* 1.無序性:不等于隨機性。存儲的數據在底層數組中并非按照數組索引的順序添加,而是根據數據的哈希值決定的。** 2.不可重復性:保證添加的元素按照equals()判斷時,不能返回true.即:相同的元素只能添加一個。** 二、添加元素的過程:以HashSet為例:***/@Testpublic void test(){Set set = new HashSet();set.add(123);set.add(456);set.add("fgd");set.add("book");set.add(new User("Tom",12));set.add(new User("Tom",12));set.add(129);Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
2、User類
public class User{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}
}
5.3、HashSet中元素的添加過程
- HashSet是Set 接口的典型實現,大多數時候使用Set 集合時都使用這個實現類。
- HashSet按Hash 算法來存儲集合中的元素,因此具有很好的存取、查找、刪除性能。
- HashSet具有以下特點:不能保證元素的排列順序
- HashSet不是線程安全的
- 集合元素可以是null
- 底層也是數組,初始容量為16,當如果使用率超過0.75,(16*0.75=12)就會擴大容量為原來的2倍。(16擴容為32,依次為64,128…等)
- HashSet 集合判斷兩個元素相等的標準:兩個對象通過hashCode() 方法比較相等,并且兩個對象的equals() 方法返回值也相等。
- 對于存放在Set容器中的對象,對應的類一定要重寫equals()和hashCode(Object obj)方法,以實現對象相等規則。即:“相等的對象必須具有相等的散列碼”。
/*** 一、Set:存儲無序的、不可重復的數據* 1.無序性:不等于隨機性。存儲的數據在底層數組中并非按照數組索引的順序添加,而是根據數據的哈希值決定的。** 2.不可重復性:保證添加的元素按照equals()判斷時,不能返回true.即:相同的元素只能添加一個。** 二、添加元素的過程:以HashSet為例:* 我們向HashSet中添加元素a,首先調用元素a所在類的hashCode()方法,計算元素a的哈希值,* 此哈希值接著通過某種算法計算出在HashSet底層數組中的存放位置(即為:索引位置),判斷* 數組此位置上是否已經有元素:* 如果此位置上沒有其他元素,則元素a添加成功。 --->情況1* 如果此位置上有其他元素b(或以鏈表形式存在的多個元素),則比較元素a與元素b的hash值:* 如果hash值不相同,則元素a添加成功。--->情況2* 如果hash值相同,進而需要調用元素a所在類的equals()方法:* equals()返回true,元素a添加失敗* equals()返回false,則元素a添加成功。--->情況2** 對于添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上數據以鏈表的方式存儲。* jdk 7 :元素a放到數組中,指向原來的元素。* jdk 8 :原來的元素在數組中,指向元素a* 總結:七上八下** HashSet底層:數組+鏈表的結構。**/
5.4、關于hashCode()和equals()的重寫
5.4.1、重寫hashCode() 方法的基本原則
- 在程序運行時,同一個對象多次調用hashCode() 方法應該返回相同的值。
- 當兩個對象的equals() 方法比較返回true 時,這兩個對象的hashCode() 方法的返回值也應相等。
- 對象中用作equals() 方法比較的Field,都應該用來計算hashCode 值。
5.4.2、重寫equals() 方法的基本原則
以自定義的Customer類為例,何時需要重寫equals()?
- 當一個類有自己特有的“邏輯相等”概念,當改寫equals()的時候,總是要改寫hashCode(),根據一個類的equals方法(改寫后),兩個截然不同的實例有可能在邏輯上是相等的,但是,根據Object.hashCode()方法,它們僅僅是兩個對象。
- 因此,違反了“相等的對象必須具有相等的散列碼”。
- 結論:復寫equals方法的時候一般都需要同時復寫hashCode方法。通常參與計算hashCode的對象的屬性也應該參與到equals()中進行計算。
5.4.3、Eclipse/IDEA工具里hashCode()的重寫
以Eclipse/IDEA為例,在自定義類中可以調用工具自動重寫equals和hashCode。問題:為什么用Eclipse/IDEA復寫hashCode方法,有31這個數字?
- 選擇系數的時候要選擇盡量大的系數。因為如果計算出來的hash地址越大,所謂的“沖突”就越少,查找起來效率也會提高。(減少沖突)
- 并且31只占用5bits,相乘造成數據溢出的概率較小。
- 31可以由i*31== (i<<5)-1來表示,現在很多虛擬機里面都有做相關優化。(提高算法效率)
- 31是一個素數,素數作用就是如果我用一個數字來乘以這個素數,那么最終出來的結果只能被素數本身和被乘數還有1來整除!(減少沖突)
/*** 2.要求:向Set(主要指:HashSet、LinkedHashSet)中添加的數據,其所在的類一定要重寫hashCode()和equals()* 要求:重寫的hashCode()和equals()盡可能保持一致性:相等的對象必須具有相等的散列碼* 重寫兩個方法的小技巧:對象中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值。*/
5.5、LinkedHashSet的使用
- LinkedHashSet是HashSet的子類
- LinkedHashSet根據元素的hashCode值來決定元素的存儲位置,但它同時使用雙向鏈表維護元素的次序,這使得元素看起來是以插入順序保存的。
- LinkedHashSet插入性能略低于HashSet,但在迭代訪問Set 里的全部元素時有很好的性能。
- LinkedHashSet不允許集合元素重復。
1、測試類
import org.junit.Test;import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;public class SetTest {/*** LinkedHashSet的使用* LinkedHashSet作為HashSet的子類,在添加數據的同時,每個數據還維護了兩個引用,記錄此數據前一個* 數據和后一個數據。* 優點:對于頻繁的遍歷操作,LinkedHashSet效率高于HashSet*/@Testpublic void test2(){Set set = new LinkedHashSet();set.add(456);set.add(123);set.add(123);set.add("AA");set.add("CC");set.add(new User("Tom",12));set.add(new User("Tom",12));set.add(129);Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
public class User{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { //return name.hashCode() + age;int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}
}
5.6、TreeSet的自然排序
- TreeSet是SortedSet接口的實現類,TreeSet可以確保集合元素處于排序狀態。
- TreeSet底層使用紅黑樹結構存儲數據
- 新增的方法如下:(了解)
- Comparator comparator()
- Object first()
- Object last()
- Object lower(Object e)
- Object higher(Object e)
- SortedSet subSet(fromElement, toElement)
- SortedSet headSet(toElement)
- SortedSet tailSet(fromElement)
- TreeSet兩種排序方法:自然排序和定制排序。默認情況下,TreeSet采用自然排序。
- TreeSet和后面要講的TreeMap采用紅黑樹的存儲結構
- 特點:有序,查詢速度比List快
- 自然排序:TreeSet會調用集合元素的compareTo(Object obj) 方法來比較元素之間的大小關系,然后將集合元素按升序(默認情況)排列。
- 如果試圖把一個對象添加到TreeSet時,則該對象的類必須實現Comparable 接口。
- 實現Comparable 的類必須實現compareTo(Object obj) 方法,兩個對象即通過compareTo(Object obj) 方法的返回值來比較大小。
- Comparable 的典型實現:
- BigDecimal、BigInteger 以及所有的數值型對應的包裝類:按它們對應的數值大小進行比較
- Character:按字符的unicode值來進行比較
- Boolean:true 對應的包裝類實例大于false 對應的包裝類實例
- String:按字符串中字符的unicode 值進行比較
- Date、Time:后邊的時間、日期比前面的時間、日期大
- 向TreeSet中添加元素時,只有第一個元素無須比較compareTo()方法,后面添加的所有元素都會調用compareTo()方法進行比較。
- 因為只有相同類的兩個實例才會比較大小,所以向TreeSet中添加的應該是同一個類的對象。
- 對于TreeSet集合而言,它判斷兩個對象是否相等的唯一標準是:兩個對象通過compareTo(Object obj) 方法比較返回值。
- 當需要把一個對象放入TreeSet中,重寫該對象對應的equals() 方法時,應保證該方法與compareTo(Object obj) 方法有一致的結果:如果兩個對象通過equals() 方法比較返回true,則通過compareTo(Object obj) 方法比較應返回0。否則,讓人難以理解。
紅黑樹
1、測試類
import org.junit.Test;import java.util.Iterator;
import java.util.TreeSet;/*** 1.向TreeSet中添加的數據,要求是相同類的對象。* 2.兩種排序方式:自然排序(實現Comparable接口) 和 定制排序(Comparator)* 3.自然排序中,比較兩個對象是否相同的標準為:compareTo()返回0.不再是equals().* 4.定制排序中,比較兩個對象是否相同的標準為:compare()返回0.不再是equals().*/
public class TreeSetTest {@Testpublic void test() {TreeSet set = new TreeSet();//失敗:不能添加不同類的對象
// set.add(123);
// set.add(456);
// set.add("AA");
// set.add(new User("Tom",12));//舉例一:
// set.add(34);
// set.add(-34);
// set.add(43);
// set.add(11);
// set.add(8);//舉例二:set.add(new User("Tom",12));set.add(new User("Jerry",32));set.add(new User("Jim",2));set.add(new User("Mike",65));set.add(new User("Jack",33));set.add(new User("Jack",56));Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
2、User類
public class User implements Comparable{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { //return name.hashCode() + age;int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if (o instanceof User) {User user = (User) o;
// return this.name.compareTo(user.name); //按照姓名從小到大排列
// return -this.name.compareTo(user.name); //按照姓名從大到小排列int compare = -this.name.compareTo(user.name); //按照姓名從大到小排列if(compare != 0){ //年齡從小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException("輸入的類型不匹配");}}
}
5.7、TreeSet的定制排序
- TreeSet的自然排序要求元素所屬的類實現Comparable接口,如果元素所屬的類沒有實現Comparable接口,或不希望按照升序(默認情況)的方式排列元素或希望按照其它屬性大小進行排序,則考慮使用定制排序。定制排序,通過Comparator接口來實現。需要重寫compare(T o1,T o2)方法。
- 利用int compare(T o1,T o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大于o2;如果返回0,表示相等;返回負整數,表示o1小于o2。
- 要實現定制排序,需要將實現Comparator接口的實例作為形參傳遞給TreeSet的構造器。
- 此時,仍然只能向TreeSet中添加類型相同的對象。否則發生ClassCastException異常。
- 使用定制排序判斷兩個元素相等的標準是:通過Comparator比較兩個元素返回了0。
1、測試類
import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;/*** 1.向TreeSet中添加的數據,要求是相同類的對象。* 2.兩種排序方式:自然排序(實現Comparable接口) 和 定制排序(Comparator)* 3.自然排序中,比較兩個對象是否相同的標準為:compareTo()返回0.不再是equals().* 4.定制排序中,比較兩個對象是否相同的標準為:compare()返回0.不再是equals().*/
public class TreeSetTest {@Testpublic void tets2(){Comparator com = new Comparator() {//按照年齡從小到大排列@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof User && o2 instanceof User){User u1 = (User)o1;User u2 = (User)o2;return Integer.compare(u1.getAge(),u2.getAge());}else{throw new RuntimeException("輸入的數據類型不匹配");}}};TreeSet set = new TreeSet(com);set.add(new User("Tom",12));set.add(new User("Jerry",32));set.add(new User("Jim",2));set.add(new User("Mike",65));set.add(new User("Mary",33));set.add(new User("Jack",33));set.add(new User("Jack",56));Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
2、User類
public class User implements Comparable{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { //return name.hashCode() + age;int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if (o instanceof User) {User user = (User) o;
// return this.name.compareTo(user.name); //按照姓名從小到大排列
// return -this.name.compareTo(user.name); //按照姓名從大到小排列int compare = -this.name.compareTo(user.name); //按照姓名從大到小排列if(compare != 0){ //年齡從小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException("輸入的類型不匹配");}}
}
2、User類
public class User implements Comparable{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { //return name.hashCode() + age;int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if (o instanceof User) {User user = (User) o;
// return this.name.compareTo(user.name); //按照姓名從小到大排列
// return -this.name.compareTo(user.name); //按照姓名從大到小排列int compare = -this.name.compareTo(user.name); //按照姓名從大到小排列if(compare != 0){ //年齡從小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException("輸入的類型不匹配");}}
}
5.8、TreeSet的課后練習
1、MyDate類
/*** MyDate類包含:* private成員變量year,month,day;并為每一個屬性定義getter, setter 方法;*/
public class MyDate implements Comparable{private int year;private int month;private int day;public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}public MyDate() {}public MyDate(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}@Overridepublic String toString() {return "MyDate{" +"year=" + year +", month=" + month +", day=" + day +'}';}@Overridepublic int compareTo(Object o) {if(o instanceof MyDate){MyDate m = (MyDate)o;//比較年int minusYear = this.getYear() - m.getYear();if(minusYear != 0){return minusYear;}//比較月int minusMonth = this.getMonth() - m.getMonth();if(minusMonth != 0){return minusMonth;}//比較日return this.getDay() - m.getDay();}throw new RuntimeException("傳入的數據類型不一致!");}
}
2、Employee類
/*** 定義一個Employee類。* 該類包含:private成員變量name,age,birthday,* 其中birthday 為MyDate 類的對象;* 并為每一個屬性定義getter, setter 方法;* 并重寫toString 方法輸出name, age, birthday*/
public class Employee implements Comparable{private String name;private int age;private MyDate birthday;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 MyDate getBirthday() {return birthday;}public void setBirthday(MyDate birthday) {this.birthday = birthday;}public Employee() {}public Employee(String name, int age, MyDate birthday) {this.name = name;this.age = age;this.birthday = birthday;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +", birthday=" + birthday +'}';}//按name排序@Overridepublic int compareTo(Object o){if(o instanceof Employee){Employee e = (Employee)o;return this.name.compareTo(e.name);}
// return 0;throw new RuntimeException("傳入的數據類型不一致");}
}
3、測試類
import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;/*** 創建該類的5 個對象,并把這些對象放入TreeSet 集合中* (下一章:TreeSet 需使用泛型來定義)分別按以下兩種方式* 對集合中的元素進行排序,并遍歷輸出:** 1). 使Employee 實現Comparable 接口,并按name 排序* 2). 創建TreeSet 時傳入Comparator對象,按生日日期的先后排序。*/
public class EmployeeTest {//問題二:按生日日期的先后排序@Testpublic void test2(){TreeSet set = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Employee && o2 instanceof Employee){Employee e1 = (Employee)o1;Employee e2 = (Employee)o2;MyDate b1 = e1.getBirthday();MyDate b2 = e2.getBirthday();//方式一:
// //比較年
// int minusYear = b1.getYear() - b2.getYear();
// if(minusYear != 0){
// return minusYear;
// }
//
// //比較月
// int minusMonth = b1.getMonth() - b2.getMonth();
// if(minusMonth != 0){
// return minusMonth;
// }
//
// //比較日
// return b1.getDay() - b2.getDay();//方式二:return b1.compareTo(b2);}
// return 0;throw new RuntimeException("傳入的數據類型不一致!");}});Employee e1 = new Employee("wangxianzhi",41,new MyDate(334,5,4));Employee e2 = new Employee("simaqian",43,new MyDate(-145,7,12));Employee e3 = new Employee("yanzhenqin",44,new MyDate(709,5,9));Employee e4 = new Employee("zhangqian",51,new MyDate(-179,8,12));Employee e5 = new Employee("quyuan",21,new MyDate(-340,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}//問題一:使用自然排序@Testpublic void test(){TreeSet set = new TreeSet();Employee e1 = new Employee("wangxianzhi",41,new MyDate(334,5,4));Employee e2 = new Employee("simaqian",43,new MyDate(-145,7,12));Employee e3 = new Employee("yanzhenqin",44,new MyDate(709,5,9));Employee e4 = new Employee("zhangqian",51,new MyDate(-179,8,12));Employee e5 = new Employee("quyuan",21,new MyDate(-340,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}
}
5.9、Set課后兩道面試題
練習:在List內去除重復數字值,要求盡量簡單
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class CollectionTest {//練習:在List內去除重復數字值,要求盡量簡單public static List duplicateList(List list) {HashSet set = new HashSet();set.addAll(list);return new ArrayList(set);}@Testpublic void test2(){List list = new ArrayList();list.add(new Integer(1));list.add(new Integer(2));list.add(new Integer(2));list.add(new Integer(4));list.add(new Integer(4));List list2 = duplicateList(list);for (Object integer : list2) {System.out.println(integer);}}
}
2、面試題
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class CollectionTest {@Testpublic void test3(){HashSet set = new HashSet();Person p1 = new Person(1001,"AA");Person p2 = new Person(1002,"BB");set.add(p1);set.add(p2);System.out.println(set);p1.name = "CC";set.remove(p1);System.out.println(set);set.add(new Person(1001,"CC"));System.out.println(set);set.add(new Person(1001,"AA"));System.out.println(set);}
}
06、Map接口
6.1、Map接口及其多個實現類的對比
import org.junit.Test;import java.util.HashMap;
import java.util.Map;/*** 一、Map的實現類的結構:* |----Map:雙列數據,存儲key-value對的數據 ---類似于高中的函數:y = f(x)* |----HashMap:作為Map的主要實現類;線程不安全的,效率高;存儲null的key和value* |----LinkedHashMap:保證在遍歷map元素時,可以按照添加的順序實現遍歷。* 原因:在原有的HashMap底層結構基礎上,添加了一對指針,指向前一個和后一個元素。* 對于頻繁的遍歷操作,此類執行效率高于HashMap。* |----TreeMap:保證按照添加的key-value對進行排序,實現排序遍歷。此時考慮key的自然排序或定制排序* 底層使用紅黑樹* |----Hashtable:作為古老的實現類;線程安全的,效率低;不能存儲null的key和value* |----Properties:常用來處理配置文件。key和value都是String類型*** HashMap的底層:數組+鏈表 (jdk7及之前)* 數組+鏈表+紅黑樹 (jdk 8)** 面試題:* 1. HashMap的底層實現原理?* 2. HashMap 和 Hashtable的異同?* 3. CurrentHashMap 與 Hashtable的異同?(暫時不講)**/
public class MapTest {@Testpublic void test(){Map map = new HashMap();
// map = new Hashtable();map.put(null,123);}
}
6.2、Map中存儲的key-value的特點
- Map與Collection并列存在。用于保存具有映射關系的數據:key-value
- Map 中的key 和value 都可以是任何引用類型的數據
- Map 中的key 用Set來存放,不允許重復,即同一個Map 對象所對應的類,須重寫hashCode()和equals()方法
- 常用String類作為Map的“鍵”
- key 和value 之間存在單向一對一關系,即通過指定的key 總能找到唯一的、確定的value
- Map接口的常用實現類:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map 接口使用頻率最高的實現類
/*** 二、Map結構的理解:* Map中的key:無序的、不可重復的,使用Set存儲所有的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例)* Map中的value:無序的、可重復的,使用Collection存儲所有的value --->value所在的類要重寫equals()* 一個鍵值對:key-value構成了一個Entry對象。* Map中的entry:無序的、不可重復的,使用Set存儲所有的entry**/
6.3、Map實現類之一:HashMap
- HashMap是Map 接口使用頻率最高的實現類。
- 允許使用null鍵和null值,與HashSet一樣,不保證映射的順序。
- 所有的key構成的集合是Set:無序的、不可重復的。所以,key所在的類要重寫:equals()和hashCode()
- 所有的value構成的集合是Collection:無序的、可以重復的。所以,value所在的類要重寫:equals()
- 一個key-value構成一個entry
- 所有的entry構成的集合是Set:無序的、不可重復的
- HashMap 判斷兩個key 相等的標準是:兩個key 通過equals() 方法返回true,hashCode值也相等。
- HashMap判斷兩個value相等的標準是:兩個value 通過equals() 方法返回true。
6.4、HashMap的底層實現原理
JDK 7及以前版本:HashMap是數組+鏈表結構(即為鏈地址法)
JDK 8版本發布以后:HashMap是數組+鏈表+紅黑樹實現。
HashMap源碼中的重要常量
/** DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16* DEFAULT_LOAD_FACTOR:HashMap的默認加載因子:0.75* threshold:擴容的臨界值,=容量*填充因子:16 * 0.75 => 12* TREEIFY_THRESHOLD:Bucket中鏈表長度大于該默認值,轉化為紅黑樹:8* MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64
*/
6.4.1、HashMap在JDK7中的底層實現原理
- HashMap的內部存儲結構其實是數組和鏈表的結合。當實例化一個HashMap時,系統會創建一個長度為Capacity的Entry數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
- 每個bucket中存儲一個元素,即一個Entry對象,但每一個Entry對象可以帶一個引用變量,用于指向下一個元素,因此,在一個桶中,就有可能生成一個Entry鏈。而且新添加的元素作為鏈表的head。
- 添加元素的過程:
- 向HashMap中添加entry1(key,value),需要首先計算entry1中key的哈希值(根據key所在類的hashCode()計算得到),此哈希值經過處理以后,得到在底層Entry[]數組中要存儲的位置i。
- 如果位置i上沒有元素,則entry1直接添加成功。
- 如果位置i上已經存在entry2(或還有鏈表存在的entry3,entry4),則需要通過循環的方法,依次比較entry1中key的hash值和其他的entry的hash值。
- 如果彼此hash值不同,則直接添加成功。
- 如果hash值相同,繼續比較二者是否equals。如果返回值為true,則使用entry1的value去替換equals為true的entry的value。
- 如果遍歷一遍以后,發現所有的equals返回都為false,則entry1仍可添加成功。entry1指向原有的entry元素。
/** 三、HashMap的底層實現原理?以jdk7為例說明:* HashMap map = new HashMap():* 在實例化以后,底層創建了長度是16的一維數組Entry[] table。* ...可能已經執行過多次put...* map.put(key1,value1):* 首先,調用key1所在類的hashCode()計算key1哈希值,此哈希值經過某種算法計算以后,得到在Entry數組中的存放位置。* 如果此位置上的數據為空,此時的key1-value1添加成功。 ----情況1* 如果此位置上的數據不為空,(意味著此位置上存在一個或多個數據(以鏈表形式存在)),比較key1和已經存在的一個或多個數據* 的哈希值:* 如果key1的哈希值與已經存在的數據的哈希值都不相同,此時key1-value1添加成功。----情況2* 如果key1的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較:* 如果equals()返回false:此時key1-value1添加成功。----情況3* 如果equals()返回true:使用value1替換value2。** 補充:關于情況2和情況3:此時key1-value1和原來的數據以鏈表的方式存儲。** 在不斷的添加過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容。默認的擴容方式:擴容為原來容量的2倍,并將原有的數據復制過來。**//*** HashMap的擴容* 當HashMap中的元素越來越多的時候,hash沖突的幾率也就越來越高,* 因為數組的長度是固定的。所以為了提高查詢的效率,* 就要對HashMap的數組進行擴容,而在HashMap數組擴容之后,* 最消耗性能的點就出現了:原數組中的數據必須重新計算其在新數組中的位置,* 并放進去,這就是resize。* * 那么HashMap什么時候進行擴容呢?* 當HashMap中的元素個數超過數組大小(數組總大小length,* 不是數組中個數size)*loadFactor時,就 會 進 行 數 組 擴 容,* loadFactor的默認值(DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。* 也就是說,默認情況下,數組大小(DEFAULT_INITIAL_CAPACITY)為16,* 那么當HashMap中元素個數超過16*0.75=12(這個值就是代碼中的threshold值,* 也叫做臨界值)的時候,就把數組的大小擴展為2*16=32,即擴大一倍,* 然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,* 所以如果我們已經預知HashMap中元素的個數,* 那么預設元素的個數能夠有效的提高HashMap的性能。*/
6.4.2、HashMap在JDK8中的底層實現原理
- HashMap的內部存儲結構其實是數組+鏈表+紅黑樹的結合。當實例化一個HashMap時,會初始化initialCapacity和loadFactor,在put第一對映射關系時,系統會創建一個長度為initialCapacity的Node數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素
- 每個bucket中存儲一個元素,即一個Node對象,但每一個Node對象可以帶一個引用變量next,用于指向下一個元素,因此,在一個桶中,就有可能生成一個Node鏈。也可能是一個一個TreeNode對象,每一個TreeNode對象可以有兩個葉子結點left和right,因此,在一個桶中,就有可能生成一個TreeNode樹。而新添加的元素作為鏈表的last,或樹的葉子結點。
- 那么HashMap什么時候進行擴容和樹形化呢?
- 當HashMap中的元素個數超過數組大小(數組總大小length,不是數組中個數size)loadFactor時,就會進行數組擴容,loadFactor的默認值(DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。也就是說,默認情況下,數組大小(DEFAULT_INITIAL_CAPACITY)為16,那么當HashMap中元素個數超過160.75=12(這個值就是代碼中的threshold值,也叫做臨界值)的時候,就把數組的大小擴展為2*16=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,所以如果我們已經預知HashMap中元素的個數,那么預設元素的個數能夠有效的提高HashMap的性能。
- 當HashMap中的其中一個鏈的對象個數如果達到了8個,此時如果capacity沒有達到64,那么HashMap會先擴容解決,如果已經達到了64,那么這個鏈會變成紅黑樹,結點類型由Node變成TreeNode類型。當然,如果當映射關系被移除后,下次resize方法時判斷樹的結點個數低于6個,也會把紅黑樹再轉為鏈表。
- 關于映射關系的key是否可以修改?answer:不要修改
映射關系存儲到HashMap中會存儲key的hash值,這樣就不用在每次查找時重新計算每一個Entry或Node(TreeNode)的hash值了,因此如果已經put到Map中的映射關系,再修改key的屬性,而這個屬性又參與hashcode值的計算,那么會導致匹配不上。
/* 總結:* jdk8 相較于jdk7在底層實現方面的不同:* 1.new HashMap():底層沒有創建一個長度為16的數組* 2.jdk 8底層的數組是:Node[],而非Entry[]* 3.首次調用put()方法時,底層創建長度為16的數組* 4.jdk7底層結構只有:數組+鏈表。jdk8中底層結構:數組+鏈表+紅黑樹。* 4.1形成鏈表時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)* 4.2當數組的某一個索引位置上的元素以鏈表形式存在的數據個數 > 8 且當前數組的長度 > 64時,此時此索引位置上的所數據改為使用紅黑樹存儲。*/
6.7、LinkedHashMap的底層實現原理(了解)
- LinkedHashMap是HashMap的子類
- 在HashMap存儲結構的基礎上,使用了一對雙向鏈表來記錄添加元素的順序
- 與LinkedHashSet類似,LinkedHashMap可以維護Map 的迭代順序:迭代順序與Key-Value 對的插入順序一致
- HashMap中的內部類:Node
/** 四、LinkedHashMap的底層實現原理(了解)* 源碼中:* static class Entry<K,V> extends HashMap.Node<K,V> {* Entry<K,V> before, after;//能夠記錄添加的元素的先后順序* Entry(int hash, K key, V value, Node<K,V> next) {* super(hash, key, value, next);* }* } */
import org.junit.Test;import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;public class MapTest {@Testpublic void test2(){Map map = new HashMap();map = new LinkedHashMap();map.put(123,"AA");map.put(345,"BB");map.put(12,"CC");System.out.println(map);}
}
6.8、Map中的常用方法1
import org.junit.Test;import java.util.*;
/*** 五、Map中定義的方法:* 添加、刪除、修改操作:* Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中* void putAll(Map m):將m中的所有key-value對存放到當前map中* Object remove(Object key):移除指定key的key-value對,并返回value* void clear():清空當前map中的所有數據* 元素查詢的操作:* Object get(Object key):獲取指定key對應的value* boolean containsKey(Object key):是否包含指定的key* boolean containsValue(Object value):是否包含指定的value* int size():返回map中key-value對的個數* boolean isEmpty():判斷當前map是否為空* boolean equals(Object obj):判斷當前map和參數對象obj是否相等* 元視圖操作的方法:* Set keySet():返回所有key構成的Set集合* Collection values():返回所有value構成的Collection集合* Set entrySet():返回所有key-value對構成的Set集合**/
public class MapTest {/*** 元素查詢的操作:* Object get(Object key):獲取指定key對應的value* boolean containsKey(Object key):是否包含指定的key* boolean containsValue(Object value):是否包含指定的value* int size():返回map中key-value對的個數* boolean isEmpty():判斷當前map是否為空* boolean equals(Object obj):判斷當前map和參數對象obj是否相等*/@Testpublic void test4(){Map map = new HashMap();map.put("AA",123);map.put(45,123);map.put("BB",56);// Object get(Object key)System.out.println(map.get(45));//containsKey(Object key)boolean isExist = map.containsKey("BB");System.out.println(isExist);isExist = map.containsValue(123);System.out.println(isExist);map.clear();System.out.println(map.isEmpty());}/*** 添加、刪除、修改操作:* Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中* void putAll(Map m):將m中的所有key-value對存放到當前map中* Object remove(Object key):移除指定key的key-value對,并返回value* void clear():清空當前map中的所有數據*/@Testpublic void test3(){Map map = new HashMap();//添加map.put("AA",123);map.put(45,123);map.put("BB",56);//修改map.put("AA",87);System.out.println(map);Map map1 = new HashMap();map1.put("CC",123);map1.put("DD",456);map.putAll(map1);System.out.println(map);//remove(Object key)Object value = map.remove("CC");System.out.println(value);System.out.println(map);//clear()map.clear();//與map = null操作不同System.out.println(map.size());System.out.println(map);}
}
6.9、Map中的常用方法2
import org.junit.Test;import java.util.*;
/*** 五、Map中定義的方法:* 添加、刪除、修改操作:* Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中* void putAll(Map m):將m中的所有key-value對存放到當前map中* Object remove(Object key):移除指定key的key-value對,并返回value* void clear():清空當前map中的所有數據* 元素查詢的操作:* Object get(Object key):獲取指定key對應的value* boolean containsKey(Object key):是否包含指定的key* boolean containsValue(Object value):是否包含指定的value* int size():返回map中key-value對的個數* boolean isEmpty():判斷當前map是否為空* boolean equals(Object obj):判斷當前map和參數對象obj是否相等* 元視圖操作的方法:* Set keySet():返回所有key構成的Set集合* Collection values():返回所有value構成的Collection集合* Set entrySet():返回所有key-value對構成的Set集合** 總結:常用方法:* 添加:put(Object key,Object value)* 刪除:remove(Object key)* 修改:put(Object key,Object value)* 查詢:get(Object key)* 長度:size()* 遍歷:keySet() / values() / entrySet()** 面試題:* 1. HashMap的底層實現原理?* 2. HashMap 和 Hashtable的異同?* 1.HashMap與Hashtable都實現了Map接口。由于HashMap的非線程安全性,效率上可能高于Hashtable。Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap 就必須為之提供外同步。* 2.HashMap允許將null作為一個entry的key或者value,而Hashtable不允許。* 3.HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。* 4.Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。* 5.Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。** 3. CurrentHashMap 與 Hashtable的異同?(暫時不講)**/
public class MapTest {/*** 元視圖操作的方法:* Set keySet():返回所有key構成的Set集合* Collection values():返回所有value構成的Collection集合* Set entrySet():返回所有key-value對構成的Set集合*/@Testpublic void test5(){Map map = new HashMap();map.put("AA",123);map.put(45,1234);map.put("BB",56);//遍歷所有的key集:keySet()Set set = map.keySet();Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("*****************");//遍歷所有的values集:values()Collection values = map.values();for(Object obj : values){System.out.println(obj);}System.out.println("***************");//遍歷所有的key-values//方式一:Set entrySet = map.entrySet();Iterator iterator1 = entrySet.iterator();while (iterator1.hasNext()){Object obj = iterator1.next();//entrySet集合中的元素都是entryMap.Entry entry = (Map.Entry) obj;System.out.println(entry.getKey() + "---->" + entry.getValue());}System.out.println("/");//方式二:Set keySet = map.keySet();Iterator iterator2 = keySet.iterator();while(iterator2.hasNext()){Object key = iterator2.next();Object value = map.get(key);System.out.println(key + "=====" + value);}}
}
6.10、TreeMap兩種添加方式的使用
- TreeMap存儲Key-Value 對時,需要根據key-value 對進行排序。TreeMap可以保證所有的Key-Value 對處于有序狀態。
- TreeSet底層使用紅黑樹結構存儲數據
- TreeMap的Key 的排序:
- 自然排序:TreeMap的所有的Key 必須實現Comparable 接口,而且所有的Key 應該是同一個類的對象,否則將會拋出ClasssCastException
- 定制排序:創建TreeMap時,傳入一個Comparator 對象,該對象負責對TreeMap中的所有key 進行排序。此時不需要Map 的Key 實現Comparable 接口
- TreeMap判斷兩個key相等的標準:兩個key通過compareTo()方法或者compare()方法返回0。
1、User類
public class User implements Comparable{private String name;private int age;public User() {}public User(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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {System.out.println("User equals()....");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return name != null ? name.equals(user.name) : user.name == null;}@Overridepublic int hashCode() { //return name.hashCode() + age;int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if(o instanceof User){User user = (User)o;
// return -this.name.compareTo(user.name);int compare = -this.name.compareTo(user.name);if(compare != 0){return compare;}else{return Integer.compare(this.age,user.age);}}else{throw new RuntimeException("輸入的類型不匹配");}}
}
2、測試類
import org.junit.Test;import java.util.*;public class TreeMapTest {/*** 向TreeMap中添加key-value,要求key必須是由同一個類創建的對象* 因為要按照key進行排序:自然排序 、定制排序*///自然排序@Testpublic void test(){TreeMap map = new TreeMap();User u1 = new User("Tom",23);User u2 = new User("Jerry",32);User u3 = new User("Jack",20);User u4 = new User("Rose",18);map.put(u1,98);map.put(u2,89);map.put(u3,76);map.put(u4,100);Set entrySet = map.entrySet();Iterator iterator1 = entrySet.iterator();while (iterator1.hasNext()){Object obj = iterator1.next();Map.Entry entry = (Map.Entry) obj;System.out.println(entry.getKey() + "---->" + entry.getValue());}}//定制排序@Testpublic void test2(){TreeMap map = new TreeMap(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof User && o2 instanceof User){User u1 = (User)o1;User u2 = (User)o2;return Integer.compare(u1.getAge(),u2.getAge());}throw new RuntimeException("輸入的類型不匹配!");}});User u1 = new User("Tom",23);User u2 = new User("Jerry",32);User u3 = new User("Jack",20);User u4 = new User("Rose",18);map.put(u1,98);map.put(u2,89);map.put(u3,76);map.put(u4,100);Set entrySet = map.entrySet();Iterator iterator1 = entrySet.iterator();while (iterator1.hasNext()){Object obj = iterator1.next();Map.Entry entry = (Map.Entry) obj;System.out.println(entry.getKey() + "---->" + entry.getValue());}}
}
6.12、Hashtable
- Hashtable是個古老的Map 實現類,JDK1.0就提供了。不同于HashMap,Hashtable是線程安全的。
- Hashtable實現原理和HashMap相同,功能相同。底層都使用哈希表結構,查詢速度快,很多情況下可以互用。
- 與HashMap不同,Hashtable不允許使用null 作為key 和value
- 與HashMap一樣,Hashtable也不能保證其中Key-Value 對的順序
- Hashtable判斷兩個key相等、兩個value相等的標準,與HashMap一致。
6.13、Properties處理屬性文件
- Properties 類是Hashtable的子類,該對象用于處理屬性文件
- 由于屬性文件里的key、value 都是字符串類型,所以Properties 里的key 和value 都是字符串類型
- 存取數據時,建議使用setProperty(String key,Stringvalue)方法和getProperty(String key)方法
1、新建jdbc.properties文件
2、編寫源代碼
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;public class PropertiesTest {//Properties:常用來處理配置文件。key和value都是String類型public static void main(String[] args){//快捷鍵:ALT+Shift+ZFileInputStream fis = null;try {Properties pros = new Properties();fis = new FileInputStream("jdbc.properties");pros.load(fis); //加載流對應文件String name = pros.getProperty("name");String password = pros.getProperty("password");System.out.println("name = " + name + ",password = " + password);} catch (IOException e) {e.printStackTrace();} finally {if(fis != null){try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}
}
如果jdbc.properties文件中寫入為中文;
防止jdbc.properties出現中文亂碼,可根據如下解決:
07、Collections工具類
- 操作數組的工具類:Arrays
- Collections 是一個操作Set、List和Map 等集合的工具類
- Collections 中提供了一系列靜態的方法對集合元素進行排序、查詢和修改等操作,還提供了對集合對象設置不可變、對集合對象實現同步控制等方法
- 排序操作:(均為static方法)
- reverse(List):反轉List 中元素的順序
- shuffle(List):對List集合元素進行隨機排序
- sort(List):根據元素的自然順序對指定List 集合元素按升序排序
- sort(List,Comparator):根據指定的Comparator 產生的順序對List 集合元素進行排序
- swap(List,int,int):將指定list 集合中的i處元素和j 處元素進行交換
7.1、Collections工具類常用方法的測試
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** Collections:操作Collection、Map的工具類** 面試題:Collection 和 Collections的區別?* Collection是集合類的上級接口,繼承于他的接口主要有Set 和List.* Collections是針對集合類的一個幫助類,他提供一系列靜態方法實現對各種集合的搜索、排序、線程安全化等操作.*/
public class CollectionTest {/*** reverse(List):反轉 List 中元素的順序* shuffle(List):對 List 集合元素進行隨機排序* sort(List):根據元素的自然順序對指定 List 集合元素按升序排序* sort(List,Comparator):根據指定的 Comparator 產生的順序對 List 集合元素進行排序* swap(List,int, int):將指定 list 集合中的 i 處元素和 j 處元素進行交換** Object max(Collection):根據元素的自然順序,返回給定集合中的最大元素* Object max(Collection,Comparator):根據 Comparator 指定的順序,返回給定集合中的最大元素* Object min(Collection)* Object min(Collection,Comparator)* int frequency(Collection,Object):返回指定集合中指定元素的出現次數* void copy(List dest,List src):將src中的內容復制到dest中* boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的所有舊值**/@Testpublic void test(){List list = new ArrayList();list.add(123);list.add(43);list.add(765);list.add(765);list.add(765);list.add(-97);list.add(0);System.out.println(list);// Collections.reverse(list);
// Collections.shuffle(list);
// Collections.sort(list);
// Collections.swap(list,1,2);int frequency = Collections.frequency(list, 123);System.out.println(list);System.out.println(frequency);}@Testpublic void test2(){List list = new ArrayList();list.add(123);list.add(43);list.add(765);list.add(-97);list.add(0);//報異常:IndexOutOfBoundsException("Source does not fit in dest")
// List dest = new ArrayList();
// Collections.copy(dest,list);//正確的:List dest = Arrays.asList(new Object[list.size()]);System.out.println(dest.size());//list.size();Collections.copy(dest,list);System.out.println(dest);/*** Collections 類中提供了多個 synchronizedXxx() 方法,* 該方法可使將指定集合包裝成線程同步的集合,從而可以解決* 多線程并發訪問集合時的線程安全問題*///返回的list1即為線程安全的ListList list1 = Collections.synchronizedList(list);}
}