集合框架概述
1.集合、數(shù)組都是對(duì)多個(gè)數(shù)據(jù)進(jìn)行存儲(chǔ)操作的結(jié)構(gòu)蜻直,簡稱java容器
此時(shí)的存儲(chǔ)盯质,主要指的是內(nèi)存層面的存儲(chǔ)袁串,不涉及到持久化的存儲(chǔ)(.txt,.jpg數(shù)據(jù)庫中)
2.數(shù)組在存儲(chǔ)多個(gè)數(shù)據(jù)方面
(1)特點(diǎn):一旦初始化后,長度確定呼巷;元素?cái)?shù)據(jù)類型確定般婆,只能操作指定類型的數(shù)據(jù);如:String[ ]朵逝、Object[ ] arr2
(2)缺點(diǎn):
a.一旦初始化蔚袍,長度不可修改;
b.數(shù)組提供的方法有限配名,對(duì)于添加啤咽、刪除、插入數(shù)據(jù)等操作渠脉,非常不便宇整,同時(shí)效率不高;
c.獲取數(shù)組中實(shí)際元素個(gè)數(shù)的需求芋膘,數(shù)組沒有現(xiàn)成的屬性和方法鳞青;
d.數(shù)組有序、可重復(fù)为朋,對(duì)于無序臂拓、不可重復(fù)需求,不能滿足
java集合分為Collection和Map兩種體系
Collection接口:單列數(shù)據(jù)习寸,定義存取一個(gè)一個(gè)對(duì)象的方法集合
1. List接口:有序胶惰,可重復(fù)(動(dòng)態(tài)數(shù)組)
|--------------------ArrayList:作為List的主要實(shí)現(xiàn)類,線程不安全霞溪,效率高孵滞;底層使用Object[ ]存儲(chǔ)、
|--------------------LinkedList:對(duì)于頻繁的插入鸯匹、刪除操作坊饶,其比ArrayList高;底層使用“雙向鏈表”存儲(chǔ)
|--------------------Vector:作為List接口的古老實(shí)現(xiàn)類殴蓬,線程安全匿级,效率低;底層使用Object[ ]存儲(chǔ)
2.Set接口:無序科雳,不可重復(fù)(數(shù)學(xué)中的“集合”)根蟹,操作過濾問題,即相同數(shù)據(jù)過濾掉
|--------------------HashSet:作為Set接口的主要實(shí)現(xiàn)類糟秘;線程不安全简逮;可以存儲(chǔ)null值
|-------------------------------LinkedHashSet:在HashSet基礎(chǔ)上添加了Link,是HashSet的子類尿赚,使其在遍歷內(nèi)部數(shù)據(jù)時(shí)散庶,可以按照添加的順序遍歷(不同于有序)
|--------------------TreeSet:可以按照添加對(duì)象的指定屬性進(jìn)行排序
Map:雙列數(shù)據(jù)蕉堰,保存具有映射關(guān)系”key-value對(duì)“的集合(數(shù)學(xué)中函數(shù)y=f(x))
|------HashMap:Map的主要實(shí)現(xiàn)類,線程不安全悲龟,效率高屋讶;可以存儲(chǔ)null(jdk7之前,底層數(shù)組+鏈表须教;jdk8皿渗,數(shù)組+鏈表+紅黑樹)
|--------------------LinkedHashMap:保證在遍歷map元素時(shí),可以按照添加的順序?qū)崿F(xiàn)遍歷轻腺。原因:在原有HashMap的底層結(jié)構(gòu)基礎(chǔ)上乐疆,添加了一堆指針,指向前一個(gè)和后一個(gè)元素贬养,對(duì)于頻繁的遍歷操作挤土,執(zhí)行效率高于HashMap
|------TreeMap:按照添加的key-value對(duì)進(jìn)行排序,實(shí)現(xiàn)排序遍歷误算。其中key的排序?yàn)樽匀慌判蚧蚨ㄖ婆判蜓雒溃坏讓邮褂玫募t黑樹
|------Hashtable:古老的實(shí)現(xiàn)類,線程安全儿礼,效率低咖杂;不能存儲(chǔ)null
|--------------------Properties:常用來處理配置文件,其中key和value都是String
Collection接口中聲明方法的測試
向Collection接口的實(shí)現(xiàn)類的對(duì)象中添加數(shù)據(jù)obj時(shí)蜘犁,要求obj所在類要重寫equals()
Collection c1 = new ArrayList();
c1.add(new Person("xiaohong",12));
c1.add(new Person("xiaohong",12));
//false,未重寫equals方法時(shí):即Object的equals方法相當(dāng)于==翰苫,基本數(shù)據(jù)類型,比較值这橙;引用數(shù)據(jù),比較地址
// System.out.println(c1.contains(new Person("xiaohong", 12)));
System.out.println(c1.contains(new Person("xiaohong", 12)));//true,重寫equals方法時(shí)导披,即比較值
Collection c2 = Arrays.asList(12);
System.out.println(c1.containsAll(c2));
下列方法操作的對(duì)象:
Collection c1 = new ArrayList();
c1.add(12);//表示數(shù)字
c1.add("12");//表示字符串
c1.add(new Person("xiaohong",12));
c1.add(new Person("xiaohong",12));
Collection c2 = Arrays.asList(12,13);
(1)retainAll(Colection coll):交集
System.out.println(c1.retainAll(c2));//true,兩個(gè)集合的交集
System.out.println(c1);//[12] 交集后輸出c1
(2)removeAll(Collection coll):差集
System.out.println(c1.removeAll(c2));//true,移除c1中存在的c2屈扎,即差集
System.out.println(c1);//[12, com.vv.string.Person@0, com.vv.string.Person@0]
(3)hashCode():哈希值
//返回當(dāng)前對(duì)象的哈希值
System.out.println(c1.hashCode());
(4)toArray():集合--》數(shù)組
//1.集合---->數(shù)組
Object[] objects = c1.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.print(objects[i] + "\t");
}
System.out.println();
//2.數(shù)組----->集合
List<Object> objects1 = Arrays.asList(objects);
System.out.println(objects1);//[12, 12, com.vv.string.Person@0, false]
//a.基本數(shù)據(jù)類型數(shù)組--->集合 將數(shù)組整體存入
List<int[]> ints = Arrays.asList(new int[]{123, 145});
System.out.println(ints);//[[I@5d6f64b1] int型的一維數(shù)組
System.out.println(ints.size());//1
//b.包裝類的數(shù)組--->集合
List<Integer> integers = Arrays.asList(new Integer[]{12, 45});
System.out.println(integers);//[12, 45]
System.out.println(integers.size());//2
(5)Iterator(迭代器)
1.主要用于遍歷Collection集合中的元素;
2.Collection接口繼承了java.lang.Iterable接口撩匕,該接口有一個(gè)iterator方法鹰晨,因此所有實(shí)現(xiàn)了Collection接口的集合都有一個(gè)iterator方法,用來返回一個(gè)實(shí)現(xiàn)了iterator接口的對(duì)象
3.Iterator僅用于遍歷集合止毕,其本身并不提供承裝對(duì)象的能力模蜡;但若需要?jiǎng)?chuàng)建Iterator對(duì)象,則必須有一個(gè)被迭代的集合
4.每次調(diào)用iterator方法都會(huì)得到一個(gè)全新的迭代器對(duì)象
兩種錯(cuò)誤的迭代方式:
// 錯(cuò)誤方式一:結(jié)果跳著輸出且NoSuchElementException
Iterator iterator = c1.iterator();
while(iterator.next() != null){
System.out.println(iterator.next());//
}
//每次c1.iterator().hasNext()都會(huì)創(chuàng)建新的迭代器對(duì)象
//錯(cuò)誤方式二:一直輸出第一個(gè)位置的值
while (c1.iterator().hasNext()){
System.out.println(c1.iterator().next());
}
(6)remove(Object obj)
Iterator iterator = c1.iterator();
while (iterator.hasNext()){
if("13".equals(iterator.next())){//????數(shù)字如何和迭代器元素比較
iterator.remove();
}
// System.out.print(iterator.next()+"\t");//出錯(cuò)扁凛,NoSuchElementException忍疾,原因是迭代器中元素改變了
}
Iterator iterator1 = c1.iterator();
while (iterator1.hasNext()){
System.out.print(iterator1.next()+"\t");//12 Person{name='xiaohong', age=14} false
}
length、length()谨朝、size()區(qū)別
length——數(shù)組的屬性卤妒;
length()——String的方法甥绿;
size()——集合的方法;
JDK5.0:ForEach遍歷集合和數(shù)組
//for(集合元素類型 局部變量:集合對(duì)象)
for(Object obj:coll)
Iterator iterator1 = c1.iterator();
for(Object c : c1){//內(nèi)部仍然調(diào)用了迭代器
System.out.print(iterator1.next()+"\t");
}
例題
int[] arr = new int[]{112,3,4};
//方式一賦值
for (int i = 0; i < arr.length; i++) {
arr[i] = 4;//對(duì)本身的數(shù)組元素進(jìn)行修改
}
//方式二賦值
// for(int a : arr){
// a = 4;//取出來则披,重新對(duì)s賦值共缕。在常量池中
// }
for(int a : arr){
System.out.print(a + "\t");//方式一:4 4 4 方式二:112 3 4
}
List接口
ArrayList、LinkedList士复、Vector三者異同
ArrayList
jdk7.0中的ArrayList對(duì)象的創(chuàng)建類似于單例的餓漢式图谷,底層創(chuàng)建了長度為10的Object[ ];而jdk8.0中的ArrayList對(duì)象的創(chuàng)建類似于單例的懶漢式阱洪,底層的Object[ ]初始化為{ },延遲了數(shù)組創(chuàng)建便贵,節(jié)省內(nèi)存
擴(kuò)容2倍
LinkedList
內(nèi)部沒有聲明數(shù)組,而是定義了Node類型的first和last澄峰,用于記錄首末元素嫉沽。其中Node用于保存數(shù)據(jù),其prev和next兩個(gè)變量分別記錄前一個(gè)和后一個(gè)元素
擴(kuò)容1.5倍
List接口中常用方法
List中:remove(int index)
ArrayList list = new ArrayList();
list.add(12);
list.add("xiaohong");
list.add(new Person("hh",10));
list.add(false);
System.out.println(list.remove(2));//Person{name='hh', age=10}
區(qū)別Colection中:remove(Object o)
c1.add("20");//表示字符串
c1.add(new Person("xiaohong",12));
System.out.println(c1.remove("20"));//true
System.out.println(c1);//[Person{name='xiaohong', age=12}]
subList(begin end)類似于String中的subString(begin end)都是左開右閉
List中主要方法
增/插add刪remove改set查get長度size()遍歷(1)Iterator(2)forEach(3)普通循環(huán)
例題:remove考察
@Test
public void test1(){
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
updateList(list);
System.out.println(list);
}
void updateList(List list){
// list.remove(2);//[1 ,2] 刪除索引位置的值
list.remove(new Integer(2));//[1,3] 刪除該對(duì)象
}
Set接口中的方法
Set接口中沒有額外定義的新方法俏竞,使用的都是Collection中聲明過的方法(無序即代表沒有索引)
List接口中有額外定義的新方法绸硕,因?yàn)長ist中有索引(有順序即代表有索引)
HashSet論證Set的無序性,并不是代表隨機(jī)性
HashSet存儲(chǔ)數(shù)據(jù)時(shí)魂毁,底層數(shù)組并非按照數(shù)組索引的順序添加玻佩,而是根據(jù)數(shù)組的哈希值確定
不可重復(fù)性:保證添加的元素按照equals()判斷時(shí),不能返回true席楚,即相同的元素只能添加一個(gè)
@Test
public void test(){
Set set = new HashSet();
set.add(12);
set.add("hh");
set.add(new Person("zhangsan",10));
set.add(new Person("zhangsan",10));
System.out.println(set);
}
添加元素:以HashSet(其底層是數(shù)組+鏈表)為例:
向HashSet中添加元素a咬崔,先調(diào)用hashCode,計(jì)算其哈希值烦秩,然后哈希值接著通過某種計(jì)算方法計(jì)算出在HashSet底層數(shù)組中的存放位置(即為:索引位置)垮斯,判斷數(shù)組此位置上是否已經(jīng)有元素:若沒有,則a添加成功只祠;若有元素b兜蠕,首先比較a,b的哈希值抛寝,若哈希值不相同熊杨,則a添加成功;若相同盗舰,進(jìn)而需要調(diào)用元素a所在類的equals方法晶府,equals返回true,a添加失敗钻趋。返回false川陆,a添加成功
對(duì)于a添加時(shí)其位置有元素而言,a和已經(jīng)存在于指定索引位置上的數(shù)據(jù)之間以鏈表方式存儲(chǔ)(jdk7爷绘,a放到數(shù)組中书劝,指向原來的元素进倍;jdk8,原來的元素在數(shù)組中购对,指向元素a)
Set set = new HashSet();
set.add(12);
set.add("hh");
set.add(new Person("zhangsan",10));
set.add(new Person("zhangsan",10));
// //當(dāng)未重寫equals和hasCode時(shí)
// System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
// //當(dāng)只重寫equals時(shí)
// System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
//當(dāng)只重寫hasCode時(shí)
System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
// //當(dāng)重寫equals和hasCode時(shí)
// System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12]
向Set中添加的數(shù)據(jù)猾昆,其所在的類必須進(jìn)行hashCode和equals重寫,重寫的hashCode和equals盡可能保持一致性骡苞,相等的對(duì)象必須具有相等的散列碼(hashCode)
LinkedHashSet
作為HashSet的子類垂蜗,在添加數(shù)據(jù)時(shí),每個(gè)數(shù)組還維護(hù)了兩個(gè)引用解幽,記錄此數(shù)據(jù)的前后數(shù)據(jù)贴见,其對(duì)于“頻繁的遍歷操作”,LinkedHashSet頻率高于HashSet
TreeSet
1.向TreeSet添加數(shù)據(jù)躲株,必須是相同的對(duì)象;
2.排序方式自然排序片部、定制排序
自然排序:
兩對(duì)象進(jìn)行比較時(shí),為CompareTo方法霜定,不再是equals方法
Set set = new TreeSet();
/*
TreeSet中存放不同對(duì)象出錯(cuò)java.lang.Integer cannot be cast to java.lang.String
set.add(12);
set.add("hh");
set.add(new Person("xiao",15));
System.out.println(set);
*/
/*
//1.基本數(shù)據(jù)類型存儲(chǔ)档悠,默認(rèn)從小到大排序
set.add(12);
set.add(32);
set.add(2);
System.out.println(set);//[2, 12, 32]
*/
//2.對(duì)象存儲(chǔ),
/*
未對(duì)對(duì)象實(shí)現(xiàn)Comparable,重寫CompareTo方法望浩,即并未告知TreeSet以對(duì)象的哪種屬性進(jìn)行排序
com.vv.string.Person cannot be cast to java.lang.Comparable
*/
set.add(new Person("hong",15));
set.add(new Person("wang",5));
set.add(new Person("zhang",35));
set.add(new Person("zhang",25));
/*在Person中實(shí)現(xiàn)Comparable接口時(shí)辖所,對(duì)屬性進(jìn)行按照指定大小進(jìn)行排序*/
// //當(dāng)僅對(duì)name屬性進(jìn)行比較時(shí)
// System.out.println(set);//[Person{name='hong', age=15}, Person{name='wang', age=5}, Person{name='zhang', age=35}]
//當(dāng)對(duì)name和age屬性進(jìn)行比較時(shí)
System.out.println(set);//[Person{name='hong', age=15}, Person{name='wang', age=5}, Person{name='zhang', age=25}, Person{name='zhang', age=35}]
定制排序:Comparator
Set set = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
return -Integer.compare(((Person) o1).age,((Person) o2).age);
//[Person{name='zhang', age=35}, Person{name='zhang', age=25}, Person{name='hong', age=15}, Person{name='wang', age=5}]
}else{
throw new RuntimeException("對(duì)象類型不一致");
}
}
});
例題:
1.屬性排序
//生日排序
@Test
public void test(){
Employee e1 = new Employee("xiaohong",12,new MyDate(1926,2,6));
Employee e2 = new Employee("zhangsan",82,new MyDate(1823,3,7));
Employee e3 = new Employee("lisi",62,new MyDate(1926,5,6));
Employee e4 = new Employee("zhaoliu",12,new MyDate(1753,2,9));
Employee e5 = new Employee("dawei",18,new MyDate(1926,5,3));
//使用Comparator對(duì)生日進(jìn)行排序
TreeSet set = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Employee && o2 instanceof Employee) {
Employee e1 = (Employee) o1;
Employee e2 = (Employee) o2;
MyDate m1 = e1.getBirthday();
MyDate m2 = e2.getBirthday();
/*//方式一:直接在該方法中比較
int year = m1.getYear() - m2.getYear();
//比較年
if (year != 0) {
return Integer.compare(m1.getYear(), m2.getYear());
}
int month = m1.getMonth() - m2.getMonth();
//比較月
if (month != 0) {
return Integer.compare(m1.getMonth(), m2.getMonth());
}
//比較日
return Integer.compare(m1.getDay(), m2.getDay());*/
//方式二:在MyDate中實(shí)現(xiàn)Comparable接口
return m1.compareTo(m2);
}
throw new RuntimeException("對(duì)象不一致");
}
});
// TreeSet set = new TreeSet();
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
// Iterator iterator = set.iterator();
for(Object o : set ){
System.out.println(o);
}
}
//name排序的同時(shí),按照生日排序
//在Person類中比較
@Override
public int compareTo(Object o) {
if(o instanceof Employee){
Employee e = (Employee) o;
int i =this.name.compareTo(e.getName());
if(i == 0){
return this.getBirthday().compareTo(e.getBirthday());
}else{
return i;
}
}
throw new RuntimeException("對(duì)象類型不一致");
}
2.在List內(nèi)去除重復(fù)元素
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Integer(2));
list.add(new Integer(2));
list.add(new Integer(1));
List list1 = updateSet(list);
for(Object o : list1){
System.out.print(o + "\t");
}
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
// }
}
public static List updateSet(List list){
HashSet set = new HashSet();
//若list中添加的是自定義對(duì)象磨德,則需要進(jìn)行hashCode重寫缘回;Integer的equals方法已經(jīng)重寫了
set.addAll(list);
return new ArrayList(set);
}
3.set***********太坑了
HashSet set = new HashSet();
Person p1 = new Person("AA",11);
Person p2 = new Person("BB",22);
set.add(p1);
set.add(p2);
p1.setName("CC");
set.remove(p1);
// 首先算出此時(shí)name為CC和age為11的哈希值,再與name為AA和age為11的哈希值比較典挑,結(jié)果不等酥宴;
// 因此,此時(shí)刪除的位置其實(shí)為空您觉,故存儲(chǔ)結(jié)果為[Person{name='BB', age=22}, Person{name='CC', age=11}]幅虑,
System.out.println(set);
set.add(new Person("CC",11));
//name為CC和age為11的位置存儲(chǔ)添加的數(shù)據(jù),其中結(jié)果的值顾犹,一個(gè)為AA所在位置的值替換,另一個(gè)為新的位置
// [Person{name='BB', age=22}, Person{name='CC', age=11}, Person{name='CC', age=11}]
System.out.println(set);
//雖然AA的位置仍在褒墨,但通過equals比較時(shí)發(fā)現(xiàn)炫刷,兩者對(duì)象不一樣,因此使用鏈表重新指向
//[Person{name='BB', age=22}, Person{name='CC', age=11}, Person{name='CC', age=11}, Person{name='AA', age=11}]
set.add(new Person("AA",11));
System.out.println(set);
Map接口
Map中的key:無序郁妈、不可重復(fù)浑玛,使用Set存儲(chǔ)所有的key--》key所在類要重寫equals和hashCode方法(例如:containsKey需要重寫equals和hashCode)
Map中的value:無序、可重復(fù)噩咪,使用Collection存儲(chǔ)所有的value--》value所在類要重寫equals方法(例如:containsValue需要重寫equals方法)
一個(gè)鍵值對(duì):key-value構(gòu)成了一個(gè)Entry對(duì)象顾彰。
Map中的entry:無序极阅、不可重復(fù),使用Set存儲(chǔ)所有的entry
map元試圖操作方法:
Map map = new HashMap();
map.put(1,2);
map.put(null,3);
map.put("AA",9);
//遍歷key
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println();
//遍歷value
Collection values = map.values();
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
//遍歷key-value
//方式一:
Set entrySet = map.entrySet();
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()){
Object obj = iterator2.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+"......"+entry.getValue());
}
//方式二:
Set set1 = map.keySet();
Iterator iterator3 = set1.iterator();
while (iterator3.hasNext()){
Object obj = iterator3.next();
Object value = map.get(obj);
System.out.println(obj+"......"+value);
}
TreeMap的key必須是同一個(gè)類創(chuàng)建的對(duì)象,因此要按照key排序:自然排序或者定制排序
Map map = new TreeMap();
map.put(new User("zhangsan",16),"shanxi");
map.put(new User("lisan",16),"xian");
map.put(new User("lisan",15),"dali");
Set set = map.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+"..........."+entry.getValue());
}
Collections是一個(gè)操作Collection(Set涨享、List)和Map等集合的工具類
注意:copy方法
List list = new ArrayList();
list.add(12);
list.add(34);
list.add(34);
list.add(85);
// //錯(cuò)誤寫法一:Source does not fit in dest筋搏,看源碼后:list.size() > list1.size()
// List list1 = new ArrayList();
// Collections.copy(list1,list);
//錯(cuò)誤寫法二:list1.size()為0,size()表示add后的數(shù)組長度
// List list1 = new ArrayList(list.size());//表示的是數(shù)組的長度
// Collections.copy(list1,list);
//eg:
List l = new ArrayList(9);//創(chuàng)建長度為9的數(shù)組
System.out.println(l.size());//0
List dest = Arrays.asList(new Object[list.size()]);
Collections.copy(dest,list);
System.out.println(dest);//[12, 34, 34, 85]
Collections中的synchronizedXxx()方法厕隧,可使指定集合包裝成線程同步的集合奔脐,從而解決多線程并發(fā)問題