一、集合概述;
集合類,或者叫集合框架;
數據多了,封裝爲對象;對象多了,封裝爲集合;
對象多了,可以存儲於數組,但是數組是固定長度的,集合是可變長度的;而且數組中只能同類類型數據,集合不限類型;
集合中的對象,被稱爲元素;
集合的特點是用於存儲對象,集合的長度是可變的,集合可以存儲不同類型的對象;
集合的作用就相當於是一個封裝對象的容器;
容器分很多中,容器的共性不斷的抽取就產生了體系,這個體系就叫做集合框架;
在一個體系中通過最頂層來認識共同基本的功能,再讓底層子類進行功能的實現和擴展並加以利用;
接口的方法有了,下面就要建立其子類對象來對其進行使用;
集合框架是工具包的成員,Java.util.中的Collection成員;Collection是這個工具包的接口;
Collection接口有兩個常見的子接口,一個是List,一個是Set;
List接口中常見的子類有:ArrayList,LinkedList,Vector;
Set接口中常見的子類有HashSet,TreeSet;
爲什麼會出現這麼多的容器呢?因爲每一個容器對數據的存儲方式都有不同,這個存儲方式稱之爲:數據結構;
數據在內存中的構成情況,存儲方式稱爲數據結構,所以他們的特點不一樣,所以就要單獨的劃分;
二:Collection接口中的共性方法;
集合作爲一個容器,應該有的功能有增刪改查;
接口的使用是,先實現子類,然後建立子類對象即ok;
ArrayList al = new ArrayList ();
1、添加元素:
al.add(“abc”); 字符串也是對象;
集合中存在的不可能是集合實體;否則還要讓實體移位置;
集合和數組一樣,裏面存放的都是地址值;
注意:add方法的參數類型是Object,以便於接受任意類型對象;
集合中存儲的都是對象的引用(地址);
al.size(al);獲取集合的長度;
sop(al)
添加完成後直接打印集合的聲明變量,結果是這個集合裏面的所有元素,不是哈希值;
2、刪除元素:
al.remove();刪除指定元素;
al.clear();清空集合;
3、判斷元素:
al.contains();
al.isEmpty();
al1.retainAll(al2);取交集,al1中只會保留和al2中相同的元素,會對原集合進行改變;先把原集合清空,然後添加相同的元素,如果沒有相同的元素,則原集合爲空;
al1.removeAll(al2);指去掉與給定集合中相同的元素;
還有addAll等都是對相同的元素進行操作;
3、取出元素:(迭代器Iterator())
通過取出元素後對元素進行操作,直接打印集合沒有什麼實際意義;
Iterator it = al.iterator(); 獲取迭代器,用於去處集合中的元素;
該表達式返回的是一個對象,然後可以通過對象去調用其對應的類的內部方法;
(這就是單例設計模式的一個例子;)完成這步之後就用it去調用方法了。
但問題的關鍵是接口不是都是抽象沒有方法不,這個比較疑惑;是在子類中實現了該方法,只不過用接口去聲明類型,避免引入更多的變量,也是一樣的;最後引入的還是子類實現的內部類的方法
it .hasNext();判斷是否有元素;it.next();獲取下一個元素;
什麼是迭代器呢?
其實就是集合的取出元素的方式;
迭代動作;
每個容器(集合)裏面都有自己的存取方式,因爲每個容器的數據結構不一樣,所以他們存取的動作細節以及存取的方式也有可能不一樣;
因爲取出的工作沒有像添加那麼的簡單,比如還需要判斷等,所以取出不能用一個方法判斷完,雖然取出的動作細節都不一樣,即所以就把取出功能封裝成一個類,這個類就定義到了集合的內部(內部類更方便操作);因爲都是有共性的內容,判斷和取出,,所以就可以將這些進行共性抽取,而這些內部類都是符合一個規則,該規則是Iterator。如何獲取集合的取出對象呢?通過一個對外提供的方法Iterator();
其實就是說,在集合的內部,用一個內部類實現了一個取出元素的接口;以後可以嘗試把這個方法自己寫出來;
寫法上:
要迭代所有的元素,用for循環比用while循環更加節約內存;
for(Iterator it = al.iterator();it.hasNext();)
it.next();
三、Collection兩個常見子接口List和Set的特點:
List接口:這個集合小體系的特點在於集合裏面的元素是有序的,並且元素可以重複;因爲該集合體繫有索引;
Set接口:集合元素是無序的,元素不可以重複,集合當中沒有索引;
(一)List接口特有的共性方法:
1、凡是可以操作腳標的方法都是該體系特有的方法;增、刪、改、查;
增:
void add(int index, E element)
boolean addAll(int index, Collection<?extends E> c)
刪
E remove(int index)
改
E set(int index, E element)
查
E get(int index)
List<E> subList(int fromIndex, inttoIndex) (其實這個可以通過get方法循環即可;)
ListIterator<E> listIterator(intindex)
注意,但凡操作腳標的都是數組原理;
獲取對象的index位置;
int indexOf(Object o)
int lastIndexOf(Object o)
獲取子列表:
List<E> subList(int fromIndex, inttoIndex)
列表迭代器
ListIterator<E> listIterator(intindex)
在迭代過程中,準備進行其他的操作,如添加或者刪除元素;
Iterator it = al.iterator();
it.remove;移除迭代出來的動作;
List集合特有的迭代器,ListIterator是Iterator的子接口;
在迭代時,不可以通過集合對象的方法操作集合中的元素,因爲會發生併發修改異常;
所以只能在迭代時,只能用迭代器方法操作元素,可是Iterator方法有限的,只能進行判斷,取出和刪除的操作;如果想要其他的操作如修改,添加等,就需要使用其子接口,ListIterator,
該接口只能通過List集合的ListIterator方法獲取;
ListIteratorli = al.ListIterator();
voidadd(E e) 在迭代的後面的添加給定的元素;
voidset(E e) 把迭代的元素改爲給定元素;
只有List集合中的迭代器纔有這種在遍歷過程中的增刪改查功能;因爲List是有腳標的;
booleanhasNext() 指針後面有沒有元素;
booleanhasPrevious() 指針前面有沒有元素;
E previous() 往前取元素;到這取,逆向遍歷;
2、List常見的三個子類對象:是因爲底層數據結構不一樣,而單獨封爲實體的;
ArrayList 底層的數據結構使用的是數組結構;特點在於查詢速度很快,因爲有腳標;加入和刪除元素時速度非常慢,元素越多越明顯;
LinkedList 底層使用的是鏈表數據結構;列表查詢非常慢,因爲只有相鄰的元素纔有關係;但是增刪比較塊;因爲查詢必須要從第一個開始查起;
Vector 底層是數組數據結構,功能和ArrayList一模一樣;它出現的時候集合框架還不存在呢?
vector與ArrayList的區別是ArrayList線程是不同步的,Vector線程是同步的;一般用ArrayList,效率高,Vector增刪查詢都非常慢;替代了Vector,多線程中可以對ArrayList進行加鎖;
Java的集合框架中會有一個專門加鎖的功能;
ArrayList的默認容量是10的空列表,當元素增加後,他會new一個新的數組,長度增加百分之50的延長,然後把原來的數組複製過來即可
Vector是百分之百的延長,比較浪費空間;基本淘汰了;
Vector裏的唯一的特有取出數據方法——枚舉,其他都被ArrayList取代了;
Enumerationen = v.elements();
while(en.hasMoreElements())
en.nextElement();可以枚舉所有的元素出來;
發現枚舉和迭代器很像;其實枚舉和迭代是一樣的;因爲枚舉的名稱以及方法的名稱都過長,所以被迭代器取代了;枚舉和Vector都是1.0版本的,以後版本的都是用迭代器了,不用枚舉了;
LinkedList鏈表結構的List,所使用頻率不是特別高;他的特有方法有:
addFirst();
addLast();
getFirst();
getLast();
獲取元素,但不刪除元素;如果集合中沒有元素,會出現NoSuchElementException
removeFirst();——peekFirst
removeLast();——pollFirst
也可以獲取元素,但是元素被刪除;如果集合中沒有元素,會出現NoSuchElementException
在JDK1.6版本中,出現了替代方法;以後就用這個,不用之前的方法了;
booleanofferFirst(E e)
boolean offerLast(E e)
EpeekFirst()
獲取但不移除此列表的第一個元素;如果此列表爲空,則返回 null。
E peekLast()
獲取但不移除此列表的最後一個元素;如果此列表爲空,則返回 null。
EpollFirst()
獲取並移除此列表的第一個元素;如果此列表爲空,則返回 null。
E pollLast()
獲取並移除此列表的最後一個元素;如果此列表爲空,則返回 null。
練習1:使用LiskedList模擬一個堆棧或者隊列數據結構;
堆棧的數據結構的特點:先進後出;如同一個杯子;
隊列的數據結構的特點:先進先出;如同一個水管;
要先封裝起來,先把描述和功能封裝起來,然後對外提供一個最簡單的訪問方式即可;
這個練習要求必須會;
練習2:去除ArrayList集合中的重複元素;
用it.next()在循環當中只能寫一次,如果寫多次就極有可能出現腳標異常;
即在迭代時循環中next調用一次,就要hasNext判斷一次;
不能同時在一個語句中
練習3:將自定義對象作爲元素存在ArrayList集合中,並去除重複元素;
比如,存人對象,同姓名,同年齡,視爲同一個人,爲重複元素;
(1)描述人,將數據封裝進入對象;
(2)定義容器,存人;
(3)取出
List集合判斷元素是否相同,依據的元素的equals方法;contains,removed底層都是依賴的這個方法,ArrayList和LinkedList集合底層的數據結構的方法依賴的都是這個;
其他的集合和這個不一樣;
涉及的增刪操作比較頻繁用LinkedList集合
涉及的查詢操作比較頻繁用ArrayList集合
當然兩者都可以用,一般實在不好找,就用ArrayList;
(二)Set集合
元素是無序的,即存入和取出的順序不一定一致,元素不可以重複;
Set集合的功能和Collection集合功能是一致的,因爲都沒有腳標;
常見的子類:
HashSet集合:底層數據結構是哈希表; TreeSet集合
1、HashSet集合
哈希值,取出來的是按照集合裏面的哈希值的順序來的;
在哈希表裏,如果哈希值重複的時候,他還有另外一個調取方式,即對象是否是同一個對象;如果哈希值相同,就比較兩個是不是一個對象;如果不是就在此位置上順延;
如果哈希值不同,就直接按哈希值直接保存;
演示:
往hashSet集合中存入自定義對象,姓名和年齡相同爲同一個人,重複元素;
Set中取出元素就只有一種方式,即迭代器;
Set集合;可以保證唯一性;因爲不可重複性;
按照條件來設定哈希值;來保證Hashcode的唯一;
HashSet是如何保證元素的唯一性的呢?
是通過元素的兩個方法,hashcode和equals來完成的,如果元素的hashcode值相同,纔會判斷equals是否爲true,如果元素的hashcode不同,則equals爲false;
當我們要把對象存放在HashSet集合當中的時候,我們一般都要重寫成我們需要的hashcode和equals方法;而且這些方法並不是我們自己調用的,而是底層內部調用的,集合把他的調用方法給封裝了,我們不知道;
一般開發中,只要我們把數據往集合中保存,我們都最好都要重寫上面兩個函數;儘量保證hashcode的唯一性,這樣纔會主動進行比較是否相同;
如何查詢是否包含,以及刪除元素:
注意:對於判斷元素是否存在以及刪除等操作,依賴的方法都是元素的hashcode和equals方法;先判斷hashcode,相等的情況再比較equals方法;
ArrayList集合裏的方法操作只依賴equals方法;
2、TreeSet集合
set:無序,不可重複元素;
HashSet集合:底層數據結構是哈希表; 線程是非同步的;因爲數據結構不一樣,保證元素唯一性的方式也不一樣;HashSet保證元素的唯一性的原理是:判斷元素的hashcode是否相同,如果相同,還會繼續判斷元素的equals方法,是否爲true;
TreeSet集合:特點是,可以對set集合中的元素進行排序;是按照自然順序來排序的;
底層數據結構是二叉樹;保證元素唯一性的依據是compareto 方法return 0;
其所有的操作也都是依賴這個方法進行操作,如判斷包含,刪除等;
演示:存自定義數據演示該集合
需求:往TreeSet集合中存儲自定義對象學生,想按照學生的年齡進行排序;
TreeSet集合往裏面存放的元素必須要具有比較性才行;否則會拋出錯誤;
因爲要排序,並且該集合不能重複,所以只要判斷是相同對象,就不會進入集合;
注意:排序時,當主要條件相同時,一定要判斷次要條件是否相同;
TreeSet底層的數據結構是:
排序無非就是互相比較,如果元素太多,就會導致效率非常慢;所以爲了優化效率,TreeSet優化了數組的效率,TreeSet用了一個比較特殊的數據結構來做這個事情;
二叉樹數據結構,可以減少比較次數;而且,如果元素比較多的情況,他比較到最後,會自動取折中值來進行比較;這樣就可以提高效率;
取值都是按照默認順序,從小到大來取;二叉樹的數據結構都是從左邊最小的數據往上開始取值;
如果要降序排列,可以把重寫的compareTo中返回值中1改爲-1等;
TreeSet排序只看compare的比較結果,不看你是用什麼方法比的;
可以讓TreeSet取出數據的方式按照存放順序取,只需要重寫比較方法,返回1即可,逆序也一樣;
TreeSet排序的第一種方式,讓元素自身具備比較性,元素需要實現comparable接口,覆蓋comparato方法,這種方式也稱爲元素的自然排序,或者叫做默認排序;
元素一定義完,就具備了比較性了,具有默認順序了;
TreeSet的的第二種排序方式:
當元素自身不具備比較性,或者具備的比較性不是所需要的;這時就需要讓集合自身具備比較性;在集合初始化時,就有了比較方式;
當元素自身不具備比較性,或者具備的比較性不是所需要的,這是需要讓容器自身具備比較性,定義了比較器,將比較器對象作爲參數傳遞給TreeSet集合的構造函數;
當兩種排序都存在時,以比較器爲主;
定義一個類,實現comparator接口,覆蓋compare方法
二叉樹都是以判斷return是否爲0,來比較是否相等;
一般,比較器的方法用的比較多;功能擴展就直接實現需要的接口即可;所以開發時候一定都把接口弄出來,以後直接升級實現接口就搞定了
練習:按照字符串長度排序
字符串本身具備比較性,但是他的比較方式不是所需要的,這時就只能使用比較器;
當然也可以把封裝的比較器用匿名內部類做也行;