Collection
數據結構,容器:
用來裝對象…,等各種管理對象的容器。
容器有相同的操作標準:
(1)增
(2)刪
(3)改
(4)查
…
因爲集合的類型很多,那麼我們把它們稱爲集合框架。
Java給這些集合抽取兩大接口:
- 1、Collection:
-
規範單值集合的接口,obj 單個
- 2、Map
-
規範對值集合的接口,(key,value) 兩個
- Collection Map
也就是把集合框架分爲兩個家族:Collection(一組對象)和Map(一組映射關係、一組鍵值對)
Collection Map
一組對象 一組映射關係、一組鍵值對
Colleciton Map
1、Collection:接口
Collection:接口
(1)它是根接口
(2)它沒有直接的實現類,有更具體的子接口:List和Set…
(3)有一些的元素是可以重複的,有些集合的元素是不能重複,有些集合的元素是有序的,有些集合的元素是無序的
Collection是代表一種對象的集合。它是Collection系列的根接口。
它們雖然:有些可能是有序的,有些可能是無序的,有些可能可以重複的,有些不能重複的,但是它們有共同的操作規範,因此這些操作的規範就抽象爲了Collection接口。
常用方法:
(1)boolean add(Object obj):添加一個
(2)boolean addAll(Collection c):添加多個
(3)boolean remove(Object obj):刪除一個
(4)boolean removeAll(Collection c ): 刪除多個
(5)boolean contains(Object c):是否包含某個
(6)boolean containsAll(Collection c): 是否包含所有
(7)boolean isEmpty():是否爲空
(8)int size():獲取元素個數
(9)void clear():清空集合
(10)Object[] toArray():獲取所有元素
(11)Iterator iterator(): 獲取遍歷當前集合的迭代器對象
(12)retainAll(Collection c):求當前集合與c集合的交集
2、Collection API
(1)添加
add(Object obj):添加一個元素
addAll(Collection c):添加多個元素
(2)獲取有效元素的個數
int size()
@SuppressWarnings("all")
@Test
public void test1() {
/*
* 我這裏左邊寫Collection,目的是隻關注Collection
* 因爲多態引用時,c編譯期間只能訪問Collection的方法
*/
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
c.add("柳六在");
Collection c2 = new ArrayList();
c2.add("柳七七");
c2.add("柳八八");
c2.add("柳九九");
System.out.println("獲取有效元素的個數:" + c2.size()); //獲取有效元素的個數:3
c.addAll(c2);
System.out.println("獲取有效元素的個數:" + c.size());//獲取有效元素的個數:7
System.out.println(c);//[張三, 李四, 王五, 柳六在, 柳七七, 柳八八, 柳九九]
}
demo
@SuppressWarnings("all")
@Test
public void test2() {
/*
* 我這裏左邊寫Collection,目的是隻關注Collection
* 因爲多態引用時,c編譯期間只能訪問Collection的方法
*/
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add(1);
c.add(2);
c.add(3);
Collection c2 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c2.add("張三");
c2.add("李四");
c2.add("王五");
c.addAll(c2);//把c2中的所有元素都添加到c集合中
// c.add(c2);
System.out.println("獲取有效元素的個數:" + c.size());//獲取有效元素的個數:6
System.out.println(c);//[1, 2, 3, 張三, 李四, 王五]
}
(3)是否包含
contains(Object o) :判斷o是否在當前的集合中
containsAll(Collection c) :判斷c是否是當前集合的子集
@SuppressWarnings("all")
@Test
public void test3() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
System.out.println(c.contains("張三"));//true
System.out.println(c.contains("李大炮"));//false
}
containsAll
@SuppressWarnings("all")
@Test
public void test4() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c2.add("張三");
c2.add("李四");
Collection c3 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c3.add("張三");
c3.add("李大炮");
System.out.println(c.containsAll(c2));//true c2是c的子集
System.out.println(c.containsAll(c3));//false c3不是c的子集
}
(4)boolean isEmpty() :判斷當前集合是否爲空
等價於 集合對象.size()==0
(5)remove(Object o):刪除一個
removeAll(Collection c):刪除多個 this = this - this ∩ c
clear():清空所有
remove
@SuppressWarnings("all")
@Test
public void test5() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
////說明下ArrayList重寫了toString的
System.out.println(c);//[張三, 李四, 王五]
c.remove("張三");//刪除一個
System.out.println(c);//[李四, 王五]
c.remove("六小子");//刪除個不存在的 不影響
System.out.println(c);//[李四, 王五]
}
removeAll this = this - this ∩ c
@SuppressWarnings("all")
@Test
public void test6() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
System.out.println(c);//[張三, 李四, 王五]
Collection c2 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c2.add("張三");
c2.add("李四");
c2.add("柳小子");
System.out.println(c2);//[張三, 李四, 柳小子]
c.removeAll(c2);
System.out.println(c);//說明ArrayList重寫了toString
//[王五]
}
clear()
@SuppressWarnings("all")
@Test
public void test7() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c2.add("張三");
c2.add("柳可愛");
c.addAll(c2);//直接加 重複的也加
////說明ArrayList重寫了toString
System.out.println(c);//[張三, 李四, 王五, 張三, 柳可愛]
c.removeAll(c2);
System.out.println(c);//[李四, 王五]
System.out.println(c.size());//2
c.clear();
System.out.println(c.isEmpty());//true
System.out.println(c.size());//0
}
(6)retainAll(Collection<?> c) :保留交集 this = this ∩ c
@SuppressWarnings("all")
@Test
public void test8() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
Collection c2 = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c2.add("張三");
c2.add("楊三寶");
c.retainAll(c2);
System.out.println(c);//[張三]
System.out.println(c2);//[張三, 楊三寶]
c.addAll(c2);
System.out.println(c);//[張三, 張三, 楊三寶]
c.addAll(c);
System.out.println(c);//[張三, 張三, 楊三寶, 張三, 張三, 楊三寶]
System.out.println(c2);//[張三, 楊三寶]
//retainAll(Collection<?> c) :保留交集 this = this ∩ c
c.retainAll(c2);
System.out.println(c);//[張三, 張三, 楊三寶, 張三, 張三, 楊三寶]
}
(7)Object[] toArray() :把集合中的元素用一個數組返回
@SuppressWarnings("all")
@Test
public void test9() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
System.out.println(c);//[張三, 李四, 王五]
Object[] all = c.toArray();
System.out.println(all);//[Ljava.lang.Object;@4f2410ac
System.out.println(all.length);//3
System.out.println(Arrays.toString(all));//[張三, 李四, 王五]
}
2、Collection系列的集合的遍歷
集合的遍歷 挨個訪問集合的元素
(1)Object[] toArray():先返回數組,然後遍歷數組
@SuppressWarnings("all")
@Test
public void test9() {
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
System.out.println(c);//[張三, 李四, 王五]
Object[] all = c.toArray();
System.out.println(all);//[Ljava.lang.Object;@4f2410ac
System.out.println(all.length);//3
System.out.println(Arrays.toString(all));//[張三, 李四, 王五]
for (int i = 0; i < all.length; i++) {
System.out.println(all[i]);
}
//Result
//張三
//李四
//王五
}
(2)迭代器設計模式 明確使用Iterator迭代器
每一個Collection系列的集合,內部都自帶一個迭代器
java.util.Iterator:接口
它是所有迭代器的標準接口。
Iterator 接口的方法:
(1)boolean hasNext()
(2)Object next()
(3)void remove()
(1)判斷是否還有下一個元素:hasNext()
(2)訪問它的下一個元素:next()
(3)移除下一個元素:remove()
java.util.Iterator:迭代器接口,這個接口的實現類在每一種集合類中,例如:ArrayList內部有一個內部類實現了Iterator接口
這裏聲明爲內部類有兩個原因:
(1)每一種集合的內部實現(物理結構不同),意味着對迭代器的實現是不同的,每一種集合都要單獨定製迭代器。
(2)內部類可以直接訪問外部類的私有的屬性,成員,迭代器就可以直接訪問集合的私有的元素。
Collection c = ....;
Iterator iter = c.iterator();
while(iter.hashNext()){
Object obj = iter.next();
//...
}
demo
@SuppressWarnings("all")
@Test
public void test1(){
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
//返回這個集合自帶的迭代器對象
//讓這個迭代器對象去挨個的訪問元素
Iterator iterator = c.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
//Result
//張三
//李四
//王五
}
@SuppressWarnings("all")
@Test
public void test2(){
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
//返回這個集合自帶的迭代器對象
//讓迭代器去挨個的訪問元素
Iterator iterator = c.iterator();
while(iterator.hasNext()){
String obj = (String) iterator.next();
//要姓“王”走人
if(obj.startsWith("王")){
iterator.remove();
}
}
System.out.println(c);//[張三, 李四]
}
2、foreach:增強for循環
foreach循環可以用於遍歷數組、Collection系列的集合等容器。
語法結構:
for(元素的類型 元素臨時名稱 : 數組和集合名){
}
不同於普通for循環。
for(int i=0; i<5; i++){
}
什麼樣集合或容器類型可以使用foreach循環?
(1)數組:
(2)實現了java.lang.Iterable接口
這個接口有一個抽象方法:Iterator iterator()
Iterator也是一個接口,它的實現類,通常在集合(容器)類中用內部類實現。並在iterator()的方法中創建它的對象。
凡是實現了java.lang.Iterable接口(可迭代)的集合或容器都支持foreach循環
foreach底層還是調用Iterator迭代器來遍歷集合。
Collection c = ....;
for(Object obj : c){
//...
}
Demo
@SuppressWarnings("all")
@Test
public void test3(){
Collection c = new ArrayList();//ArrayList是Collection下面的一個實現類而已
c.add("張三");
c.add("李四");
c.add("王五");
//Object:元素的數據類型
//obj:臨時的元素名稱
//c:要遍歷的集合的名稱
for (Object obj : c) {
System.out.println(obj);
}
//Result
//張三
//李四
//王五
}
public class MyArrayList implements Iterable{
//爲什麼使用Object,因爲只是說這個容器是用來裝對象的,但是不知道用來裝什麼對象。
private Object[] data;
private int total;
//其他代碼省略....
@Override
public Iterator iterator() {
return new MyItr();
}
private class MyItr implements Iterator{
private int cursor;//遊標
@Override
public boolean hasNext() {
return cursor!=total;
}
@Override
public Object next() {
return data[cursor++];
}
}
}
MyArrayList.java
import java.util.Arrays;
import java.util.Iterator;
/*
* MyArrayList我們自己設計的一種數據結構,一種邏輯結構,當別人用我這個MyArrayList的對象時,就是一個容器對象,
* 可以用來裝對象。
*/
public class MyArrayList implements Iterable{
//爲什麼使用Object,因爲只是說這個容器是用來裝對象的,但是不知道用來裝什麼對象。
private Object[] data;
private int total;
public MyArrayList(){
data = new Object[5];
}
//添加一個元素
public void add(Object obj){
//檢查是否需要擴容
checkCapacity();
data[total++] = obj;
}
private void checkCapacity() {
//如果data滿了,就擴容爲原來的2倍
if(total >= data.length){
data = Arrays.copyOf(data, data.length*2);
}
}
//返回實際元素的個數
public int size(){
return total;
}
//返回數組的實際容量
public int capacity(){
return data.length;
}
//獲取[index]位置的元素
public Object get(int index){
//校驗index的合理性範圍
checkIndex(index);
return data[index];
}
private void checkIndex(int index) {
if(index<0 || index>=total){
throw new RuntimeException(index+"對應位置的元素不存在");
// throw new IndexOutOfBoundsException(index+"越界");
}
}
//替換[index]位置的元素
public void set(int index, Object value){
//校驗index的合理性範圍
checkIndex(index);
data[index] = value;
}
//在[index]位置插入一個元素value
public void insert(int index, Object value){
/*
* (1)考慮下標的合理性
* (2)總長度是否夠
* (3)[index]以及後面的元素往後移動,把[index]位置騰出來
* (4)data[index]=value 放入新元素
* (5)total++ 有效元素的個數增加
*/
//(1)考慮下標的合理性:校驗index的合理性範圍
checkIndex(index);
//(2)總長度是否夠:檢查是否需要擴容
checkCapacity();
//(3)[index]以及後面的元素往後移動,把[index]位置騰出來
/*
* 假設total = 5, data.length= 10, index= 1
* 有效元素的下標[0,4]
* 移動:[1]->[2],[2]->[3],[3]->[4],[4]->[5]
* 移動元素的個數:total-index
*/
System.arraycopy(data, index, data, index+1, total-index);
//(4)data[index]=value 放入新元素
data[index] = value;
//(5)total++ 有效元素的個數增加
total++;
}
//返回所有實際存儲的元素
public Object[] getAll(){
//返回total個
return Arrays.copyOf(data, total);
}
//刪除[index]位置的元素
public void remove(int index){
/*
* (1)校驗index的合理性範圍
* (2)移動元素,把[index+1]以及後面的元素往前移動
* (3)把data[total-1]=null 讓垃圾回收器儘快回收
* (4)總元素個數減少 total--
*/
//(1)考慮下標的合理性:校驗index的合理性範圍
checkIndex(index);
//(2)移動元素,把[index+1]以及後面的元素往前移動
/*
* 假設total=8, data.length=10, index = 3
* 有效元素的範圍[0,7]
* 移動:[4]->[3],[5]->[4],[6]->[5],[7]->[6]
* 移動了4個:total-index-1
*/
System.arraycopy(data, index+1, data, index, total-index-1);
//(3)把data[total-1]=null 讓垃圾回收器儘快回收
data[total-1] = null;
// (4)總元素個數減少 total--
total--;
}
//查詢某個元素的下標
/* public int indexOf(Object obj){
for (int i = 0; i < total; i++) {
//這兩種寫法都有風險
if(obj.equals(data[i])){
//if(data[i].equals(obj)){
return i;//找到,返回第一個找到的
}
}
return -1;//沒找到返回-1
}*/
//查詢某個元素的下標
public int indexOf(Object obj){
if(obj == null){
for (int i = 0; i < total; i++) {
if(data[i] == null){//等價於 if(data[i] == obj)
return i;
}
}
}else{
for (int i = 0; i < data.length; i++) {
if(obj.equals(data[i])){
return i;
}
}
}
return -1;
}
//刪除數組中的某個元素
//如果有重複的,只刪除第一個
public void remove(Object obj){
/*
* (1)先查詢obj的[index]
* (2)如果存在,就調用remove(index)刪除就可以
*/
//(1)先查詢obj的[index]
int index = indexOf(obj);
if(index != -1){
remove(index);
}
//不存在,可以什麼也不做
//不存在,也可以拋異常
//throw new RuntimeException(obj + "不存在");
}
public void set(Object old, Object value){
/*
* (1)查詢old的[index]
* (2)如果存在,就調用set(index, value)
*/
// (1)查詢old的[index]
int index = indexOf(old);
if(index!=-1){
set(index, value);
}
//不存在,可以什麼也不做
//不存在,也可以拋異常
//throw new RuntimeException(old + "不存在");
}
@Override
public Iterator iterator() {
return new MyItr();
}
private class MyItr implements Iterator{
private int cursor;//遊標
@Override
public boolean hasNext() {
System.out.println("還有下一個");
return cursor!=total;
}
@Override
public Object next() {
System.out.println("拿到下一個");
return data[cursor++];
}
}
}
@SuppressWarnings("all")
@Test
public void test5() {
//我自己寫的動態數組
MyArrayList list = new MyArrayList();
list.add("張三");
list.add("李四");
list.add("王五");
for (Object obj : list) {
System.out.println(obj);
}
//Result
//還有下一個
//拿到下一個
//張三
//還有下一個
//拿到下一個
//李四
//還有下一個
//拿到下一個
//王五
//還有下一個
}
思考:如果遍歷數組,什麼情況下選用foreach,什麼情況下選用for循環?
當如果你的操作中涉及到[下標]操作時,用for最好。
當你只是查看元素的內容,那麼選foreach更簡潔一些。
思考:如果遍歷Collection系列集合,什麼情況下選用foreach,是否能選用for循環?
首先考慮使用foreach,如果該集合也有索引信息的話,也可以通過for來操作,如果沒有下標的信息,就不要用for。即,如果該集合的物理結構是數組的,那麼可以用for,如果物理結構是鏈式,那麼使用下標操作效率很低。
思考:如果遍歷Collection系列集合,什麼情況下選用foreach,什麼情況下使用Iterator?
如果只是查看集合的元素,使用foreach,代碼會更簡潔。
但是如果要涉及到在遍歷集合的同時根據某種條件要刪除元素等操作,那麼選用Iterator。
參考資料
記錄 - 搞定Java核心技術