集合是一種容器,類似于數組,但集合的大小可變,開發中也非常常用。Collection代表單列集合,每個元素(數據)只包含1個值。Collection集合分為兩類,List集合與set集合。
特點
List系列集合:添加的元素有序,可重復,有指引。
即ArrayList、LinkedList:有序、可重復、有索引。
Set系列集合:添加的元素是無序、不重復、無索引。
HashSet:無序、不重復、無索引;
LinkedHashSet 有序、不重復、無索引。
TreeSet:按大小默認升序排序、不重復、無索引。
(Collection,List與Set是接口,ArrayList等集合是它們的實現類)
示例:
package Collection;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;public class CollectionDemo1 {public static void main(String[] args) {//目標:搞清楚Collection集合的整體特點//1、list家族的集合:有序,可重復,有索引List<String> list = new ArrayList<>();list.add("java");list.add("java");list.add("c++");list.add("python");System.out.println(list);for(int i = 0; i < list.size(); i++)System.out.println(list.get(i));//2、set家族的集合:無序,不可重復,無索引Set<String> set = new HashSet<>();set.add("java");set.add("java");set.add("c++");set.add("python");System.out.println(set);//無索引,沒有get方法}
}
Collection集合的通用方法
package Collection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;public class CollectionDemo2 {public static void main(String[] args) {//目標:搞清楚Collection集合的通用功能Collection<String> list = new ArrayList<>();//添加集合元素list.add("java");list.add("c++");list.add("python");System.out.println(list);//判斷集合元素個數System.out.println(list.size());//刪除集合元素list.remove("c++");System.out.println(list);//判斷集合是否為空System.out.println(list.isEmpty());//清空集合list.clear();System.out.println(list.isEmpty());System.out.println(list);list.add("java");list.add("c++");//判斷集合中是否包含某個元素System.out.println(list.contains("java"));System.out.println(list.contains("c++"));System.out.println(list.contains("python"));//集合轉數組
// Object[] arr = list.toArray();
// for (int i = 0; i < arr.length; i++) {
// System.out.println(arr[i]);
// }String[] arr = list.toArray(String[]::new);System.out.println(Arrays.toString(arr));}
}
集合的幾種遍歷方式
通常,我們有三種方法可以遍歷集合
一、迭代器遍歷
package Collection;import java.util.ArrayList;
import java.util.Iterator;public class CollectionDemo3 {public static void main(String[] args) {//目標:理解集合的遍歷方式//方式一:迭代器遍歷ArrayList<String> names = new ArrayList<>();names.add("林青霞");names.add("張曼玉");names.add("王祖藍");names.add("柳巖");names.add("張無忌");System.out.println(names);//1、得到一個迭代器對象Iterator<String> it = names.iterator();//it的位置是集合的第一個位置//System.out.println(it.next()); //取出當前位置數據,并且移動到下一個位置//while循環遍歷while(it.hasNext()){ //判斷當前位置有沒有數據String name = it.next();System.out.println(name);}}
}
如上述代碼,通過java庫中Iterator<數據類型>變量名 來獲取一個迭代器對象,用變量名.hasNext()來判斷當前位置是否有值,沒有值則不進入循環。再通過變量名.next獲得當前位置數據,并將指針移向下一位數字來進行數據的輸出。
二、增強for循環遍歷
package Collection;import java.util.ArrayList;public class CollectionDemo4 {public static void main(String[] args) {//方式二:增強for循環遍歷ArrayList<String> names = new ArrayList<>();names.add("林青霞");names.add("張曼玉");names.add("王");names.add("柳巖");names.add("張無忌");System.out.println(names);//可以遍歷集合,也能遍歷數組for(String name : names){System.out.println(name);}}
}
這是一個通用的方法,可以用于集合也可以用于數組,用name接收集合中的值并一個個將其打出。
三:Lambda方法:forEach遍歷
package Collection;import java.util.ArrayList;
import java.util.function.Consumer;public class CollectionDemo5 {public static void main(String[] args) {//方式三:LambdaArrayList<String> names = new ArrayList<>();names.add("林青霞");names.add("張曼玉");names.add("王");names.add("柳巖");names.add("張無忌");//forEach(Consumer<? super T> action)//這是一個接口,所以需要使用Lambda表達式
// names.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });names.forEach(s -> System.out.println(s));}
}
通過重寫集合中的accept方法進行遍歷。
并發修改異常問題與解決
在遍歷一個集合并對其中數據進行修改時,若是在循環中直接刪掉一個數容易產生并發修改異常問題。例如,一個集合中有李明,李四,張三,王五 。當我們需要刪除名字中帶李的人時,遍歷到李明時指針為0,李明被刪除,數組上移一位,李四變為指針0,可此時指針已經移動到1的位置,李四被略過沒刪除,這就是并發修改異常問題。
示例
package Collection;import java.util.ArrayList;
import java.util.Iterator;public class CollectionDemo6 {public static void main(String[] args) {//目標:認識并發修改異常問題,搞清楚每種遍歷的區別ArrayList<String> list = new ArrayList<>();list.add("Java入門");list.add("黑枸杞");list.add("寧夏枸杞");list.add("枸杞");list.add("特級枸杞");list.add("枸杞子");list.add("西洋參");System.out.println(list);//[Java入門, 黑枸杞, 寧夏枸杞, 枸杞, 特級枸杞, 枸杞子, 西洋參]for (int i = 0; i < list.size(); i++) {String s = list.get(i);if (s.contains("枸杞")){list.remove(s);}}System.out.println(list); //枸杞沒有刪完//[Java入門, 寧夏枸杞, 特級枸杞, 西洋參]//原因是當遍歷到了黑枸杞并刪除時,此時索引變成了2,但因為黑枸杞被刪了,寧夏枸杞的索引變成了1,所以索引為1的元素被跳過了System.out.println("========================================================");ArrayList<String> list2 = new ArrayList<>();list2.add("Java入門");list2.add("黑枸杞");list2.add("寧夏枸杞");list2.add("枸杞");list2.add("特級枸杞");list2.add("枸杞子");list2.add("西洋參");System.out.println(list2);//解決方案1:刪除時索引-1
// for (int i = 0; i < list2.size(); i++)
// {
// String s = list2.get(i);
// if (s.contains("枸杞"))
// {
// list2.remove(i);
// i--;//刪除后索引要減一
// }
// }//方案二:倒著遍歷 (支持索引)for (int i = list2.size()-1; i >=0 ; i--) {String s = list2.get(i);if (s.contains("枸杞"))list2.remove(s);}System.out.println(list2);System.out.println("========================================================");//三種遍歷的區別ArrayList<String> list3 = new ArrayList<>();list3.add("Java入門");list3.add("黑枸杞");list3.add("寧夏枸杞");list3.add("枸杞");list3.add("特級枸杞");list3.add("枸杞子");list3.add("西洋參");System.out.println(list3);//一、迭代器遍歷刪除默認也存在并發修改異常問題Iterator<String> it = list3.iterator();while (it.hasNext()){String s = it.next();if (s.contains("枸杞"))//it.remove(s);it.remove();//用迭代器自己的方法刪}System.out.println(list3);System.out.println("========================================================");ArrayList<String> list4 = new ArrayList<>();list4.add("Java入門");list4.add("黑枸杞");list4.add("寧夏枸杞");list4.add("枸杞");list4.add("特級枸杞");list4.add("枸杞子");list4.add("西洋參");System.out.println(list4);//二、三:增強for和Lambda(無法解決并發修改異常)
// for (String s : list4) {
// if (s.contains("枸杞"))
// list4.remove(s);
// }//結論:增強for和Lambda只適合做遍歷,不適合遍歷并修改
// System.out.println(list4);list4.removeIf(s -> s.contains("枸杞"));System.out.println(list4);}
}
迭代器遍歷內有專門解決這個問題的方法,即迭代器.remove(),刪掉當前迭代器指向的值并且指針不動。用正常for循環時,可以用每刪一個值指針就向前一位解決。還有一種方法就是從后往前遍歷,刪除后面的數據對前面的數據不造成影響,也可以正常刪除。而增強for和Lambda則沒有方法解決這個并發修改異常問題。因此,得出結論,增強for與Lambda適合用于遍歷,而遍歷同時進行修改則需要使用別的遍歷方法。