<span style="font-family: Arial, Helvetica, sans-serif;">JCF = Java Collections Framework =>Java集合框架</span>
什麼是集合: 存放數據的容器
爲什麼使用集合:
一個方法只有一個返回值,如何將一組數據返回? 使用集合操作
JCF 從1.2 開始 把集合成體系的提供給我們
1.0 時 Vector HashTable
JCF 兩大分支:
Collection
Map
[存放單值類型對象]
[存放鍵值對對象]
List Set
Queue Deque
[有序,不唯一]
[無序,"唯一"] [隊列 5.0]
[1.6]
SortedSet 對Set接口進行了有序,"唯一"的排序
1st. List接口的實現類 ArrayList
import java.util.ArrayList;
/*首先理解包裝類(Wrapper Class)的概念
*
* Java當中的數據類型有兩種: 基本數據類型,引用類型
* Java當中的集合只允許存放引用類型的對象[換言之 基本數據類型是無法被添加進集合的 Java當中的集合存放的是引用類型的地址值,而非實際對象]
*
*
* */
public class TestArrayList0 {
public static void main(String[] args){
//java 集合當中只允許存放引用類型對象
ArrayList<Integer> list = new ArrayList<Integer>();
//JDK 5.0之前 下面這三句添加值的語句會變異報錯的,因爲JDK5.0之前 Object類型的參數絕對不能匹配 int, boolean 等基本數據類型
list.add(1);//自動打包 list.add(new Integer(1));
list.add(2);
//從JDk5.0開始基本數據類型和它所對應的包裝類類型可以直接賦值 自動打包
int num1 = 2;
Integer num2 = num1;//Integer num2 = new Integer(num1);
//從5.0開始 包裝類類型和它對應的基本數據類型也可以直接賦值 自動解包
Integer num3 = 7;
int num4 = num3;//int num4 = num3.intValue();
//SCJP
Boolean data1 = null;
boolean data2 = data1;// boolean data2 = data1.booleanValue();//data1 == null
System.out.println(data2);//? java.lang.NullPointerException
}
}
/*class Integer{//引用類型
private int value;//基本數據類型
public int intValue(){
return value;
}
}*/
/*
* 基本數據類型: boolean char byte short int long folat double
* 包裝類: Boolean Character Byte Short Integer Long Float Double
* 理解:
* 基本數據類型比作爲水的話,那麼他們對應的包裝類類型就是裝水的容器
*
* */
ArrayList的最基本的用法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//ArrayList 的最基本用法
/**
* 如何創建對象:new ArrayList<>(可以直接數組初始化元素的個數 也可不填);
* 如何添加元素:add()
* 如何得到第幾個元素:List.get(int index);
* 得到所有元素個數:size();
* 如何遍歷:
* 1.for循環 2.Iterator迭代器 3.foreach(5.0)
* */
public class TestArrayList1 {
public static void main(String[] args){
//如何創建ArrayList對象
//ArrayList<Integer> list = new ArrayList<Integer>();//創建ArrayList對象並指定當中只能存放的數據類型
List<Integer> list = new ArrayList<Integer>(5);//多態 創建ArrayList對象並指定當中只能存放的數據類型 如果可以確定集合當中以公會存放多少個元素,可以直接指定 不寫的話,默認10塊空間
//向ArrayList當中添加元素:add()
list.add(77);//new Integet(77);
list.add(new Integer(55));
list.add(99);
list.add(55);
System.out.println(list.size());//得到集合當中元素個數:size()
// 如何得到集合當中的第幾個元素:get(int index)
Integer num = list.get(0);//data[0]
System.out.println(num);
System.out.println(list);//[77, 55, 99, 55] ArrayList對象已經覆蓋過了toString()
//如何遍歷ArrayList
//1st way:for()+[i]
System.out.println("====================");
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("====================");
//2nd way:Iterator 迭代器 它也可以指定泛型 1.測試還有沒有下一個元素:hasNext(); 2.得到集合中的下一個元素:next();3.刪除當前的元素 remove()
Iterator<Integer> car = list.iterator();//集合對象調用iterator() 返回該集合對象便利用的迭代器
while(car.hasNext()){//測試是否還有下一個元素
Integer data = car.next();//得到下一個元素
System.out.println(data);
}
System.out.println("====================");
//3rd way: foreach (5.0)
for(Integer i:list){
System.out.println(i);
}
/*
* length length() length() size()
* 數組 字符串 文件 集合
*
* */
}
}
ArrayList中常用的 但被我們容易忽略的一些方法
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//ArrayList 還有很多經常用到的方法 addAll();把一個集合中的所有元素添加到另一個集合中(合二爲一) subList(0,3);==>從下標0開始取出到下標3的所有元素組成一個新的集合
// indexOf(Object);返回指定對象的下標索引值 如果不存在返回-1 lastIndexOf(Object);返回指定對象最後出現的位置 contains(Object);//測試是否包含一個元素
public class TestArrayList2 {
public static void main(String[] args) throws Exception{
ArrayList<Integer> list = new ArrayList<Integer>(5);
// Class c = Class.forName("TestArrayList2");//得到TestArrayList2.class文件的類 類型對象
// Method[] ms = c.getDeclaredMethods();//得到當前類中所有方法 getDeclaredMethods() 返回Method[]
// for(Method m:ms){
// System.out.println(m);
// }
//向集合中添加元素
// list.add(1);
// list.add(2);
// list.add(3);
// list.add(4);
// list.add(5);
//向集合中添加元素 可以直接使用Collections.addAll();
//addAll(list,1,2,3,4);
Collections.addAll(list, 1,2,3,4,5);//Connection是一個接口 Collections是集合常用工具類
System.out.println(list.size());
//刪除元素 ArrayList集合當中提供了兩個remove(int index)明確的刪除第幾個元素 和 remove(Object obj)
list.remove(1);//優先被認定爲 int index
System.out.println(list);
//remove(Object obj) 明確刪除哪一個元素
list.remove(new Integer(5));
System.out.println(list);
//清空整個集合 兩種方法 while(list.size()!=0) list.remove(0);//集合內有元素 就將下標0刪除 list.clear();//sun公司提供的清空整個集合的方法
//list.clear();
System.out.println(list.contains(new Integer(7)));
}
/*
public static void addAll(List list,Integer ... data){//5.0 可變參
//遍歷
for(Integer i:data){
list.add(i);//添加元素
}
}*/
}
import java.lang.reflect.Field;
import java.util.ArrayList;
//ArrayList 那些應該知道 而經常被忽略掉的:trimToSize(); ensureCapacity(int size);
//ArrayList 不定義默認10塊空間就只能放10個元素,當放第十一個元素的時候會觸發擴容操作
//jdk7.0之前 擴容機制:*3/2+1 7.0和8.0 擴容機制: old+(old>>1) 1.5倍擴容
public class TestArrayList4 {
public static void main(String[] args)throws Exception{
//6.0: 10 16 25 38 ...
//7.0: 10 15 22 33 ...
//如果你已經確定最重要存放多少個元素,那麼最好在構造的時候構造方法直接傳入參數
Object obj = new Object();//測試不使用ensureCapacity(int size);時 執行效率
int size = 5000000;
ArrayList<Object> list = new ArrayList<Object>();
//從1970年1月1日0點0分000毫秒 - 執行這行代碼時總共經歷了多少毫秒
long time1 = System.currentTimeMillis();//System.nanoTime();隨機返回一個長整型的納秒數 System.currentTimeMillis();返回毫秒數
//向list中不斷地添加obj元素
for(int i = 0;i<size;i++)
list.add(obj);
list.trimToSize();
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
//ArrayList 創建對象的時候 構造方法直接傳參
//ArrayList<Object> list2 = new ArrayList<Object>(size);
//如果要是不能確定底層空間 我們需要在夠早完了之後通過ensureCapacity(int size)來調整底層的個數
ArrayList<Object> list2 = new ArrayList<Object>();
list2.ensureCapacity(size);//將底層數組調整到我們最終需要的那個大小
long time3 = System.currentTimeMillis();
for(int i = 0;i < size;i++){
list2.add(obj);
}
list2.trimToSize();
long time4 = System.currentTimeMillis();
System.out.println(time4 - time3);
/*
//如果不確定,在元素添加完成之後,底層的數組元素個數未必和實際的元素個數相同,所以要學會使用list.trimToSize();
//利用反射 測試ArrayList底層擴容機制
for(int i = 0;i < 12; i++){
list.add(i);//new Integer(i)
}
//在添加完元素以後 應該在手動調用一下trimToSize() 將數組大小修正到你實際用了多少 就要多少空間
list.trimToSize();
Class c = Class.forName("java.util.ArrayList");
Field f = c.getDeclaredField("elementData");//參數elementData 是ArrayList類中的一個屬性 Field 代表屬性(用來獲得屬性的) Method代表方法 ( 用來獲得方法名字的)
f.setAccessible(true);//反射面前無私有,反射可以破封裝
Object[] data = (Object[])(f.get(list));//通過list當中得到f的那個屬性 然後再進行強轉成Object[] 因爲本身就是Object[] 當然f.get(list)方法爲了保證萬能實用性將返回類型定義成了Object
System.out.println(data.length);//打印list當中的那個私有屬性elementData
*/
}
}
ArrayList中的remove(Object obj) 是如何找到元素 以及刪除元素的
import java.util.ArrayList;
import java.util.Collections;
//ArrayList的remove(Object obj) 它如何找到元素的 如何刪除你的元素
//添加到集合的是i1 而刪除的是i2 爲什麼調用remove()的時候 卻把i1給刪掉了? 因爲ArrayList的remove方法完全尊重equals的比較結果
public class TestArrayList3 {
public static void main(String[] args){
// ArrayList<String> list = new ArrayList<String>();
// //Integer i1 = new Integer(7);
// //Integer i2 = new Integer(7);
// String str1 = new String("okay");
// String str2 = new String("okay");
// //System.out.println(i1 == i2);//false 創建了兩塊空間 怎麼可能是同一個對象 (new) == 比較兩個引用對象的內存地址是否相同
// list.add(new String(str1));
// System.out.println(list.size());//1
// list.remove(str2);
// System.out.println(list.size());//0 因爲ArrayList的remove() 完全遵守equals比較機制
/*
*在某些情況下 我們需要將兩個不同的對象根據其特定的屬性值將其認定爲是相同的對象進行操作,這時 我們需要覆蓋Object類提供的equals方法
*
* */
ArrayList<Student> stu = new ArrayList<Student>();
Student stu1 = new Student("tom",12);
Student stu2 = new Student("tom",20);
stu.add(stu1);
System.out.println(stu.size());
stu.remove(stu2);//==>stu1.equals(stu2) Object 中equals方法底層是連等比較(比較的是引用地址) 那麼如何將屬性相同的視爲同一個對象呢?需要覆蓋equals方法
System.out.println(stu.size());
}
}
class Student{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
//如果兩個對象的內容相等,我們就將其認定爲同一個對象 換言之 其實就是將什麼樣的兩個Student當做同一個對象
@Override
public boolean equals(Object obj){
if(obj == null) return false;
if(!(obj instanceof Student)) return false;//instanceof 用來判斷左邊的是不是右邊的一個實力 換言之 Obj是不是一個Student類型的對象
if(obj == this) return true;
Student s1 = this;//參與比較的第一個對象
Student s2 = (Student)obj;//需要的是Student類型,而這裏是Object類型,所以需要進行強轉
//return s1.name.equals(s2.name) && (s1.age == s2.age);//String 用equals比較內容是否相等,基本數據類型只能用連等比較
return s1.name.equals(s2.name);//只要名字相同 即認定爲同一個對象
}
}
使用迭代器遍歷ArrayList時需要注意的地方
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
//在使用迭代器遍歷ArrayList的過程當中,絕對不允許對集合整體進行任何添加刪除操作(除了未遂的 list.remove(new Integer(999));//因爲list這個集合當中根本沒有999這個元素)
//因爲在迭代器當中 會對集合的當前的元素甚至當前的操作進行一個統計,如果他發現在他遍歷集合的過程當中 他的每一次next()都回去檢查我剩餘的元素是否和應當剩餘的元素一致的
//在遍歷過程當中,只要不是通過迭代器去刪除或添加的話,就會跑出修改併發異常 應該使用car.remove();//刪除那個隱形的光標所指向的元素 不可以指定要刪除的元素
//ConcurrentModificationException => 併發修改異常
public class TestArrayList5 {
public static void main(String[] args){
List<Integer> list = new ArrayList<Integer>(5);
Collections.addAll(list, 1,2,3,4,5);
//刪除小於3的元素
Iterator car = list.iterator();
while(car.hasNext()){
Integer num = (Integer) car.next();
if(num<3){
//list.remove(num);//ConcurrentModificationException => 併發修改異常
car.remove();//刪除那個隱形的光標所指向的元素 不可以指定要刪除的元素 否則會報錯
}
}
System.out.println(list);
testCopyOnWriteArrayList();
}
/*
* 迭代器在得到任務開始遍歷集合 在沒有結束之前 它的每一次next()都會校驗剩餘的元素和它已經記錄着的那個元素是否是匹配的 只要發現不一樣就會跑出修改併發異常
* 如何解決:
* 5.0之前:必須使用迭代器的remove方法刪除元素
* 5.0之後:可以使用util包中concurrent子包中提供的CopyOnWriteArrayList類
*
* */
//使用CopyOnWriteArrayList
public static void testCopyOnWriteArrayList(){
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();//不允許指定空間
Collections.addAll(list, 1,2,3,4,5);
//刪除小於3的元素
Iterator car = list.iterator();
while(car.hasNext()){
Integer num = (Integer)car.next();
if(num<3)
list.remove(num);
}
System.out.println(list);
}
}
模仿ArrayList中 幾個常用的核心實現
//模仿ArrayList底層實現(主要的方法)
public class TestArrayList6 {
public static void main(String[] args){
MyList list = new MyList(3);
list.add(77);
list.add(88);
list.add(99);
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
//自定義一個ArrayList
class MyList{
private Object[] data;//null
private int size;//用戶究竟存放了多少個元素進來,就是一個計數器 add==>size++ remove==>size--
//帶參的構造方法 根據用戶指定的容量初始化底層數組的構造方法
public MyList(int length){
data = new Object[length];
}
//默認的構造方法,它(this(10))共享了有參構造方法 默認10塊空間
public MyList(){
//data = new Object[10];//默認分配10塊空間
this(10);//構造方法中 首行this是使兩個構造方法代碼共享用的,相當於給上邊的帶參的構造方法穿了一個10進去
}
//用來得到已經存放了多少個元素的個數
public int size(){
return size;
}
//得到第幾個元素的方法 get (int index)
public Object get(int index){
return data[index];
}
//添加元素的方法
public void add(Object obj){
if(data.length == size){//如果底層的數組存儲空間已經滿了
Object[] temp = new Object[data.length*3/2+1];//那麼 需要一個更大的新數組空間
System.arraycopy(data, 0, temp, 0, size);//將老數組中的元素複製到新數組中1.要複製的原數組,2.從元素組的第幾個元素開始複製,3.複製到那個數組中,4.複製到新數組的第幾個位置(按下標),5.總共複製多少個
data = temp;//返回新數組,將老數組交給垃圾回收器 這個方法一結束 temp就會被回收,所以要再重新將值保存給Object []data
}
data[size++] = obj;//size 上邊我們定義的那個下標 那個計數器 size++;//統計一空存了多少個元素
}
//刪除第幾個元素的方法 採用複製的方式實現的
public void remove(int index){
System.arraycopy(data, index+1,data ,index, size-index-1);
size--;
/*刪除下標2指向的那個元素
* 0 1 2 3 4 //要刪除的是下標2 那麼其實就是從下標3開始向下標2複製元素 總共複製2個元素(元素的總個數 - 我要刪除的下標 - 1 即 5-2-1)
* 0 1 3 4
*
* */
}
//刪除指定的那個元素的方法
public void remove(Object obj){
for(int i = 0;i<size;i++){//size 是本類中的屬性 即便是私有的 在本來中也可以得到
//拿着用戶傳進來的每一個obj 去調用equals方法和我data[i]個元素進行比較,如果哪一個返回true就remove掉
if(obj.equals(data[i])){
remove(i);//調用上邊的那個remove方法 remove(int index)
return ;//將操作的結果返回回去,直接結束for循環,另外一個remove只能刪除一個元素,要想刪除多個 需要調用removeAll方法
}
}
}
}