泛型方法、初始集合和集合的遍歷
一、泛型方法
1.什麼是泛型方法
語法格式:
【修飾符】<泛型的型別> 返回值型別 方法名(形參列表)[throws異常列表]{}
例如:public void method (T t){
}
3,為什麼有泛型方法
(1)對於靜態方法來說那我們之前宣告的<泛型>是不能用於靜態成員,所以如果某個靜態方法需要用到泛型,那麼可以考慮使用泛型方法
(2)類或者介面已經宣告,不是泛型類後新增的方法可以使用泛型
不管是(1)還是(2)都有一個原因,因為在設計這個方法的時候,可能它的形參型別或返回值型別未知。這個時候考慮使用泛型方法
4.注意
(1)泛型方法的<泛型型別形參列表>同樣可以宣告上限
<T extends 上限1 & 上限2 …>
上限中類只能有一個,必須在左邊,介面可以有多個
多個上限之間是&的關係
(2)泛型方法的<泛型型別形參列表>必須寫在返回值型別前面,修飾符的後面
(3)泛型方法的泛型型別不用手動指定,會根據實參的型別自動推斷形參的型別。
二、泛型萬用字元
1.長什麼樣子
(1)<?>: ?代表任意型別
(2)<? extends xx>
(3)<? super xx>
2、作用
(1)宣告一個變數時,該變數的型別如果是一個泛型類或泛型介面,呢麼可以加萬用字元,例如:Student 類
宣告變數student<?> stu;
(2)宣告一個形參時,該形參的型別如果是一個泛型類或泛型介面,那麼可以加萬用字元,
例如:public [static ] void method(Student <?> stu){}
(3)注意;不能用在宣告泛型的位置
public class MyClass <?>{}
public static <?> void method(){}
3、為什麼要有萬用字元?
(1)我們在使用泛型類或泛型介面時,除非泛型被擦除了,否則你指定了什麼型別,就必須是什麼型別。
如果希望是任意型別,又不想泛型擦除,只能用<?>。
public class TestWildCard {
//需求一:宣告一個方法,該方法,可以接受任意一種學生物件,至於方法的功能我們暫時先不管
/*public void method1(Student stu){//寫法一:可以,但是沒有指定泛型,相當於泛型擦除
//...
}*/
public void method2(Student<Object> stu){//寫法二:有問題,只能接收new Student<Object>();
//...
}
public void method3(Student<?> stu){//寫法三:可以,這個?代表的就是任意型別
//...
}
@Test
public void test01(){
method2(new Student<Object>());
// method2(new Student<String>());//錯誤的
// method2(new Student<Integer>());//錯誤的
}
@Test
public void test02(){
method3(new Student<Object>());
method3(new Student<String>());
method3(new Student<Integer>());
}
//需求2:用泛型類Student,宣告一個陣列,該陣列中可以儲存任意學生物件
@Test
public void test03(){
// Student<Object>[] arr = new Student[3];
Student<?>[] arr = new Student[3];
arr[0] = new Student<Object>();
arr[1] = new Student<String>();
arr[2] = new Student<Integer>();
}
}
class Student<T>{
private String name;
private T score;
public Student(String name, T score) {
this.name = name;
this.score = score;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
泛型萬用字元<?>等形式有侷限性:
如果某個泛型類或泛型介面使用時,用到<?>等形式,該型別對應的變數修改值就受到了限制。
三、集合
1.容器
java中容器:陣列(可以裝各種基本資料型別的值和物件)、
集合:可以裝各種物件,不能裝基本資料型別的值
分好多中:列表list,集:set,佇列Queue:,對映:Map…
2.有陣列還要有集合?
(1)陣列:
優點:速度快,可與根據下標定位元素
缺點:長度是固定的,
陣列要求開闢一整塊連續的空間GC回收導致效能降低
(2)集合
優點:型別很多,表示我們的可選性多,可以根據需求選擇合適的集合;集合的底層可以自動擴容
缺點:型別很多,需要了解每一種型別的特點,好選擇
3、集合的分類
(1)collection系列:包括列表List,集Set,佇列Queue等
儲存一組物件,比喻:party
(2)Map系列
儲存鍵值對(對映關係),(key,value),比喻
3.1collection
1.java.util.collection介面
(1)層次結構中的跟介面
(2)表示一組物件
(3)一些collection允許有重複的元素,另一些不允許
一些有序,一些無序。
(4)沒有類能直接實現collection介面,可以實現子介面(list、Set)
(5)此介面通常用來傳遞collection ,並在需要最大普遍性的地方操作這些collection.
即如果我需要List,Set等集合之間轉換的時候,可以使用Collection作為橋樑進行傳遞,說白了就是多型引用
2.collection的常用方法
他下面的子介面的實現類都有該介面的方法
代表了集合的通用操作:增、查、刪除等操作
Collection泛型介面
之前用T,代表Type,通用型別
現在用E代表元素的型別,
(1)新增
一次新增一個元素
boolean add(E e) 一次新增多個元素
boolean addAll(Collection<? extends E> c) :一次新增多個元素
<? extends E>:當前集合的元素是E,c集合的元素型別要求是E或E的子類物件。 ###### (2)刪除元素 void clear() : 清空 boolean remove(Object o):刪除一個元素 用Object的原因,早期沒有泛型,後期有了之後沒改,新增的時候要檢查型別是否符合,刪除時不用。 boolean removeAll(Collection <?> c): this當前集合 = this當前集合 - this當前集合 ∩ c集合 <?>表示任意型別(3)修改:沒有提供修改元素的方法
(4)查詢
int size() 獲取元素的個數
boolean isEmpty() : 判斷是否是空集合
boolean contains(Object o):是否包含某個物件、
boolean containsAll(Collection<?> c):是否包含c集合的所有元素
返回true,c是this集合的子集
5.其他
boolean retainAll(Collection<?> c)保留this集合和c集合的交集,即this集合 = this集合 ∩ c集合、
Object[] toArray() :把集合中的元素放到一個Object陣列中返回
T[] toArray(T[] a):把集合的元素放到a陣列中,但是a陣列的型別沒有檢查是否符合當前集合的元素E型別。用的時候謹慎。
public void test01(){
// Collection coll = new Collection() ;//錯誤,介面不能直接new物件
Collection<String> coll = new ArrayList<>() ;//ArrayList是List子介面的實現類,它也是Collection系列。
//多型引用,coll編譯期間只能看Collection裡面的方法,關注點更集中。
//<String>代表集合的元素型別是String
coll.add("hello");
coll.add("world");
coll.add("java");
System.out.println(coll);//說明ArrayList實現類重寫類toString方法
}
二、集合的便利
1、Collection 系列的集合有一個方法:
Iteratoriterator()
1、Collection系列的集合支援foreach便利
foreach:稱為增強for迴圈,比普通的for迴圈簡潔,功能更強大,可以直接遍歷陣列、Collection系列的集合等等,不需要下標等資訊。
語法格式:
for(元素的型別 元素名 : 陣列/集合的名稱){
}
注意:元素名是自己命名的,只是一個臨時名稱,在當前for迴圈中使用的臨時變數。
問題1:foreach是如何工作的?
foreach其實本質上就是Iterator迭代器。
問題2:陣列為什麼支援呢?
陣列這個型別是編譯器動態生成的型別,它在生成的過程中,也會提供迭代器的實現。
問題3:是否所有的集合,或者容器都支援foreach迭代器呢?
不是。
只有實現了java.lang.Iterable介面。
實現這個Iterable介面允許物件成為 “foreach” 語句的目標。
因為Iterable介面的抽象方法是 Iterator iterator() 。如果要實現這個方法,就要手動實現迭代器,為foreach的執行提供基礎。
問題4:在foreach遍歷集合的過程中,能不能呼叫集合的remove方法呢?
不能
結論:根據條件刪除元素,只有一種方式,用Iterator迭代,並且用 Iterator迭代器的remove方法。
如果精確刪除,就直接呼叫集合remove方法即可。
Collection系列的集合有一個方法:
itertor()
java.util.Iterator迭代介面,有三個方法:
(1)boolean hasNext():判斷是否還有下一個元素
(2)E next(): 取出下一個元素
(3)void remove():刪除剛剛迭代的元素
Collection的remove適合用於精確刪除
迭代器中的無法精確的說明元素時用迭代器的remove方法
public class TestIterator {
@Test
public void test03() {
Collection<String> coll = new ArrayList<>();
coll.add("hello");
coll.add("world");
coll.add("java");
coll.add("java");
Iterator<String> iterator = coll.iterator();
while(iterator.hasNext()) {
String element = iterator.next();
if (element != null && element.startsWith("j")) {
coll.remove(element);
//報ConcurrentModificationException:併發修改異常。 兩條線路在同時訪問與修改集合,一個是iterator,一個是coll
//你的coll的remove方法刪除了元素之後,沒有把最新的資訊告知iterator
}
}
System.out.println(coll);
}
@Test
public void test02(){
Collection<String> coll = new ArrayList<>();
coll.add("hello");
coll.add("world");
coll.add("java");
//需求:刪除以w開頭的字串
/*
思考:用Collection介面的remove方法能否完成?
*/
// coll.remove();//無法完成,即Collection的remove適用於精確刪除
//使用Iterator迭代器刪除,標準寫法
Iterator<String> iterator = coll.iterator();
while(iterator.hasNext()){
String element = iterator.next();
if(element!=null && element.startsWith("w")){
iterator.remove();
}
}
}
@Test
public void test01(){
Collection<String> coll = new ArrayList<>();
coll.add("hello");
coll.add("world");
coll.add("java");
Iterator<String> iterator = coll.iterator();
/* System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());//NoSuchElementException:沒有這個元素了*/
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
四、Set介面
1.java.util.Set
一個不包含重複元素的collection,對數學中的集的抽象
2.Set時Collection子介面
,但是Set介面沒有增加自己的方法和Collection的方法一樣
3.Set介面有一些實現類:
(1)HashSet:
不能重複,無序(和新增順序不一致)
根據hashCode和equals方法。
hashCode值,影響他的儲存位置
(2)TreeSet:
元素也不能重複。
元素有大小順序的,說明元素要支援比較大小,要求元素必須實現java.lang.Comparable介面。
如果元素沒有實現Comparable介面,或者實現這個介面的比較規則不適合當前的需求,就要藉助java.util.Comparator。
大小順序和不能重複依據的是什麼?
大小順序和Comparable的compareTo或Comparator的compare方法有關;
不可重複也是和Comparable的compareTo或Comparator的compare方法有關。
Comparable的compareTo或Comparator的compare方法返回0,就認為是“重複”元素。
TreeSet底層用紅黑樹(是一種自平衡的二叉樹)實現。
3、LinkedHashSet
元素也不能重複。
元素有順序,遍歷結果是按照新增的順序遍歷的。
底層實現:雜湊表 + 連結串列。 連結串列的作用是記錄新增的順序。
什麼時候用LinkedHashSet?
當我們需要元素不可重複,但是又要記錄新增順序時用它。
LinkedHashSet比HashSet效率低,因為它在新增和刪除等過程中,要考慮順序的問題。
五、List集合
1、java.util.List介面:結合列表,也稱為序列。
有序的collection(也稱為序列)
此介面的使用者可以佇列表中每個元素的插入位置進行精確地控制。使用者根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。 == >List的很多操作和【index】有關與set不用,列表通常允許重複的元素。
List:有序可重複。
2、List的方法,在Collection的基礎上增加了很多方法都和index有關
(1)新增
void add(int index, E element):在[index]位置新增一個元素
boolean addAll(int index,Collection<? extends E> c)在[index]位置新增多個元素
(2)刪除
E remove(int index)
(3)修改
E set(int index , E element);替換[index]位置的元素
(4)查詢
E get(int index); 獲取[index]位置的元素
int indexOf(Object o ):查詢o物件在當前List中的下標,如果沒有返回-1,如果多個返回第一個
int lastIndexOf(Object o):查詢o物件在當前List中的小標,如果沒有返回-1,如果有多個,返回最後一個
List subList(int fromIndex, int toIndex):擷取一段列表
(5)遍歷
ListIterator listIerator()
ListItertor listIterator(int index)
ListIterator是Itertor的子介面比Itertor多了幾個功能:
倒序遍歷、獲取下表資訊、遍歷過程中新增和替換元素
public class TestList {
@Test
public void test13() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
ListIterator<String> iter = list.listIterator();
while(iter.hasNext()){
String element = iter.next();
if(element.startsWith("w")){
iter.set("atguigu");
}
}
System.out.println(list);//[hello, java, atguigu, xxx]
}
@Test
public void test12() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
ListIterator<String> iter = list.listIterator();
while(iter.hasNext()){
String element = iter.next();
if("world".equals(element)){
iter.add("atguigu");//遍歷過程中插入元素
}
}
System.out.println(list);//[hello, java, world, atguigu, xxx]
}
@Test
public void test11() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
//倒序遍歷
ListIterator<String> iter = list.listIterator(list.size());//遊標預設在列表的結尾
while(iter.hasPrevious()){
System.out.println(iter.previousIndex() + ":" + iter.previous());
}
}
@Test
public void test10() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
ListIterator<String> iter = list.listIterator();//遊標預設在列表的開頭
while(iter.hasNext()){
//遍歷過程中獲取元素的下標
System.out.println(iter.nextIndex() + ":" + iter.next());
}
}
@Test
public void test09() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
ListIterator<String> iter = list.listIterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
}
@Test
public void test08() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("world");
list.add("xxx");
List<String> subList = list.subList(1, 3);
System.out.println(subList);//[java, world]
}
@Test
public void test07() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.set(0,"atguigu");
System.out.println(list);//[atguigu, java]
}
@Test
public void test06() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//刪除元素值是1
// list.remove((Object)1);
// Collection<Integer> coll = list;
// coll.remove(1);
list.remove(Integer.valueOf(1));
System.out.println(list);//[2, 3, 4, 5]
}
@Test
public void test05() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.remove(1);//刪除[1]位置的元素
System.out.println(list);//[1, 3, 4, 5]
}
@Test
public void test04() {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.remove(1);
System.out.println(list);//[10, 30, 40, 50]
}
@Test
public void test03() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.remove(0);
System.out.println(list);
}
@Test
public void test02() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
List<String> list2 = new ArrayList<>();
list2.add("chai");
list2.add("xxx");
list.addAll(1, list2);
System.out.println(list);//[hello, chai, xxx, java]
}
@Test
public void test01(){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add(0,"atguigu");//[atguigu, hello, java]
list.add(5,"chai");//IndexOutOfBoundsException: Index: 5, Size: 3
//我們之前見過的ArrayIndexOutOfBoundsException是IndexOutOfBoundsException的子類
System.out.println(list);
}
}
3、List介面的實現類們
(1)ArrayList:動態陣列
底層實現:陣列
(2)Vector:動態陣列
底層實現:陣列
(3)LinkdList:雙向連結串列
(4)Stack: 棧
底層實現:陣列,因為他是Vector的子類
問題1:ArrayList與Vector的區別?
Vector是最古老的動態陣列,比Collection和List介面還要早。執行緒安全。
Vector在擴容時,預設擴容為2倍。(擴容的頻率第,浪費空間的可能性增加)
Vector的預設初始化容量是10。
ArrayList是比Vector新一點。執行緒不安全。
ArrayList在擴容時,預設擴容為1.5倍。(擴容的頻率高,空間利用率高)
ArrayList的預設初始化容量,在JDK1.7之前也是10,在JDK1.7之後,一開始是0,第一次新增元素時初始化為10.
無論是Vector還是ArrayList使用時,如果對元素的個數有預估,最好使用:
ArrayList(int initialCapacity):一開始就指定初始化容量,就避免擴容。
Vector(int initialCapacity) :一開始就指定初始化容量,就避免擴容。
Vector(int initialCapacity, int capacityIncrement):除了指定初始化容量,還可以指定每次增加幾個元素。
問題2:ArrayList從JDK1.7之後為什麼預設初始化容量是0?
我們很多方法的返回值型別是List或ArrayList,特別是DAO(資料庫層)方法。
這些方法在實現時,如果沒有結果返回,一般不返回null。因為返回null,呼叫者就必須加if(xx != null)避免空指標,太麻煩。
不返回null,但是又沒有資料,就返回了一個空的ArrayList物件。
那麼如果初始化容量是10的話,其實這10個空間是浪費的。所以,就初始化容量為0。
問題3:動態陣列與雙向連結串列有什麼區別?
區別一:
動態陣列的元素:資料本身
雙向連結串列的元素:結點
結點包含:
①前一個結點的首地址:prev
②資料本身
③後一個結點的首地址:next
區別二:
動態陣列的元素是連續儲存,提前開闢一整塊連續的記憶體空間。
雙向連結串列的元素不要求連續儲存,來一個申請一個連線一個。
連續的好處:查詢快,可以根據陣列的首地址,快速的定位到某個元素。
不連續的缺點:只能從頭或尾挨個查詢,定位到對應的位置。
連續的壞處:插入,刪除時,需要移動元素,插入時可能考慮擴容。
不連續的好處:不用提前申請空間,插入和刪除只需要修改前後元素的prev和next
問題4:Stack
它是Vector的子類。
在Vector的基礎上增加了幾個方法:
①E peek():檢視棧頂元素
②E pop():返回棧頂元素,棧頂換元素
③ E push(E item):把元素壓入棧稱為新的棧頂元素
…
棧的結構特點:後進先出(LIFO)或先進後出(FILO)
LIFO:Last In First Out
FILO:First In Last Out
像桶,箱子
| | 棧頂
| |
| |
|______ | 棧底
現在不太建議我們用Stack,如果要用棧結構,建議用LinkedList。因為LinkedList也提供了這些方法,使我們實現棧的效果。
問題5:LinkedList說起來是雙向連結串列,但是它又是Queue,又是Deque,還可以用作棧等。
名稱 deque 是“double ended queue(雙端佇列)”的縮寫,通常讀為“deck”。
佇列結構特點:先進先出(FIFO)
相關文章
- Java遍歷Map集合的方法Java
- Collection集合的遍歷
- 集合迭代/遍歷
- 07.集合和泛型泛型
- Map集合&&Map集合的不同遍歷【keySet()&&entrySet()】
- 如何遍歷HashMap集合?HashMap
- MVC遍歷map集合MVC
- C#泛型集合C#泛型
- Java:Collection集合、泛型Java泛型
- Kotlin---集合與遍歷Kotlin
- Map集合的四種遍歷方式
- jsp下實現遍歷集合JS
- C#中的介面和泛型集合探討C#泛型
- 關於Map集合的遍歷總結
- 按指定格式遍歷集合字串字串
- Java8集合遍歷Java
- Day22 集合,ArrayList,泛型泛型
- Java 集合列舉泛型(一)Java泛型
- 使用jstl無法遍歷bean中的集合JSBean
- Java 中的泛型 集合(List,Set) MapJava泛型
- C# 泛型集合的自定義型別排序C#泛型型別排序
- js技巧用Map集合代替Array遍歷JS
- Java之HashMap集合簡介及遍歷JavaHashMap
- 集合框架(List儲存字串並遍歷)框架字串
- C#泛型鍵值對集合C#泛型
- Java反射給泛型集合賦值Java反射泛型賦值
- Lambda 表示式遍歷集合時用remove方法刪除list集合中滿足條件的元素問題REM
- C#類繼承自泛型集合C#繼承泛型
- Java入門教程十二(集合與泛型)Java泛型
- 8.集合、泛型、列舉、註解、反射泛型反射
- 泛型類和泛型方法泛型
- java8 對list集合中的物件遍歷,重新賦值兩種方法,遍歷某個屬性返回陣列Java物件賦值陣列
- Guava集合--新集合型別Guava型別
- jsp頁面中同時遍歷多個list集合JS
- 關於集合遍歷並刪除報錯詳解
- Java集合 Collection、Set、Map、泛型 簡要筆記Java泛型筆記
- Java進階學習之集合與泛型(1)Java泛型
- Java中如何使用泛型實現介面中的列表集合?Java泛型