java集合结构----集合框架以及背后的数据结构
2.選擇排序和冒泡排序的原理和區別:
1.Collection常見的方法實例
1)咱們的JAVA集合框架是定義在java.util包底下的一組接口和實現類
2)實現Iterable接口的類可以通過for each來進行遍歷我們的集合
3)實現list接口都屬于線性結構,比如說順序表,鏈表,隊列,戰
4)Queue:隊列,它本身也是一個線性結構,有一種隊列不是線性的,比如說優先級隊列(沒實現Dqueue接口)
(隊尾進,隊頭出)
5)DeQueue:雙端隊列(兩端都可以進出)
6)實現Set接口的類是不能重復的
7)sortSet實現了set接口,不僅僅可以放重復的元素,還可以在存放元素的時候進行排序咱們的treeset就繼承了SortedSet這個類,但是HashSet就沒有實現SortedSet這個類
8)LinkedList本身可以做雙向鏈表,可以做普通的隊列,還可以做雙端隊列,他實現了Queue和list接口
9)PriorityQueue實現了Queue接口
10)實現Map接口的類是不可以通過foreach來進行遍歷,TreeMap和TreeSet底層都是紅黑樹,TreeMap擁有排序的功能,HashMap沒有排序的功能
Collection的常用接口:
1)boolean add(E e)添加元素e放到集合里面
2)void clear()清除集合里面的所有元素,先通過循環的方式把每一個元素都置為空,再將我們的size變成0
3)boolean IsEmpty()判斷集合里面是否沒有任何元素
4)boolean remove(E e)如果元素e出現在集合里面,那么就刪除一個
5)int size()返回集合中的元素個數
沒有get方法,沒有獲取元素的方法
,指定集合類中放哪些元素的時候,一定要指定類型,<>里面不要用簡單類型
Collection<Integer> collection=new ArrayList<>();//1添加某些元素collection.add(1);collection.add(2);collection.add(3);//2刪除某個元素collection.remove(1);//注意這里面的clear是針對集合類里面的元素全部進行清空//3打印集合類里面的元素System.out.println(collection);//打印結果是2,3//4把集合類中的所有元素放到數組中Object[] arr1= collection.toArray();//5判斷是否為空System.out.println(collection.isEmpty());//6獲取到集合類里面的長度System.out.println(collection.size()); Map<String,String> map=new HashMap<>();map.put("及時雨","宋江");map.put("國民女神","高圓圓");Set<Map.Entry<String,String>> set=map.entrySet();//1)當我們調用這個方法之后,會把哈希表中的每一對key和value包裝成一個整體,相當于把兩個元素放到一個大盒子里面,變成一個元素//2)然后內存就會把每一個大盒子放到Set里面,這個大盒子就是Map.Entry(String,String)類型for(Map.Entry<String,String> sb:set){System.out.println(sb.getKey());System.out.println(sb.getValue());}Map的常用方法:
1)V? get(object k)根據K查找對應的value
2)V? getOrDefault(Object K,V defaultValue)根據K查找對應的V,查不到就用我們制定的默認值來進行代替
3) V put(K k,V v)存放鍵值對
4)boolean containsKey(Object K),boolean containsValue(Object V)判斷是否存在Key和Value
2.簡單類型和包裝類(針對的是基本數據類型)
1)將簡單的數據也是可以面向對象的,讓整數與字符串之間的轉換更加方便,只需要調用一個方法即可,就不需要自己寫一個方法來進行實現,幫我們對數據進行處理,String不是一個包裝類
byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean我們在對數據類型進行轉換的時候
//1將字符串轉化成整數String str="123";int a=Integer.valueOf(str);int b=Integer.parseInt(str);//2將整數轉化成字符串String str1=Integer.toString(a);int與Integer的區別
1)類型不同:Integer是對象類型,int是基本數據類型,Integer是int的包裝類
2)Integer的默認值是null,int的默認值是0,當new Integer()的時候實際上是生成一個指針指向對象,但是int直接儲存數值;
3)Integer變量需要實例化之后才能使用,int則不需要。
4)Integer變量和int變量進行比較時,java會自動將Integer對象拆包裝為int,然后進行比較,實際上就變為兩個int變量比較。
5)Integer實際是對象的引用,指向此new的Integer對象;int是直接存儲數據值
6)包裝類型可以用于泛型,但是簡單類型是不可以的
2)裝箱和拆箱
裝箱/裝包:將一個簡單的數據類型轉化成包裝類型Integer.valueOf();
拆箱/拆包:將一個包裝類型轉化成簡單的數據類型intValue();
//1.自動裝箱裝包,隱式Integer a=10;//看似是將一個簡單類型轉化成包裝類型,本質上就是調用了Integer.valueOf方法//2.自動進行拆箱,拆包,隱式int b=a;//在這里面默認調用了Integer的intValue方法,自動進行拆包 //1.下面都是裝包,是顯式的進行裝包Integer t1=Integer.valueOf(123);Integer t2=new Integer(123);Integer t1=(Integer)123;//2.下面是拆包,是顯式的進行拆包int a=t1.intValue();double b=t1.doubleValue();float c=t1.floatValue();int f=(int)t1; 下面是隱式的進行裝包和拆包1)Integer a=123;//123本來是一個簡單數據類型,但是最終變成了包裝類,這個過程就是裝箱 //上面的過程中底層默認調用了Integer.valueOf()方法2)int b=a;//a本來是一個包裝類,就將一個包裝類型轉化成簡單的數據類型,拆箱 //他的底層默認調用了intValue()方法 _________________________________________________________________________________________Integer a=10;System.out.println(a.value); 這會訪問失敗,因為value字段是默認是Integer包裝類中的用private修飾的字段 所以說簡單數據類型在包裝類中就是一個Value屬性 下面是顯示進行裝包和拆包//顯示進行裝包Integer integer=new Integer(123);Integer s1=Integer.valueOf(123);//顯示進行拆包int a=s1.intValue();double b=s1.doubleValue(); Integer a=123;Integer b=123;System.out.println(a==b);//trueInteger c=129;Integer d=129;System.out.println(c==d);//false上面都涉及了自動裝包 public static Integer valueOf(int i) {? ? ? if(i>IntegerCache.low&&i<=IntegerCache.high)(low=-128,high=127){ return Integer.Cache[i+(-IntegerCache.low)](注意,這里面的Cache是一個數組)}return new Integer(i);//new 對象 }1)當我們傳入的數據是在-128-127之間,會返回一個catche數組的值
當我們傳入127的時候,返回的是Cache[255],此時128是最大的數據,此時得到的數組下標就是255;當我們傳入-128的時候,返回的是Cache[0]
注意這個數組里面的范圍下標是0-255;
Cache[0]=-128; Cache[1]=-127;.........Cache[255]=127
總結:當我們傳入的數據在-128-127內,會先進行計算下標,再會返回數組里面所存放的值;返回的是數值;
掏出了這個數據范圍之后,就會在堆上new 一個新的對象,返回的是一個對象,此時我們要進行比較的是引用;
Integer的比較方式:
1)==只能用于非Integer的值,在-127~128直接按照值的方式來進行比較,超過這個范圍就不適用了,在valueOf方法里面,Integer的取值在-128~127之間,他會進行復用原來有的對象,否則就會直接在堆上面new Integer()對象出來
2)使用equals方法,在Integer中重寫了equals方法:通過拆箱來進行比較
public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}return false;}3)Integer實現了Compareable接口,并且重寫了里面的compareTo方法,取出Integer對象的value屬性來進行比較:前一個數-后一個數
public int compareTo(Integer anotherInteger) {return compare(this.value, anotherInteger.value); } public static int compare(int x, int y) {return (x < y) ? -1 : ((x == y) ? 0 : 1); }4)直接進行運算:我們可以直接將兩個值進行相減,來進行判斷,如果說相減的值是0,那么說明他們相等
5)我們可以進行調用里面的intValue方法來進行比較:
Integer a=new Integer(100);Integer b=new Integer(100);System.out.println(a.intValue()==b.intValue());6)通過異或的方式來進行比較:不同為1,相同為0
3.泛型
問題:下面是一個簡單的順序表,我們在這里面實現的一個順序表,是存放的數據類型只有int類型,這就會很不通用,如果我們想什么樣的類型的數據都想要放進去,就要把這個數組的類型設置成Object類型
能不能啥樣的類型都可以存放呢?
class MyArraylist{private int[] arr1;private int usedsize;public MyArraylist(){this.arr1=new int[10];}public void add(int pos,int val){this.arr1[pos]=val;}public int get(int pos){return this.arr1[pos];}}改成下面的代碼之后,還是發現有問題:
1)這個代碼太通用了,什么樣類型的數據都可以進行存放不能指定元素,下面的代碼里面的元素既可以存放int,又可以存放String,完全就是一個大雜燴,能不能只讓他存放整型或者是
字符串類型呢?
2)取出我們的Object順序表中的元素,因為咱們返回的是Object類型,還需要進行強制類型轉換,這是很麻煩的;
我們現在想要做到下面幾件事:
1)能不能指定我這個順序表的類型,只能存放一種數據類型?
2)指定類型之后,是不是就只能存放指定類型的數據呢?
3)取出數據之后,可不可以不進行數據類型轉換?
static class MyArraylist{private Object[] arr1;private int usedsize;public MyArraylist(){this.arr1=new Object[10];}public void add(Object val){this.arr1[usedsize]=val;usedsize++;}public Object get(int pos){return this.arr1[pos];}}public static void main(String[] args) {MyArraylist list=new MyArraylist();list.add(1);list.add("hello");String ret=(String)list.get(1);}在指定類的后面寫上<T>,它是代表當前的類是一個泛型類,此時的這個T就只是一個占位符而已,把類型參數化了,直接就是一個T類型的數組了,不能泛型化數組
private T[]=new T[10];--------private T[]=(T[])new Object[];創建數組最好用反射
泛型的意義:
1)存放數據的時候,在編譯的時候會自動地對要放進去的類型進行檢查,當取數據的時候,編譯器會自動地對取出來的數據進行強制類型轉換
2)泛型中<>里面的內容是不進行參與類型的組成的
3)泛型類型的參數,只能是包裝類,而不能是簡單類型
咱們內部的ArrayList使用泛型的時候也是new Object數組,但是他的get方法,把他的單個元素強轉成(E)(array[index]),不是整體數組強轉,而是單個類型強轉成E類型
泛型是怎么進行編譯的?范型只有上界,沒有下界
toarray()將一個list數組轉化成數組
javap -c? 類名
反省是編譯時期的一種機制
泛型這個概念只在編譯時期起作用,在運行時期是沒有泛型這個概念的;在編譯時會被擦除為Object類型,編譯器生成的字節碼文件并不包含泛型的類型信息
一:public class MyArray<E extends Number>{ } 這表示E可以是Number或者是Number的子類 1)MyArray<Integer> l1;//這是正常的,因為Integer是Number的子類 2)MyArray<String> l2;//這是錯誤的,因為String并不是Integer的子類 二:沒有指定邊界,默認就是Object,比如說class MyArray<T>{}下面我們來寫一個方法,來求數組中元素的最大值
class ALG<T>{public T GetMax(T[] array){T max=array[0];for(int i=1;i<array.length;i++){//這是引用類型的比較,max是T類型,array[i]也是引用類型,引用類型是不可以通過>=<來進行比較的if(array[i]>max){max=array[i];}}return max;}}既然是引用類型,我們就要重寫Compareable接口,重寫compareTo方法來比較兩個引用類型,但是我們發現上述這個代碼,compareTo方法點不出來,因為程序不知道你這個類是否繼承了Compareable接口?重寫了CompareTo方法
class ALG<T extends Comparable<T>> 這種寫法就表示此時這個T一定要實現Compareable接口,這是泛型的上界 package Demo; class ALG<T extends Comparable<T>>{public T GetMax(T[] array){T max=array[0];for(int i=1;i<array.length;i++){//這是引用類型的比較,max是T類型,array[i]也是引用類型,引用類型是不可以通過>=<來進行比較的if(array[i].compareTo(max)>0){max=array[i];}}return max;} } public class TestData {public static void main(String[] args) {ALG<String> avg=new ALG<>();String[] strings={"abcd","abc","ab","a"};int[] array={1,3,2,45,67,87};String str= avg.GetMax(strings);System.out.println(str);} }1)這時候會出現一個問題,咱們每一次調用findmax都要newALG()這樣的對象嗎,才可以通過這個對象的實例來進行調用findmax這樣的方法,就顯得太麻煩了
2)這個時候我們加上static關鍵字,不就可以保證通過類名來進行調用了嗎?如果加上了static關鍵字之后,這個靜態方法是不依賴于對象的,咱們的這個T參數是在new ALG中的尖括號進行傳參的
class ALG<T extends Comparable<T>>{public static<T extends Comparable<T>> T GetMax(T[] array){T max=array[0];for(int i=1;i<array.length;i++){//這是引用類型的比較,max是T類型,array[i]也是引用類型,引用類型是不可以通過>=<來進行比較的if(array[i].compareTo(max)>0){max=array[i];}}return max;}} 這個時候就不需要進行new對象了,這就是咱們的一個靜態的泛型方法 package Demo; class ALG{public static<T extends Comparable<T>> T GetMax(T[] array){T max=array[0];for(int i=1;i<array.length;i++){//這是引用類型的比較,max是T類型,array[i]也是引用類型,引用類型是不可以通過>=<來進行比較的if(array[i].compareTo(max)>0){max=array[i];}}return max;}} public class TestData {public static void main(String[] args) {Integer[] array={12,34,34,45,67};int max=ALG.<Integer>GetMax(array);}}理論上來說ArrayList<Integer> 不是ArrayList<Number>的父親類型
ArrayList<Number>也不是ArrayList<Integer>的子類
通配符:
通配符是無法解決泛型無法諧變的問題的,諧變指的是Student如果是Person的子類,那么List<Student>也應該是List<Person>的子類,但是泛型是不支持這樣的父子類關系的
1)泛型T是確定的類型,一旦你要是傳了,我就定下來了,但是通配符可以說是更為靈活或者不穩定,更多地用于擴充參數的范圍
2)或者我們可以這么理解:泛型T就像是一個變量,等待著你可以傳輸一個具體的類型,而通配符是一種規定,規定你可以傳哪些參數
class AVL{public static<T> void print1(ArrayList<T> list){for(T x:list){//編譯器一定知道當前傳遞過來的是一個T類型的數據System.out.println(x);}}public static void print2(ArrayList<?> list){for(Object x:list)//編譯器不知道?是啥類型,具體的類型我不知道{System.out.println(x);}} }通配符的上界:
<? extends 上界> <? extends Number>//可傳入的參數類型是Number或者是Number的子類 舉例: public static void printAll(ArrayList<? extends Number > list>{} //上面表示可以傳入的類型是Number的子類的任意類型的ArrayList 下面都是正確的: printAll(new ArrayList<Integer>()); printAll(new ArrayList<Double>()); printAll(new ArrayList<Number>()); 下面搜是錯誤的 printAll(new ArrayList<String>()); printAll(new ArrayList<Object>());假設現在有下面的關系:
Animal
Cat extends Animal
Dog extends Animal
根據上面的關系,寫一個代碼,打印一個存儲了Animal或者Animal子類的list
代碼1:
public static void print(ArrayList<Animal> list>{}這樣是不可以進行解決問題的,因為print的參數是List<Animal>,我們就不可以進行接收List<Cat> list
代碼2:
public static<T extends Animal> void print3(List<T> list){for(T animal:list){System.out.println(animal);}}這時候T類型是Animal的子類或者是自己,該方法也是可以實現的,這里面的類型是一個確定的類型,編譯器知道是什么類型
代碼三:通配符來進行實現:
public static void print(List<? extends Animal> list> {for(Animal x:list){System.out.println(x);//編譯器此時不知道這是調用誰的ToString方法 發生了向上轉型,不知道這個類型具體是啥類型,反正指定了上界,編譯器就認為你傳遞過來的類一定是Animal或者是Animal的子類,如果沒有這個通配符上界Animal這里面就應該寫成Object了} }1)總結:ArrayList<? extends Number>是ArrayList<Integer>或者ArrayList<Double>的父類類型
2)ArrayList<?>是ArrayList<? extends Number>的父親類型
通配符的上介是不適合用于寫入對象的:
ArrayList<Integer> list1=new ArrayList<>();list1.add(1);list1.add(2);ArrayList<Double> list2=new ArrayList<>();List<?extends Number> list=list1;//list.add(1,9);//這里面是不適合進行寫入數據的,適合于讀數據,如果你進行存放的話,即可以進行存放整數,也可以存放浮點數這是不可以的,因為最最終引用的只有一種類型//list.add(2,10.9);Number number= list.get(1);//正確//Integer s1=list.get(0);這樣的寫法是錯誤的,因為不知道里面具體存放的是哪一種類型,萬一存放的是Double類型呢我們的通配符的上介適合讀取數據,不適合寫入數據,上面的list可以進行引用的對象有很多,編譯器是無法確定你的具體的類型的,所以說編譯器為了安全起見,此時只允許你進行讀取
通配符的下界:
<? super 下界> <? super Integer>這是代表可以進行傳入的實參的類型是Integer或者是Integer的父類類型假設有下面代碼:
public static void printAll(ArrayList<? super Integer> list){ } //下面表示傳入的實參都是Integer的父類的任意類型的ArrayList 下面的調用都是正確的: printAll(new ArrayList<Integer>()); printAll(new ArrayList<Number>()); printAll(new ArrayList<Object>()); 下面的調用是編譯錯誤的: printAll(new ArrayList<String>); printAll(new ArrayList<Double>);咱們的ArrayList<? super Integer> 是ArrayList<Integer>的父類類型
ArrayList<?>是ArrayList<? super Integer>的父類類型
ArrayList<? super Person> list=new ArrayList<>(); //ArrayList<? super Person> list2=new ArrayList<Student>();這里面會出現報錯,因為list2只能引用Person或者Person父類類型的list list.add(new Person());//添加元素的時候,只要添加的元素是Person或者是Person的子類就可以了 list.add(new Student()); Person person=list.get(0)//父類引用引用子類對象,應該是對的呀???? Student s=list.get(0)//錯誤,因為Person的子類有很多,不一定就是Student Object s=list.get(1);//正確我們在進行添加元素的時候,我們知道list引用的對象肯定是Person或者是Person的父類的集合,此時我們可以確定此時能夠儲存的最小粒度比Person小就可以,你放的時候,放的都是Person或者Person的子類,但是你讀取的時候,你能確定你讀取的時候讀取的是那一個子類嗎?
關于異常的復習:
1、運行時異常
(1)運行時異常都是RuntimeException類及其子類異常,如NullPointerException、IndexOutOfBoundsException等,這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應該從邏輯角度盡可能避免這類異常的發生。
當出現RuntimeException的時候,我們可以不處理。當出現這樣的異常時,總是由虛擬機接管。比如:我們從來沒有人去處理過NullPointerException異常,它就是運行時異常,并且這種異常還是最常見的異常之一。
出現運行時異常后,如果沒有捕獲處理這個異常(即沒有catch),系統會把異常一直往上層拋,一直到最上層,如果是多線程就由Thread.run()拋出,如果是單線程就被main()拋出。拋出之后,如果是線程,這個線程也就退出了。如果是主程序拋出的異常,那么這整個程序也就退出了。運行時異常是Exception的子類,也有一般異常的特點,是可以被catch塊處理的。只不過往往我們不對他處理罷了。也就是說,你如果不對運行時異常進行處理,那么出現運行時異常之后,要么是線程中止,要么是主程序終止。
如果不想終止,則必須捕獲所有的運行時異常,決不讓這個處理線程退出。隊列里面出現異常數據了,正常的處理應該是把異常數據舍棄,然后記錄日志。不應該由于異常數據而影響下面對正常數據的處理。
2、非運行時異常
(2)非運行時異常是RuntimeException以外的異常,類型上都屬于Exception類及其子類。如IOException、SQLException等以及用戶自定義的Exception異常。對于這種異常,JAVA編譯器強制要求我們必需對出現的這些異常進行catch并處理,否則程序就不能編譯通過。所以,面對這種異常不管我們是否愿意,只能自己去寫一大堆catch塊去處理可能的異常,也可以用throws聲明異常
RuntimeException體系包括錯誤的類型轉換,數組越界訪問以及嘗試訪問空指針,必須被try catch語句塊捕獲,這是錯誤的,可以直接由JVM處理
1)Object類型是所有class類型的父親,是根,數組當然也是一種class類型,因此Object[]和Test[]類型都是Object類型的子類型,對于Test[]類型和Object[]類型,二者同為數組類型,他們之間沒有什么父子關系,而是平級的關系,所以數組之間是不可以進行強制類型轉換的,String與String[]的父類都是Object類型
2)如果強制進行數組類型轉換就會拋出ClassCastException異常
String[] s=new String[10];Object e=new String[10];Object t=new String("abc"); import java.io.FileNotFoundException; import java.io.IOException; public class HelloWorld {public static void TestDemo(){System.out.println("執行這個方法");}public static void main(String[] args) {((HelloWorld)null).TestDemo();}public static void run(){try{//這里面做了一些事情} catch(FileNotFoundException e){System.out.println("發生文件找不到異常");}catch (IOException ex){System.out.println("發生了IOException");}catch (java.lang.Exception e){System.out.println("發生了異常");}}//這里面無論語句發生了什么異常,最終只能有一個catch語句快執行,他們是構成父子類關系的,只能拋出一個異常,只能捕獲一個異常 }重寫equals方法的時候為什么一定要重寫hashcode?
1)equals方法和hashcode方法是Object中的兩個基礎方法,他們共同來協作判斷兩個對象是否相等,這樣做的好處就是,效率更高,如果不重寫,就會出現BUG
2)因為通過hashcode的值,我們就可以直接定位到一個數據的存儲位置,而不需要一個一個的循環查找
3)hashcode也叫作散列碼,他是由對象推導出的一個整數值,并且這個值包括任意整數,包括正數和負數,況且散列碼是沒有規律的,如果說x,y是兩個不同的對象,那么x.hashcode()和y.hashcode()基本上不會相同(也有可能是相同的),Object中的hashcode是只是根據地址只生成的,對比兩個對象的引用地址
String str=new String("abc");String str2=str;//重寫了hashcode方法String str3=str2;System.out.println(str.hashCode());System.out.println(str2.hashCode());System.out.println(str3.hashCode()); 他們的地址值都是相同的 package Demo; class Task{public String name;public int age;public Task(String name,int age){this.age=age;this.name=name;} } public class DemoKail{public static void main(String[] args) {Task task1=new Task("李佳偉",19);Task task2=task1;System.out.println(task1.hashCode());System.out.println(task2.hashCode());Task task3=new Task("李佳偉",19);System.out.println(task3.hashCode());} } 打印結果:644117698 644117698 1872034366但是重寫hashcode之后,這三個值就相同了
總結:
1)當我們使用HashMap,HashSet集合的時候,會進行計算對象在散列表中的位置
2)如果我們只重寫hashcode方法,不重寫equals方法,兩個相同的對象就會存儲到相同的位置,但是此時equals方法沒有重寫,兩個對象就被判定成不相等
3)如果我們只重寫equals方法,而沒有重寫hashcode方法,像hashMap等集合類的判斷邏輯是先進行判斷hash之是否相等,在進行判斷對象值是否相等,如果沒有重寫hashcode,兩個相等的對象可能就被放在散列表不同的位置,根本就沒有equals判斷的機會
4)如果是我們自己自定義的類,也不用這些集合類,就不會調用hashcode方法,只需要重寫equals方法即可,此時也沒有必要重寫equals方法了
如果在HashSet中存儲重寫的自定義對象的時候,就會無法實現去重:
package Demo;import java.util.HashSet; import java.util.Iterator; import java.util.Objects;class Task{public String name;public int age;public Task(String name,int age){this.age=age;this.name=name;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Task task = (Task) o;return age == task.age && Objects.equals(name, task.name);}// @Override // public int hashCode() { // return Objects.hash(name, age); // }@Overridepublic String toString() {return "Task{" +"name='" + name + '\'' +", age=" + age +'}';} } public class DemoKail{public static void main(String[] args) {Task task1=new Task("李佳偉",18);Task task2=new Task("李佳偉",18);Task task3=new Task("李佳偉",18);HashSet<Task> set=new HashSet<>();set.add(task1);set.add(task2);set.add(task3);Iterator<Task> iterable=set.iterator();while(iterable.hasNext()){Task task=iterable.next();System.out.println(task);}} }總結
以上是生活随笔為你收集整理的java集合结构----集合框架以及背后的数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国内人才申领《上海市居住证》审核试行办法
- 下一篇: PHP高级编程(PDG)