集合定位:存儲數據的容器
與數組的區別:
-
數組只能存儲同種數據類型數據,集合可以存儲不同類型的數據。
-
數組的長度一旦創建長度不可變,集合的長度是可變的
-
數組的操作單一,集合的操作比較豐富(增刪改查)
可以分為單列集合與雙列集合:
單例集合:每個元素(數據)只包含一個值。
collecion--->List集合:ArrayList類LinkedList類添加的元素是有序、可重復、有索引。Set集合: HashSet: 無序、不重復、無索引;LinkedHashSet: 有序、不重復、無索引。TreeSet:按照大小默認升序排序、不重復、無索引。雙列集合:每個元素包含兩個值(鍵值對)。
Map -->
1.Collection的常用方法
Collection是單列集合的祖宗,它規定的方法(功能)是全部單列集合都會繼承的。
常用方法如下:
//1.public Object[] toArray(): 把集合轉換為數組
Object[] array = c.toArray();
System.out.println(Arrays.toString(array)); //[java1,java2, java2, java3]//2.如果想把集合轉換為指定類型的數組,可以使用下面的代碼
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]//3.還可以把一個集合中的元素,添加到另一個集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去
System.out.println(c1); //[java1, java2, java3, java4]
2.Collection的遍歷方法
2.1 迭代器遍歷集合
原理如下:
-
當調用iterator()方法獲取迭代器時,當前指向第一個元素
-
hasNext()方法則判斷這個位置是否有元素,如果有則返回true,進入循環
-
調用next()方法獲取元素,并將當月元素指向下一個位置,
-
等下次循環時,則獲取下一個元素,依此內推
-
如果取元素越界,會出現**NoSuchElementException異常。**
Collection<String> c = new ArrayList<>();
c.add("趙敏");
c.add("小昭");
//第一步:先獲取迭代器對象
//解釋:Iterator就是迭代器對象,用于遍歷集合的工具)
Iterator<String> it = c.iterator();//第二步:用于判斷當前位置是否有元素可以獲取
//解釋:hasNext()方法返回true,說明有元素可以獲取;反之沒有
while(it.hasNext()){//第三步:獲取當前位置的元素,然后自動指向下一個元素.String e = it.next();System.out.println(s);
}
2.2 增強for遍歷集合
格式:for(元素的數據類型 變量名:數組或者集合)
增強for不光可以遍歷集合,還可以遍歷數組。
Collection<String> c = new ArrayList<>();
c.add("趙敏");
c.add("小昭");
//1.使用增強for遍歷集合
for(String s: c){System.out.println(s);
}//2.再嘗試使用增強for遍歷數組
String[] arr = {"迪麗熱巴", "古力娜扎", "稀奇哈哈"};
for(String name: arr){System.out.println(name);
}
2.3 foreach遍歷集合
forEach方法的參數是一個Consumer接口,而Consumer是一個函數式接口,所以可以傳遞Lambda表達式。
Collection<String> c = new ArrayList<>();
c.add("趙敏");
c.add("小昭");
//調用forEach方法
//由于參數是一個Consumer接口,所以可以傳遞匿名內部類
c.forEach(new Consumer<String>{@Overridepublic void accept(String s){System.out.println(s);}
});//也可以使用lambda表達式對匿名內部類進行簡化
c.forEach(s->System.out.println(s)); //[趙敏, 小昭, 素素, 滅絕]
當往集合中存對象時,實際上存儲的是對象的地址值:
3.List系列集合
ArrayList、LinekdList:有序,可重復,有索引。
3.1 List集合的常用方法
3.2 ArrayList底層的原理
ArrayList集合底層是基于數組結構實現的,也就是說當你往集合容器中存儲元素時,底層本質上是往數組中存儲元素。
數組擴容,并不是在原數組上擴容(原數組是不可以擴容的),底層是創建一個新數組,然后把原數組中的元素全部復制到新數組中去。
ArrayList集合適合的應用場景:
-
適合根據索引查詢數據,或者數據量不是很大時。
-
不適合在數據量大的同時,又要頻繁的進行增刪操作。
3.3 LinkedList底層原理
LinkedList集合是基于雙向鏈表實現了,所以相對于ArrayList新增了一些可以針對頭尾進行操作的方法,如下圖示所示:
3.4 LinkedList的應用場景
它可以用來設計隊列和棧結構。
//1.創建一個隊列:先進先出、后進后出
LinkedList<String> queue = new LinkedList<>();//入對列queue.addLast("第1號人");
queue.addLast("第2號人");
queue.addLast("第3號人");
queue.addLast("第4號人");
System.out.println(queue);?//出隊列System.out.println(queue.removeFirst()); //第4號人System.out.println(queue.removeFirst()); //第3號人System.out.println(queue.removeFirst()); //第2號人System.out.println(queue.removeFirst()); //第1號人
//1.創建一個棧對象
LinkedList<String> stack = new ArrayList<>();
//壓棧(push) 等價于 addFirst()
stack.push("第1顆子彈");
stack.push("第2顆子彈");
stack.push("第3顆子彈");
stack.push("第4顆子彈");
System.out.println(stack); //[第4顆子彈, 第3顆子彈, 第2顆子彈,第1顆子彈]//彈棧(pop) 等價于 removeFirst()
System.out.println(statck.pop()); //第4顆子彈
System.out.println(statck.pop()); //第3顆子彈
System.out.println(statck.pop()); //第2顆子彈
System.out.println(statck.pop()); //第1顆子彈//彈棧完了,集合中就沒有元素了
System.out.println(list); //[]
4. Set集合
4.1 Set集合的特點
4.2 HashSet底層原理
哈希值就是一個int類型的數值,Java中每個對象都有一個哈希值。
java中的所有對象,都可以調用Object類提供的hashCode方法,返回該對象自己的哈希值。
-
同一個對象多長調用hashCode()方法返回的哈希值是相同的
-
不同的對象,它們的哈希值一般不相同,但也有可能相同(哈希碰撞)
數據結構:哈希表(哈希函數+數組+鏈表+紅黑樹)
-
創建HashSet初始一個長度為16的數組table,加載因子默認為0.75
-
添加元素,首先用元素的hash值和數組長度取余后得到存儲位置,如果位置有數據則調用equals比較兩個值是否相等,相等則不存儲,不相等直接掛在老元素后面,如果鏈表長度超過8且數組長度大于64時,將鏈表轉成紅黑樹,如果紅黑樹節點少于8的時候紅黑樹退化成鏈表。
4.3HashSet去重原理
HashSet存儲元素的原理,依賴于兩個方法:一個是hashCode方法用來確定在底層數組中存儲的位置,另一個是用equals方法判斷新添加的元素是否和集合中已有的元素相同。
要想保證在HashSet集合中沒有重復元素,我們需要重寫元素類的hashCode和equals方法。
4.4 TreeSet集合
reeSet集合的特點是可以對元素進行排序,但是必須指定元素的排序規則。
如何排序?
-
值為int或Integer等,按照從小到大的順序排序。
-
字符串,默認按照第一個字符的對應ASCII的值,相同的則比較第二個,直到比出大小。
-
對象類型:可以讓對象實現Comparable接口,重寫compareTo方法;使用TreeSet的有參構造器 參數Compartor
//第一步:先讓Student類,實現Comparable接口
//注意:Student類的對象是作為TreeSet集合的元素的
public class Student implements Comparable<Student>{private String name;private int age;private double height;//無參數構造方法public Student(){}//全參數構造方法public Student(String name, int age, double height){this.name=name;this.age=age;this.height=height;}//...get、set、toString()方法自己補上..//第二步:重寫compareTo方法//按照年齡進行比較,只需要在方法中讓this.age和o.age相減就可以。/*原理:在往TreeSet集合中添加元素時,add方法底層會調用compareTo方法,根據該方法的結果是正數、負數、還是零,決定元素放在后面、前面還是不存。*/@Overridepublic int compareTo(Student o) {//this:表示將要添加進去的Student對象//o: 表示集合中已有的Student對象return this.age-o.age;}
}
//創建TreeSet集合時,傳遞比較器對象排序
/*
原理:當調用add方法時,底層會先用比較器,根據Comparator的compare方是正數、負數、還是零,決定誰在后,誰在前,誰不存。
*/
//下面代碼中是按照學生的年齡升序排序
Set<Student> students = new TreeSet<>(new Comparator<Student>{@Overridepublic int compare(Student o1, Student o2){//需求:按照學生的身高排序return Double.compare(o1,o2); }
});//創建4個Student對象
Student s1 = new Student("至尊寶",20, 169.6);
Student s2 = new Student("紫霞",23, 169.8);
Student s3 = new Student("蜘蛛精",23, 169.6);
Student s4 = new Student("牛魔王",48, 169.6);//添加Studnet對象到集合
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
System.out.println(students);
4.5 總結Collection集合
4.6 并發修改異常
在使用迭代器遍歷集合時,可能存在并發修改異常。
下面是出現問題的代碼,問題出在list.remove():
List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李愛花");
list.add("張全蛋");
list.add("曉李");
list.add("李玉剛");
System.out.println(list); // [王麻子, 小李子, 李愛花, 張全蛋, 曉李, 李玉剛]//需求:找出集合中帶"李"字的姓名,并從集合中刪除
Iterator<String> it = list.iterator();
while(it.hasNext()){String name = it.next();if(name.contains("李")){list.remove(name);}
}
System.out.println(list);
為什么會出現這個異常呢?那是因為迭代器遍歷機制,規定迭代器遍歷集合的同時,不允許集合自己去增刪元素,否則就會出現這個異常
怎么解決這個問題呢?不使用集合的刪除方法,而是使用迭代器的刪除方法,代碼如下:
List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李愛花");
list.add("張全蛋");
list.add("曉李");
list.add("李玉剛");
System.out.println(list); // [王麻子, 小李子, 李愛花, 張全蛋, 曉李, 李玉剛]//需求:找出集合中帶"李"字的姓名,并從集合中刪除
Iterator<String> it = list.iterator();
while(it.hasNext()){String name = it.next();if(name.contains("李")){//list.remove(name);it.remove(); //當前迭代器指向誰,就刪除誰}
}
System.out.println(list);
l如果能用for循環遍歷時:可以倒著遍歷并刪除;或者從前往后遍歷,但刪除元素后做i --操作。
5. Collection的其他操作
5.1 可變參數
可變參數是一種特殊的形式參數,定義在方法、構造器的形參列表處,它可以讓方法接收多個同類型的實際參數。**
可變參數在方法內部,本質上是一個數組
具體代碼實現如下:
public class ParamTest{public static void main(String[] args){//不傳遞參數,下面的nums長度則為0, 打印元素是[]test(); //傳遞3個參數,下面的nums長度為3,打印元素是[10, 20, 30]test(10,20,30); //傳遞一個數組,下面數組長度為4,打印元素是[10,20,30,40] int[] arr = new int[]{10,20,30,40}test(arr); }public static void test(int...nums){//可變參數在方法內部,本質上是一個數組System.out.println(nums.length);System.out.println(Arrays.toString(nums));System.out.println("----------------");}
}
最后還有一些錯誤寫法,需要讓大家寫代碼時注意一下,不要這么寫哦!!!
一個形參列表中,只能有一個可變參數;否則會報錯**
一個形參列表中如果多個參數,可變參數需要寫在最后;否則會報錯
5.2 Collections工具類
這里的Collections是用來操作Collection的工具類。它提供了一些好用的靜態方法,如下:
演示方法如下:
public class CollectionsTest{public static void main(String[] args){//1.public static <T> boolean addAll(Collection<? super T> c, T...e)List<String> names = new ArrayList<>();Collections.addAll(names, "張三","王五","李四", "張麻子");System.out.println(names);//2.public static void shuffle(List<?> list):對集合打亂順序Collections.shuffle(names);System.out.println(names);//3.public static <T> void short(List<T list): 對List集合排序List<Integer> list = new ArrayList<>();list.add(3);list.add(5);list.add(2);Collections.sort(list);System.out.println(list);}
}
上面我們往集合中存儲的元素要么是Stirng類型,要么是Integer類型,他們本來就有一種自然順序所以可以直接排序。但是如果我們往List集合中存儲Student對象,這個時候想要對List集合進行排序自定義比較規則的。指定排序規則有兩種方式,如下:
排序方式1:讓元素實現Comparable接口,重寫compareTo方法;然后再使用
Collections.sort(list集合)
對List集合排序
public class Student implements Comparable<Student>{private String name;private int age;private double height;//排序時:底層會自動調用此方法,this和o表示需要比較的兩個對象@Overridepublic int compareTo(Student o){//需求:按照年齡升序排序//如果返回正數:說明左邊對象的年齡>右邊對象的年齡//如果返回負數:說明左邊對象的年齡<右邊對象的年齡,//如果返回0:說明左邊對象的年齡和右邊對象的年齡相同return this.age - o.age;}//...getter、setter、constructor..
}//3.public static <T> void short(List<T list): 對List集合排序
List<Student> students = new ArrayList<>();
students.add(new Student("蜘蛛精",23,169.7));
students.add(new Student("紫霞",22,169.8));
students.add(new Student("紫霞",22,169.8));
students.add(new Student("至尊寶",26,169.5));/*
原理:sort方法底層會遍歷students集合中的每一個元素,采用排序算法,將任意兩個元素兩兩比較;每次比較時,會用一個Student對象調用compareTo方法和另一個Student對象進行比較;根據compareTo方法返回的結果是正數、負數,零來決定誰大,誰小,誰相等,重新排序元素的位置注意:這些都是sort方法底層自動完成的,想要完全理解,必須要懂排序算法才行;
*/
Collections.sort(students);
System.out.println(students);
?**排序方式2:使用調用sort方法是,傳遞比較器**
/*
原理:sort方法底層會遍歷students集合中的每一個元素,采用排序算法,將任意兩個元素兩兩比較;每次比較,會將比較的兩個元素傳遞給Comparator比較器對象的compare方法的兩個參數o1和o2,根據compare方法的返回結果是正數,負數,或者0來決定誰大,誰小,誰相等,重新排序元素的位置注意:這些都是sort方法底層自動完成的,不需要我們完全理解,想要理解它必須要懂排序算法才行.
*/
Collections.sort(students, new Comparator<Student>(){@Overridepublic int compare(Student o1, Student o2){return o1.getAge()-o2.getAge();}
});
System.out.println(students);
后續還會更新map集合,上述所涉及圖片均出自b站黑馬程序員java進階課程ppt。