什么是集合
簡單來講:集合就是一個放數據的容器,准確的說是放引用數據的容器。
為什么要使用集合框架?
傳統的容器(數組)在進行增、刪等破壞性操作時,需要移動元素,可能導致性能問題;同時添加、刪除等算法和具體業務耦合在一起,增加了程序開發的復雜度。
Java集合框架提供了一套性能優良、使用方便的接口和類,它們位於java.util包中
package cn.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /**Collection是java集合框架(collection-frame)中的頂層接口。 Collection接口是一個容器,容器中只能存儲引用數據類型,建議存同一類型的引用類型,方便后續遍歷等操作。 容器中的元素可以是有序的、可重復的,稱為List接口 也可能是無序的、唯一的,稱為Set接口。 * * * */ public class TestCollection { public static void main(String[] args) { /**通過實現類來實現 * 增:add/addAll * 刪:clear/remove/removeAll/retainAll * 改: * 查:contains/containsAll/isEmpty/size */ Collection c1=new ArrayList();//通過ArrayList類(本質上是List接口的實現類)實現Collection接口 //通過ArrayList類把引用數據追加給Collection容器 c1.add("apple"); // Object object = new String("apple"); c1.add("banana");//把引用數據類型加入Collection容器 System.out.println(c1);//輸出結果:[apple, banana] //基本數據類型通過包裝類存入Collection // c1.add(1); // Object object = new Integer(1); // 追加一個集合 Collection c2 = new ArrayList(); c2.add("java");//把引用數據類型加入Collection容器 c2.add("c+"); c2.add("c"); c2.add("c++"); c2.add("a++"); c2.add("d++"); c1.addAll(c2);//可以直接把一個集合追加給另一個相同類型的集合 System.out.println(c1);//輸出結果:[apple, banana, java, c+, c, c++, a++, d++] Collection c3=new ArrayList(); c3.add("d++"); c3.add("ff++"); //刪除列表中的所有元素。 //c1.clear(); System.out.println(c1);//輸出結果:[] // 刪除此列表中首次出現的指定元素(如果存在,從左往右)。 c2.remove("c++"); System.out.println(c2);//輸出結果:[java, c+, c, a++, d++] //刪除c1中的和c3有同樣的元素(刪除兩個容器中同樣的元素) c1.removeAll(c3); System.out.println(c1);//輸出結果:[apple, banana, java, c+, c, c++, a++] //提取兩個容器中相同的元素,然后清空c1,再把相同元素追加給c1 c1.retainAll(c2); System.out.println(c1);//輸出結果:[java, c+, c, a++] //如果此列表中包含指定的元素,則返回 true。 System.out.println(c1.contains("apple"));//輸出結果:false System.out.println(c2);//輸出結果:[java, c+, c, a++, d++] c1.add("d++"); //c1和c2是否相等 System.out.println(c1.containsAll(c2));//輸出結果:true //c1是否為空 //c1.clear();//清空c1//輸出結果:true System.out.println(c1.isEmpty());//輸出結果:false // 返回集合元素的個數 System.out.println(c1.size());//輸出結果:5 //c1和c2內元素是否相等 System.out.println(c1.equals(c2));//輸出結果:true Collection c4 = new ArrayList(); c4.add("apple"); c4.add("banana"); c4.add("coco"); // 快速遍歷 // for-each // Object 表示元素類型 // object 表示迭代變量(循環輸出的變量) // c1表示集合 for (Object object : c4) { System.out.println(object.toString());//輸出結果:apple(循環輸出) } // banana // coco /**Iterator接口:幾乎所有接口的都繼承與它的超級接口 * 有hasNext方法:如果仍有元素可以迭代,則返回 true。 * next()方法:返回迭代的下一個元素。 * remove() : 從迭代器指向的 collection 中移除迭代器返回的最后一個元素(可選操作)。 */ // 迭代器遍歷(國內) /**快速遍歷的本質 * Collection繼承Iterable接口,表示集合支持快速遍歷。Iterable接口定義了一個方法iterator()用於獲取集合的迭代器, 是一個Iterator接口類型,iterator()內部返回一個實現類實現類Iterator接口。 這個實現類一定具有hasNext和next方法用於判斷是否有下一個元素和獲取下一個元素。快速遍歷就是基於迭代器工作的。 */ //Iterator是對 collection 進行迭代的迭代器,蘊含了迭代的能力的接口 Iterator it = c4.iterator();////iterator是Iterable類的方法,通過方法的接口Iterator返回在此 collection 的元素上進行迭代的迭代器。 while(it.hasNext()) { //是否還有下個元素 Object item = it.next(); //Object是幾乎所有類的父類,所以通過父類引用子類來實現方法 System.out.println(item.toString());//輸出結果:apple(循環輸出) } // banana // coco // 國外 for(Iterator it2=c4.iterator();it2.hasNext();) { Object item = it2.next(); System.out.println(item.toString()); } } }
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /**1.3List接口(繼承了collection接口) List 接口中的元素時有序的、可重復的。List接口中的元素通過索引(index)來確定元素的順序。 有序的 collection(也稱為序列)。可以對列表中每個元素的插入位置進行精確地控制。 用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素 */ public class ArraysList { public static void main(String[] args) { /**通過List接口實現類來實現,比collection接口本身使用實現類來實現更完善方法更多 * 增:add/addAll/add(index,el)/addAll(index,collection) * 刪:clear/remove/removeAll/remove(index) * 改:set(index,el) * 查:get(index)/indexOf/lastIndexOf() * 其他:contains/containsAll/isEmpty/size */ List list1 = new ArrayList();//接口引用實現類,ArrayList類是collection接口和List接口共有的實現類 // 添加元素 list1.add("apple"); list1.add("banana"); // 在指定位置添加元素 list1.add(0, "coco"); System.out.println(list1);//輸出結果:[coco, apple, banana] List list2 = new ArrayList(); list2.add("java"); list2.add("c++"); list1.addAll(1, list2); //把list2的元素放在list1的第一個位置 System.out.println(list1);//輸出結果:[coco, java, c++, apple, banana] // 刪除 //從此列表中移除第一次出現的指定元素(如果存在)(可選操作)。 list1.remove("coco"); System.out.println(list1);//輸出結果:[java, c++, apple, banana] //移除列表中指定位置的元素(可選操作)。 list1.remove(0); System.out.println(list1);//輸出結果:[c++, apple, banana] // 修改:指定元素替換列表中指定位置的元素(可選操作)。 list1.set(0, "javax"); System.out.println(list1);//輸出結果:[javax, apple, banana] // 查: 返回列表中指定位置的元素。 System.out.println(list1.get(0));//輸出結果:javax list1.add("apple"); list1.add("apple"); //List:元素是有序的、可重復的。 System.out.println(list1);//輸出結果:[javax, apple, banana, apple, apple] //從左到右返回此列表中第一次出現的指定元素的索引;如果此列表不包含該元素,則返回 -1。 System.out.println(list1.indexOf("apple"));//輸出結果:1 //從右到左返回此列表中第一次出現的指定元素的索引;如果此列表不包含該元素,則返回 -1。 System.out.println(list1.lastIndexOf("apple"));//輸出結果:4 /**ListIterator 繼承於Iterator,在Iterator的基礎上提供了以正向遍歷集合,也可以以逆序遍歷集合。 * 所以有hasNext/next 以正向遍歷 還多了 hasPrevious/previous 以逆序遍歷 */ List list3 = new ArrayList(); list3.add("apple"); list3.add("banana"); list3.add("coco"); // 【1】快速遍歷 for (Object item : list3) { System.out.println(item.toString());//輸出結果:apple banana coco } // 【2】普通for(collection接口不行) //size()返回列表中的元素數。 for(int i=0;i<list3.size();i++) {//因為知道元素個數,所以通過循序遍歷輸出 System.out.println(list3.get(i));//輸出結果:apple banana coco } // 【3】集合迭代器 Iterator it = list3.iterator(); while(it.hasNext()) { System.out.println(it.next());//輸出結果:apple banana coco } //ListIterator接口是繼承了Iterator接口,所以ListIterator接口能力更強大 // listIterator()返回此列表元素的列表迭代器(按適當順序) //listIterator(int index) 返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始。 // 正向遍歷 ListIterator it2 = list3.listIterator(); while(it2.hasNext()) { System.out.println(it2.next());//輸出結果:apple banana coco } // 逆序遍歷 //hasPrevious()逆序輸出 while(it2.hasPrevious()) { System.out.println(it2.previous());//輸出結果: coco banana apple } ////listIterator(int index) 返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始。 ListIterator it3 = list3.listIterator(1); while(it3.hasNext()) { System.out.println(it3.next());//輸出結果:banana coco } } }
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /**ArrayList 是List接口的實現類,底層數據結構是數組,實現大小可變的數組。 ArrayList 線程不安全,jdk1.2 ArrayList 底層數據結構是數組,默認數組大小是10,如果添加的元素個數超過默認容量, ArrayList會自動拓容,拓容原則:newCapacity = oldCapacity + oldCapacity / 2; 如果未來確定序列的元素不在增加,通過調用trimToSize()調制容量至合適的空間。 ArrayList作為List接口的實現類,常用方法和遍歷方法參考List接口。 */ public class ArraysList01 { public static void main(String[] args) { ArrayList list1 = new ArrayList(); list1.add("apple1"); list1.add("apple2"); list1.add("apple3"); list1.add("apple4"); list1.add("apple5"); list1.add("apple6"); list1.add("apple7"); list1.add("apple8"); list1.add("apple9"); list1.add("apple10"); System.out.println(list1.size()); //結果輸出:10 ArrayList list = new ArrayList(); list.add("apple1"); list.add("apple2"); list.add("apple3"); for (Object item: list) { System.out.println(item);//結果輸出:apple1 apple2 apple3 } for (int i = 0; i < list.size(); i++) { Object item = list.get(i); System.out.println(item);//結果輸出:apple1 apple2 apple3 } Iterator it = list.iterator(); while(it.hasNext()) { System.out.println(it.next());//結果輸出:apple1 apple2 apple3 } ListIterator it2 = list.listIterator(); while(it2.hasNext()) { System.out.println(it2.next());}//結果輸出:apple1 apple2 apple3 } }
package cn.collection; import java.util.ListIterator; import java.util.Vector; /*Vector 是List接口的實現類,底層數據結構也是數組,也是大小可變的數組。 Vector是線程安全的,jdk1.0 Vector底層數據結構是數組,默認數組大小是10,如果添加的元素個數超過默認容量,Vector會自動拓容, 拓容原則:newCapacity = oldCapacity +capacityIncrement(增長因子); 如果未來確定序列的元素不在增加,通過調用trimToSize()調制容量至合適的空間。 注意:Vector 在實現List接口的同時,同添加了自身特有的方法xxxElement, 未來使用時為了程序的可拓展性,一定要按照接口來操作Vector。*/ public class Vector01 { public static void main(String[] args) { Vector list = new Vector(); list.add("apple1"); list.add("apple2"); System.out.println(list.size());//輸出結果:2 // 返回此向量的當前容量。 System.out.println(list.capacity());//輸出結果:10 // 返回此向量中第一次出現的指定元素的索引,從 index 處正向搜索,如果未找到該元素,則返回 -1。 System.out.println(list.indexOf("apple2",1));//輸出結果:1 //將指定的元素添加在容器末尾,容量加1 list.addElement("apple3"); ListIterator it = list.listIterator(); while(it.hasNext()) { System.out.println(it.next());}//結果輸出:apple1 apple2 apple3 // vector:特有的方法都加了:Element } }
package cn.collection; import java.util.LinkedList; /*LinkedList是List接口的實現類,底層數據結構是鏈表。 LinekList常用方法和遍歷方法參照List接口。 LinkedList 線程不安全。 除了實現List接口, 還實現棧接口 */ //push入棧操作 pop出棧操作 public class LinkedList01 { public static void main(String[] args) { //push入棧操作(add/remove/element() 可能會出現NoSuchElementException異常) LinkedList list = new LinkedList(); list.push("apple"); list.push("banana"); list.push("coco"); System.out.println(list.element());//獲取表頭元素:輸出結果:coco(先入的在最底下) // pop出棧操作:從此列表所表示的堆棧處彈出一個元素。(先入后出)入口和出口相同 System.out.println(list.pop());//輸出結果:coco System.out.println(list.pop());//輸出結果:banana System.out.println(list.pop());//輸出結果:apple //如果容器內已沒有元素,再次 pop出棧操作就會 異常輸出 //System.out.println(list.pop());// 輸出結果:java.util.NoSuchElementException(沒有元素異常) //以隊列形式操作LinkedList,(先入后出)一個入口和一個出口(分開來的) LinkedList queue = new LinkedList(); // 入隊 /** * 隊列頭 隊列尾 *<----- <----- * [apple, banana, coco] */ queue.add("apple");//將指定元素添加到此列表的結尾。 queue.add("banana"); queue.add("coco"); System.out.println(queue);//輸出結果:[apple, banana, coco] // 獲取表頭元素 System.out.println(queue.element());//輸出結果:apple // 出隊, System.out.println(queue.remove());//輸出結果:apple System.out.println(queue.remove());//輸出結果:banana System.out.println(queue.remove()); //輸出結果:coco System.out.println(queue);//輸出結果:[] //System.out.println(queue.remove());// java.util.NoSuchElementException // 入隊(offer/poll/peek 可能會返回特殊值(null)) /**先入先出 * 隊列頭 隊列尾 *<----- <----- * [apple, banana, coco] */ LinkedList list1 = new LinkedList(); list1.offer("apple"); list1.offer("banana"); list1.offer("coco"); System.out.println(list1);//輸出結果:[apple, banana, coco] System.out.println(list1.peek());//輸出結果:apple // 出隊列 System.out.println(list1.poll());//輸出結果:apple System.out.println(list1.poll());//輸出結果:banana System.out.println(list1.poll());//輸出結果:coco //獲取但不移除此列表的頭(第一個元素)。 System.out.println(list1.peek());//輸出結果:null(因為已經全部取出了,容器已沒有元素) LinkedList queue2 = new LinkedList(); // 入隊 /**頭和尾同時輸出 *<----- <----- * [apple, banana, coco] * ----> -----> */ queue2.addFirst("apple"); queue2.addFirst("banana"); queue2.addFirst("coco"); System.out.println(queue2);//輸出結果:[coco, banana, apple] System.out.println(queue2.getFirst());//表頭:輸出結果:coco //先輸出尾在輸出頭最后才輸出中間 System.out.println(queue2.removeLast());//輸出結果:apple System.out.println(queue2.removeFirst());//輸出結果:coco System.out.println(queue2.removeFirst());//輸出結果:banana System.out.println(queue2);//輸出結果:[] // 獲取頭元素 System.out.println(queue2.getFirst());//輸出結果: java.util.NoSuchElementException } }
數據結構(補充)
數據結構就是數據在內存中存儲結構。根據存儲的方式不同,分為線性表、二叉樹、圖、棧、隊列等
線性表
線性表數據按照一定的邏輯順序存儲在內存中。線性表是有序的。線性表根據內存的物理結構分為兩種:數組和鏈表
數組是一種邏輯上有序的線性表,物理上也連續。
|
鏈表是一種邏輯上有序的線性表,但物理上不連續。
|
數組和鏈表的區別
數組在查詢時效率高,在添加、刪除元素時效率低(涉及移動元素)
鏈表在查詢時效率低(每次從頭開始,不能跳躍訪問),在添加、刪除元素時效率高(不涉及移動元素)
棧
特性:先進后出,后進先出
|
隊列
特性:先進先出
![]()
|
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /*Iterator在迭代過程中不允許向集合中添加元素 * 但ListIterator接口繼承於Iterator,在這基礎上擁有了在運行時增加元素的功能
當通過Iterator集合迭代器遍歷集合過程中,不能再向集合匯總添加元素,否則出現ConcurrentModificationException 並發修改異常。
ListIterator允許程序員按任一方向遍歷列表、迭代期間修改列表,並獲得迭代器在列表中的當前位置* */
public class Iterator1 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("apple"); list.add("banana"); list.add("coco"); /*Iterator it=list.iterator(); while(it.hasNext()) { String item = (String) it.next(); if(item.equals("banana")) { it.add("test");輸出過程中增加元素(直接編譯不了,屬於檢查性異常) } } System.out.println(list);*/ //ListIterator接口繼承於Iterator,在這基礎上擁有了在運行時增加元素的功能 ListIterator it1 = list.listIterator(); while(it1.hasNext()) { String item = (String) it1.next(); if(item.equals("banana")) { it1.add("test"); } } System.out.println(list);//輸出結果:[apple, banana, test, coco] } }
package cn.collection; //泛型類:當一個類中屬性的數據類型不確定時,具體是什么類型由使用者來確定時,使用泛型。泛型類的形式public class 類名<T> {} public class FanClass<T> { private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } public FanClass(T t) { super(); this.t = t; } public FanClass() { super(); } public void fan(){ System.out.println(t); } }
package cn.collection; import java.util.Arrays; /**/ public class Student { /*如果定義一個不知道什么類型的方法重載,要把每個類型寫一遍,但用泛類型只用寫一個就可以了 * 泛型方法可以定義多個泛型類型 多個泛型類型進一步優化了方法重載。 public void showInfo(int a) { System.out.println(a); } public void showInfo(float a) { System.out.println(a); } public void showInfo(String a) { System.out.println(a); }*/ /*泛型方法:當一個方法的參數類型不確定時,具體是什么類型由使用者來確定,可以考慮使用泛型方法。 形式:public <T> void xxx(T a) {System.out.println(a);} 泛型方法在調用時確定(指明)類型。 泛型方法在一定程度上優化了方法重載。*/ //用泛類型的方法重載 public <T> void showInfo(T a) { System.out.println(a); } // 可以定義多個泛型的類型 public <A,B> void showInfo(A a,B b) { System.out.println(a); System.out.println(b); } // 多個同類型的泛型 public <A> void print(A a) { System.out.println(a); } public <A> void print(A a,A b) { System.out.println(a); System.out.println(b); } /*多個同類型的泛型 A… a 表示方法可以接受多個參數。當調用方法傳遞多個參數時,多個參數被放到a數組中,a是什么類型的數組由開發者調用處傳參決定。 print(A...a) 方法稱為可變參數的泛型形式。*/ public void foo(int...a) {//多個同類型的int組成的數組a System.out.println(a); System.out.println(Arrays.toString(a)); } public <A> void print(A...a) {//多個同類型的泛型組成的數組a // a 是一個數組 // System.out.println(a); System.out.println(Arrays.toString(a)); } }
package cn.collection; //泛型(generic) /*泛型的概念: * 泛型允許開發者在強類型程序設計語言(java)編寫代碼時定義一些可變部分,這些部分在使用前必須作出指明。 泛型就是將類型參數化 ArrayList<E> list表示聲明了一個列表list,列表的元素是E類型 ArrayList<String> list = new ArrayList<String>(); 聲明了一個列表list,列表的元素只能是String類型。 泛型在編譯器起作用,運行時jvm察覺不到泛型的存在。泛型在運行時已經被擦除了。 * */ public class Test { public static void main(String[] args) { //在提取時確定類型 FanClass<String> fan = new FanClass<String>(); fan.setT("apple"); fan.fan();//輸出結果:apple FanClass<Integer> fan2 = new FanClass<Integer>(); fan2.setT(1); fan2.fan();//輸出結果:1 Student stu = new Student(); stu.showInfo(1);//輸出結果:1 stu.showInfo("apple",1.0f);//輸出結果:apple 1.0 //stu.print(1, "apple");不行 //必須stu.print(1, 5);同類型 stu.print(1);//輸出結果:1 stu.print(1,2);//輸出結果:1 2 stu.print("apple");//輸出結果:apple stu.print("apple","banana");//輸出結果:apple banana stu.foo(1,2,3,4,5,6);//輸出結果:[I@747541f8(證明它是一個數組) //輸出結果:[apple, banana, banana, banana, banana] stu.print("apple","banana","banana","banana","banana");//輸出結果:[apple, banana, banana, banana, banana] } }
ArrayList<String> list = new ArrayList<String>();//泛型的集合(因為集合底層結構是數組,所以基本類型要包裝才能存進集合) list.add("apple"); ArrayList<Integer> list2 = new ArrayList<Integer>(); list2.add(1); // list instanceof ArrayList // list instanceof ArrayList<String> /*instanceof是Java、php的一個二元操作符(運算符),和==、>、<是同一類東西。由於它是由字母組成的,所以也是Java的保留關鍵字。 * 它的作用是判斷其左邊對象是否為其右邊類的實例,返回boolean類型的數據。可以用來判斷繼承中的子類的實例是否為父類的實現。 * 相當於c#中的is操作符。java中的instanceof運算符是用來在運行時指出對象是否是特定類的一個實例。 * instanceof通過返回一個布爾值來指出,這個對象是否是這個特定類或者是它的子類的一個實例。*/ System.out.println(list instanceof ArrayList);//輸出結果:true // System.out.println(list instanceof ArrayList<String>); System.out.println(list instanceof ArrayList<?>);//輸出結果:true
package cn.collection; //如果接口中的方法的參數(形參、返回值)不確定時,可以考慮使用泛型接口。形式 public interface FANString<T> { public void showInfo(T t); } /** * 泛型的命名規則: * 【1】一定要大寫,推薦使用單個字母 * 【2】字母任意,一般情況下下 T--type E---Element */
package cn.collection; //[1]實現類能確定泛型接口的類型 // public class Test00 implements FANString<String>{ @Override public void showInfo(String t) { // TODO Auto-generated method stub } } //實現類不能確定泛型接口的類型->繼續泛。 /* public class Test00<T> implements FANString<T>{ @Override public void showInfo(T t) { } }*/
泛型的上限和下限 (C)
public static void print(ArrayList<? extends Pet> list) { for (Pet pet : list) { pet.showInfo(); } } |
泛型的上限ArrayList(? extends Pet) list 聲明了一個容器,容器中的元素類型一定要繼承於Pet,我們稱這種形式叫做泛型的上限。
泛型的下限ArrayList(? super Pet) list 聲明了一個容器,容器中的元素類型一定要是Pet的父類,我們稱這個形式為泛型的下限。