一. Collection 單列集合
? ? ? ?1.? Collection代表單列集合,每個元素(數據)只包含一個值
? ? ? ?2. Collection集合特點
? ? ? ? ? ? ? ? ① List系列集合:添加的元素是有序、可重復、有索引。
? ? ? ? ? ? ? ? ? ? ? ? ArrayList、LinekdList:有序、可重復,有索引
? ? ? ? ? ? ? ? ② Set系列集合:添加的元素是無序、不重復、無索引
? ? ? ? ? ? ? ? ? ? ? ? HashSet:無序、不重復、無索引
? ? ? ? ? ? ? ? ? ? ? ? LinkedHashSet:有序、不重復、無索引
? ? ? ? ? ? ? ? ? ? ? ? TreeSet:按照大小默認升序排序、不重復、無索引
? ? ? 3. Collection 集合的常用方法
????????????????Collection 是所有單列集合的父類,他規定的方法是全部單列集合都會繼承(包括List<E>集合 和 Set<E> 集合)
常用方法 | 說明 |
boolean?add(E?e) | 添加元素,成功返回true |
addAll() | 把集合的全部元素移動到另一個集合中 |
void?clear() | 清空集合中的元素 |
boolean?isEmpty() | 判斷集合是否為空 是則返回true |
int?size() | 獲取集合的大小 |
boolean?contains(Object?o) | 判斷集合是否包含某個元素 |
boolean?remove(Object?o) | 刪除某個元素,如果有多個默認刪除第一個 |
Object[]?toArray() | 將集合轉換成數組 |
public static void main(String[] args) {Collection<String> c = new ArrayList<String>();//boolean add(E e) 添加元素,成功返回true 因為可重復 所以一定會返回truec.add("卡莎");c.add("泰坦");c.add("洛");System.out.println(c);//[卡莎, 泰坦, 洛]//void clear() 清空集合中的元素c.clear();System.out.println(c);//[卡莎, 泰坦, 洛]//boolean isEmpty() 判斷集合是否為空 是則返回trueSystem.out.println(c.isEmpty());//truec.add("卡莎");c.add("泰坦");c.add("洛");c.add("A");c.add("洛");System.out.println(c.isEmpty());//false//int size() 獲取集合的大小System.out.println(c.size());//5//boolean contains(Object o) 判斷集合是否包含某個元素System.out.println(c);//[卡莎, 泰坦, 洛, A, 洛]System.out.println(c.contains("a"));//falseSystem.out.println(c.contains("A"));//true//boolean remove(Object o) 刪除某個元素,如果有多個默認刪除第一個c.remove("洛");System.out.println(c);//[卡莎, 泰坦, A, 洛]//Object[] toArray() 將集合轉換成數組Object[] object = c.toArray();System.out.println(Arrays.toString(object));//[卡莎, 泰坦, A, 洛]String[] strings = c.toArray(new String[c.size()]);System.out.println(Arrays.toString(strings));//[卡莎, 泰坦, A, 洛]//把集合的全部元素移動到另一個集合中Collection<String> c2 = new ArrayList<String>();c2.addAll(c);System.out.println(c2);//[卡莎, 泰坦, A, 洛]}
????????4.?Collection 集合的遍歷方式
? ? ? ? ? ? ? ? (1) 迭代器:迭代器是用來遍歷集合的專用方式(數組沒有迭代器),Java中最常用的迭代器是Iterator
方法名 | 說明 |
Iterator<E> iterator() | 返回集合中的迭代器對象,該迭代器對象默認指向當前集合的第一個元素 |
Boolean hasNext() | 詢問當前位置是否有元素存在,存在返回true 不存在返回false |
E next() | 獲取當前位置的元素,并同時將迭代器對象指向下一個元素 |
public static void main(String[] args) {//iteratorCollection<String> c = new ArrayList();c.add("卡莎");c.add("泰坦");c.add("洛");//Iterator<E> iterator()返回集合中的迭代器對象,該迭代器對象默認指向當前集合的第一個元素Iterator<String> iterator = c.iterator();//Boolean hasNext() 詢問當前位置是否有元素存在,存在返回true 不存在返回false//E next() 獲取當前位置的元素,并同時將迭代器對象指向下一個元素/*System.out.println(iterator.next());//卡莎System.out.println(iterator.next());//泰坦System.out.println(iterator.next());//洛System.out.println(iterator.next());//NoSuchElementException 異常*/while (iterator.hasNext()) {String ele = iterator.next();System.out.println(ele);//卡莎 泰坦 洛}}
? ? ? ? ? ? ? ?(2)?增強for:既可以用來遍歷集合,也可以用來遍歷數組
? ? ? ? ? ? ? ? ? ? ? ? 格式:for(元素的數據類型 變量名 : 數組或集合){? }
? ? ? ? ? ? ? ? ? ? ? ? 本質上就是迭代器遍歷集合的簡化寫法
public static void main(String[] args) {Collection<String> c = new ArrayList();c.add("卡莎");c.add("泰坦");c.add("洛");for (String s : c){System.out.println(s);//卡莎 泰坦 洛}String[] ss = {"飛機","奧恩"};for (String s : ss){System.out.println(s);//飛機 奧恩}
}
? ? ? ? ? ? ? ? (3) lambda表達式:JDK8 開始的Lambda表達式,提供了一種更簡單、更直接的方式來遍歷集合
? ? ? ? ? ? ? ? ? ? ? ? default void forEach(Consumer<? super T> action) 結合lambda遍歷集合
public static void main(String[] args) {Collection<String> c = new ArrayList();c.add("卡莎");c.add("泰坦");c.add("洛");// default void forEach(Consumer<? super T> action) 結合lambda遍歷集合c.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);//卡莎 泰坦 洛}});c.forEach((String s) ->{System.out.println(s);//卡莎 泰坦 洛});c.forEach((s) ->System.out.println(s)//卡莎 泰坦 洛);c.forEach(s -> System.out.println(s));//卡莎 泰坦 洛c.forEach(System.out::println);}
????????5. List集合
? ? ? ? ? ? ? ? (1) List集合支持索引,除了Collection的方法,多了很多與索引相關的方法。包括ArrayList集合和LinkedList集合
方法名稱 | 說明 |
void add(int index, E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 刪除指定索引處的元素,返回被刪除的元素 |
E set(int index, E element) | 修改指定索引出的元素,返回被修改的元素 |
E get(int index) | 返回指定索引出的元素 |
public static void main(String[] args) {List<String> list = new ArrayList<String>();list.add("卡莎");list.add("泰坦");list.add("泰坦");list.add("洛");System.out.println(list);//[卡莎, 泰坦, 泰坦, 洛]//void add(int index, E element) 在此集合中的指定位置插入指定的元素list.add(3, "霞");System.out.println(list);//[卡莎, 泰坦, 泰坦, 霞, 洛]//E remove(int index) 刪除指定索引處的元素,返回被刪除的元素System.out.println(list.remove(2));//泰坦System.out.println(list);//[卡莎, 泰坦, 霞, 洛]//E set(int index, E element) 修改指定索引出的元素,返回被修改的元素System.out.println(list.set(2, "伊澤"));//霞//E get(int index) 返回指定索引出的元素System.out.println(list.get(2));//伊澤
}
? ? ? ? ? ? ? ? (2) List集合的遍歷方式
? ? ? ? ? ? ? ? ? ? ? ? ① for循環(因為List集合有索引)
? ? ? ? ? ? ? ? ? ? ? ? ② 迭代器
? ? ? ? ? ? ? ? ? ? ? ? ③ 增強for循環
? ? ? ? ? ? ? ? ? ? ? ? ④ Lambda表達式
public static void main(String[] args) {List<String> list = new ArrayList<String>();list.add("卡莎");list.add("泰坦");list.add("泰坦");list.add("洛");//① for循環(因為List集合有索引)for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}//② 迭代器Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}//③ 增強for循環for (String string : list) {System.out.println(string);}//④ Lambda表達式list.forEach(System.out::println);
}
? ? ? ? 6.?ArrayList集合的底層原理
? ? ? ? ? ? ? ? (1)?ArrayList與LinekdList都是 有序、可重復、 有索引,但底層采用的數據結構不同,應用場景也不同。
? ? ? ? ? ? ? ? (2)?ArrayList的底層原理:基于數組實現的。利用無參構造器創建的集合,會在底層創建一個默認長度為0的數組;添加第一個元素時,底層會創建一個新的長度為10的數組;存滿時,會擴容1.5倍;如果一次添加多個元素,1.5倍還放不下,則新創建數組的長度以實際為準;
? ? ? ? ? ? ? ? ? ? ? ? ① 查詢速度快(根據索引查詢數據快):查詢數據通過地址值和索引定位,查詢任意數據耗時相同
? ? ? ? ? ? ? ? ? ? ? ? ② 刪除效率低:可能需要把后面很多的數據進行前移
? ? ? ? ? ? ? ? ? ? ? ? ③ 添加效率低:可能需要把后面很多的數據后移,再添加元素;或者可能需要進行數組的擴容
? ? ? ? 7.?LinekdList集合的底層原理
? ? ? ? ? ? ? ? (1)?ArrayList的底層原理:基于雙鏈表實現的;鏈表中的節點都是獨立的對象,在內存中時不連續的,每一個節點包含數據值和下一個節點的地址。
? ? ? ? ? ? ? ? (2) 特點:
? ? ? ? ? ? ? ? ? ? ? ? ① 查詢慢,無論查詢哪個數據都要從頭開始找。
? ? ? ? ? ? ? ? ? ? ? ? ② 增刪相對快,對首尾元素進行增刪改查的速度是極快的。
? ? ? ? ? ? ? ? (3)?LinekdList新增了很多首尾操作的特有方法
方法名稱 | 說明 |
public void addFirst(E e) | 將指定的元素追加到列表的開頭 |
public void addLast(E e) | 將指定的元素追加到列表的末尾 |
public E getFirst() | 返回第一個元素 |
public E getLast() | 返回最后一個元素 |
public E removeFirst() | 刪除并返回第一個元素 |
public E removeLast() | 刪除并返回最后一個元素 |
public static void main(String[] args) {LinkedList<String> list = new LinkedList<>();list.add("卡莎");list.add("泰坦");list.add("泰坦");list.add("洛");System.out.println(list);//[卡莎, 泰坦, 泰坦, 洛]//public void addFirst(E e) 將指定的元素追加到列表的開頭list.addFirst("張飛");System.out.println(list);//[張飛, 卡莎, 泰坦, 泰坦, 洛]//public void addLast(E e) 將指定的元素追加到列表的末尾list.addLast("豆芽");System.out.println(list);//[張飛, 卡莎, 泰坦, 泰坦, 洛, 豆芽]//public E getFirst() 返回第一個元素System.out.println(list.getFirst());//張飛//public E getLast() 返回最后一個元素System.out.println(list.getLast());//豆芽//public E removeFirst() 刪除并返回第一個元素System.out.println(list.removeFirst());//張飛//public E removeLast() 刪除并返回最后一個元素System.out.println(list.removeLast());//豆芽
}
? ? ? ? 8. Set集合
? ? ? ? ? ? ? ? (1) 特點:添加的元素是無序(添加數據的順序和獲取數據出的數據順序不一樣)、不重復、無索引
? ? ? ? ? ? ? ? (2) Set 要用到的常用方法,基本上就是Collection提供的,自己幾乎沒有新增的方法
????????9. HashSet集合
? ? ? ? ? ? ? ? (1) 無序、不重復、無索引
? ? ? ? ? ? ? ? 底層原理:
? ? ? ? ? ? ? ? (2)?哈希值:就是一個int類型的數值,Java中每一個對象都有一個哈希值。都可以通過Objectl類提供的hashCode方法獲取對象的哈希值。public int hashCode()
? ? ? ? ? ? ? ? ? ? ? ? ① 同一個對象多次調用hashCode()方法返回的哈希值是相同的
? ? ? ? ? ? ? ? ? ? ? ? ② 不同的對象,他們的哈希值一般不相同,但也有可能會相同(哈希碰撞)
? ? ? ? ? ? ? ? (3) HashSet 是基于哈希表實現的。哈希表是一種增刪改查數據性能都較好的數據結構。JDK8之前哈希表=數組+鏈表;JDK8之后:數組+鏈表+紅黑樹
? ? ? ? ? ? ? ? (4)?DK8開始,當鏈表的長度超過8,且數組長度>=64時,自動將鏈表轉成紅黑樹
? ? ? ? ? ? ? ? (5) 如果希望Set集合認為2個內容一樣的對象是重復的,必須重寫對象的hashCode()和equals()方法
public class Student {private int id;private String name;private Double[] grades;public Student() {}public Student(int id, String name, Double[] grades) {this.id = id;this.name = name;this.grades = grades;}// idea快捷鍵生成,類中右鍵選擇Generate->equals() and hashCode()@Overridepublic int hashCode() {return Objects.hash(id, name, Arrays.hashCode(grades));}@Overridepublic boolean equals(Object o) {if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return id == student.id && Objects.equals(name, student.name) && Objects.equals(grades, student.grades);}}
????????10. LinkedHashSet集合
? ? ? ? ? ? ? ? (1) 有序(添加元素的順序和獲取元素的順序一樣)、不重復、無索引
? ? ? ? ? ? ? ? 底層原理:
? ? ? ? ? ? ? ? (2) 基于哈希表(數組、鏈表、紅黑樹)實現的;但是,他的每個元素都額外多了一個雙鏈表的機制記錄它前后元素的位置(更占內存)
????????11. TreeSet集合
? ? ? ? ? ? ? ? (1) 按照大小默認升序排序、不重復、無索引
? ? ? ? ? ? ? ? 底層原理:????????
? ? ? ? ? ? ? ? (2) 基于紅黑樹實現的排序
? ? ? ? ? ? ? ? (3) 注意:
? ? ? ? ? ? ? ? ? ? ? ? ① 對于數值類型:Integer、Double,默認按照數值大小進行排序
? ? ? ? ? ? ? ? ? ? ? ? ② 對于字符串類型:默認按照字符的編號升序排序
? ? ? ? ? ? ? ? ? ? ? ? ③ 對于自定義類型(如Student)對象,TreeSet默認是無法直接排序的
? ? ? ? ? ? ? ? ? ? ? ? ④ 自定義排序規則:TreeSet集合存儲自定義類型的對象時,必須指定排序規則(有兩種):
? ? ? ? ? ? ? ? ? ? ? ? 規則1:讓自定義類實現Comparable接口,重寫里面的CompareTo()方法
? ? ? ? ? ? ? ? ? ? ? ? 規則2:通過調用TreeSet集合有參構造器,可以設置Comparator對象(比較器對象)--public TreeSet(Comparator<? Super E> comparator)
? ? ? ? ? ? ? ? ? ? ? ? 如果規則1和規則2同時使用,默認就近原則(規則2)
//方式1. Comparable:讓該類對象實現Comparable(比較規則)接口,然后重寫compareTo方法,自己制定比較規則
public class Student implements Comparable<Student>{private String name;private int age;private double score;public Student() {}public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}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 double getScore() {return score;}public void setScore(double score) {this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {//左邊對象 大于 右邊對象 返回正整數//左邊對象 小于 右邊對象 返回負整數//左邊對象 等于 右邊對象 返回0//如按照成績排序 默認升序/*if (this.score > o.score) {return 1;}else if (this.score < o.score) {return -1;}else {return 0;}*/return Double.compare(this.getScore(), o.getScore());//升序//降序的話://左邊對象 大于 右邊對象 返回負整數//左邊對象 小于 右邊對象 返回正整數//左邊對象 等于 右邊對象 返回0/* if (this.score > o.score) {return -1;}else if (this.score < o.score) {return 0;}else {return 0;}return Double.compare(o.getScore(), this.getScore());//降序*/}
}
public static void main(String[] args) {//HashSetSet<String> set = new HashSet();//無序 不重復 無索引set.add("1");set.add("2");set.add("3");set.add("4");set.add("1");set.add("5");set.add("2");System.out.println(set);////LinkedHashSetSet<String> set1 = new LinkedHashSet();//有序 不重復 無索引set1.add("3");set1.add("2");set1.add("1");set1.add("4");set1.add("5");System.out.println(set1);//TreeSetSet<String> set3 = new TreeSet();set3.add("3");set3.add("1");set3.add("2");set3.add("6");set3.add("5");System.out.println(set3);Set<Integer> set4 = new TreeSet();set4.add(1);set4.add(7);set4.add(5);set4.add(6);set4.add(2);set4.add(5);System.out.println(set4);//自定義對象TreeSetStudent student = new Student("卡莎", 18, 99);Student student1 = new Student("泰坦", 19, 93);Student student2 = new Student("伊澤", 16, 98);Student student3 = new Student("璐璐", 16, 98);Set<Student> set5 = new TreeSet();set5.add(student);set5.add(student1);set5.add(student2);set5.add(student3);//[Student{name='泰坦', age=19, score=93.0}, Student{name='伊澤', age=16, score=98.0}, Student{name='卡莎', age=18, score=99.0}]System.out.println(set5);//由于伊澤和璐璐 分數98相同 后一個不存了Set<Student> set6 = new TreeSet(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o1.getAge()- o2.getAge();}});set6.add(student);set6.add(student1);set6.add(student2);set6.add(student3);//[Student{name='伊澤', age=16, score=98.0}, Student{name='卡莎', age=18, score=99.0}, Student{name='泰坦', age=19, score=93.0}]System.out.println(set6);}
二. 集合的并發修改異常問題
? ? ? ? 1. 集合的并發修改異常:使用迭代器遍歷集合時,又同時在刪除集合中的數據,程序就會出現并發修改異常
? ? ? ? 2. 由于增強for循環遍歷集合就是迭代器遍歷集合的簡化寫法,因此,使用增強for循環遍歷集合,又在同時刪除集合中的數據時,程序也會出現并發修改異常
? ? ? ? 3. 解決方法:
? ? ? ? ? ? ? ? ① 如果使用迭代器遍歷集合,用迭代器自己的刪除方法刪除數據即可
? ? ? ? ? ? ? ? ② 如果能用for循環遍歷時,可以倒著遍歷并刪除;或者從前往后遍歷,刪除元素后進行i--;
public static void main(String[] args) {//集合的并發修改異常問題ArrayList<String> list = new ArrayList<String>();list.add("泰坦");list.add("卡莎");list.add("王莎");list.add("洛");list.add("伊澤");list.add("趙莎");list.add("璐璐");list.add("孫莎");System.out.println(list);//迭代器 ConcurrentModificationException異常Iterator<String> iterator = list.iterator();/*while (iterator.hasNext()) {String s = iterator.next();if (s.contains("莎")){list.remove(s);//ConcurrentModificationException}}*///解決方法iterator.remove();while (iterator.hasNext()) {String s = iterator.next();if (s.contains("莎")){iterator.remove();//刪除迭代器當前遍歷到的數據,每刪除一個數據后,相當于i--}}System.out.println(list);ArrayList<String> list2 = new ArrayList<String>();list2.add("泰坦");list2.add("卡莎");list2.add("王莎");list2.add("洛");list2.add("伊澤");list2.add("趙莎");list2.add("璐璐");list2.add("孫莎");//for循環for (int i = 0; i < list2.size(); i++) {String s = list2.get(i);if (s.contains("莎")){list2.remove(i);}}//出現bug 沒有刪除完System.out.println(list2);//[泰坦, 王莎, 洛, 伊澤, 璐璐]//解決方法1 i--for (int i = 0; i < list2.size(); i++) {String s = list2.get(i);if (s.contains("莎")){list2.remove(i);i--;}}//解決方法2 倒著刪for (int i = list2.size() - 1; i > 0; i--) {String s = list2.get(i);if (s.contains("莎")){list2.remove(i);}}ArrayList<String> list3 = new ArrayList<String>();list3.add("泰坦");list3.add("卡莎");list3.add("王莎");list3.add("洛");list3.add("伊澤");list3.add("趙莎");list3.add("璐璐");list3.add("孫莎");//使用正確for循環遍歷集合并刪除數據,沒有辦法解決ConcurrentModificationException異常//ConcurrentModificationException異常for (String s : list3) {if (s.contains("莎")){list3.remove(s);}}//使用正確forEach循環遍歷集合并刪除數據 沒有辦法解決ConcurrentModificationException異常//ConcurrentModificationException異常list.forEach(name -> {if (name.contains("莎")){list3.remove(name);}});}