集合框架整理
集合框架可以分為兩個部分:Collection和Map,詳細的框架圖如下:
我們先來分析一下Collection集合框架,Collection包括兩大體系List和Set,其中List中的元素存取有序、元素可重複、有索引,可以根據索引獲取值,Set的元素存取無序、不能儲存重複元素。
List下面有有三個實現類ArrayList、vector、LinkedList,ArrayList和Vector底層是陣列,LinkedList底層實現是連結串列。
ArrayList和Vector底層是陣列實現,能夠根據索引直接獲取值,所以查詢快,但是刪除和新增操作慢,因為需要向前或向後挪動多個元素。Vector是舊版本的,執行緒安全,所以如果是單執行緒進行存取,最好用ArrayList,效率快,如果是多執行緒我們最好用Vector來進行儲存,因為Vector裡面的方法是執行緒安全的。
LinkedList是基於連結串列實現的,查詢必須從頭開始,所以查詢速度慢,但是刪除和新增只需要挪動兩個節點,所以刪除和新增速度快。連結串列提供了特殊的方法,所以連結串列可以實現棧或者佇列。
Set介面有三個實現類HashSet、LinkedHashSet、TreeSet
HashSet集合儲存不重複,無序,原理是什麼?因為HashSet底層實現是雜湊表,雜湊表通過hashCode()和equals()方法來共同保證元素不重複。首先根據儲存的元素算出hashCode值,然後根據算出的hashCode值和陣列的長度算出儲存的下標;如果下標位置無元素,那麼直接儲存,如果有元素,那麼就要使用存入的元素和已經存在的元素進行equals方法進行比較,如果結果為真就不儲存,如果為假就進行儲存,以連結串列方式進行儲存。
注意:一般我們自定義的類都需要重新寫hashCode()和equals(),必須要重寫Object類的這兩個方法,因為hash值是根據儲存的元素獲得的
如:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
LinkedHashSet基於連結串列和雜湊表實現的,所以具有存取有序,元素唯一的特點。
package cn.yqg.day4;
public class Person {
private int age ;
private String name;
public Person(int age,String name) {
this.age=age; this.name=name; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
package cn.yqg.day4;
import java.util.Iterator;
import java.util.LinkedHashSet;
public class Test {
public static void main(String[] args) {
LinkedHashSet<Person> list=new LinkedHashSet<>();
list.add(new Person(1,"12"));
list.add(new Person(1,"12")); list.add(new Person(3,"14")); list.add(new Person(4,"15")); Iterator<Person> it=list.iterator(); while(it.hasNext()) { Person p=it.next(); System.out.println(p); } } }
執行結果:
Person [age=1, name=12]
Person [age=3, name=14]
Person [age=4, name=15]
TreeSet:特點存取無序,元素唯一,可以進行排序;TreeSet是基於二叉樹實現。
儲存過程:如果是第一個元素,直接存入,作為根節點,下一個元素進來就會進行比較,如果大於加點就放在節點右邊,如果小於節點就放在節點左邊,等於節點就不儲存,後面的元素會依次進行比較直到有儲存的位置為止。
package cn.yqg.day4;
import java.util.TreeSet;
public class Test2 {
public static void main(String[] args) {
TreeSet<String> set=new TreeSet<>();
set.add("abd");
set.add("abc"); set.add("bcd"); set.add("bce"); set.add("bce"); for(String str : set) { System.out.println(str); } } }
執行結果:
abc
abd
bcd
bce
TreeSet保證元素唯一有兩種方式:
1.自定義物件實現Comparable()介面,重寫CompareTo()方法,該方法返回0表示相等,大於0表示存入的元素比被比較的元素大。反之小於0。
2.在建立TreeSet的時候向構造器中傳入比較器Comparator介面實現類的物件,實現Comparator介面重寫compare方法。
如果向TreeSet中儲存自定義類沒實現Comparable介面,或者沒有傳入Comparator比較器時,會出現ClassCastException異常。
下面演示用兩種方式儲存自定義物件
package cn.yqg.day4;
import java.util.TreeSet;
public class Test4 {
public static void main(String[] args) {
TreeSet<Person> treeSet=new TreeSet<>();
treeSet.add(new Person(4,"1"));
treeSet.add(new Person(1,"張三")); treeSet.add(new Person(1,"李四")); treeSet.add(new Person(3,"大王")); treeSet.add(new Person(2,"小王")); treeSet.add(new Person(3,"大王")); treeSet.add(new Person(4,"1")); treeSet.add(new Person(4,"1")); for(Person p : treeSet) { System.out.println(p); } } }
package cn.yqg.day4;
public class Person implements Comparable<Person>{
private int age ;
private String name;
public Person(int age,String name) {
this.age=age; this.name=name; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public int compareTo(Person o) { int rusult=this.age-o.age; if(rusult==0) { return this.name.compareTo(o.name); } return rusult; } }
另一種方式:使用比較器Comparator
package cn.yqg.day4;
import java.util.Comparator;
import java.util.TreeSet;
public class Test5 {
public static void main(String[] args) {
TreeSet<Person2> treeSet2=new TreeSet<>(new Comparator<Person2>() {
@Override
public int compare(Person2 o1, Person2 o2) {
if(o1==o2) { return 0; } int result=o1.getAge()-o2.getAge(); if(result==0) { return o1.getName().compareTo(o2.getName()); } return result; } }); treeSet2.add(new Person2(1,"張三")); treeSet2.add(new Person2(5,"小龍")); treeSet2.add(new Person2(4,"3")); treeSet2.add(new Person2(5,"小慶")); treeSet2.add(new Person2(4,"1")); treeSet2.add(new Person2(1,"1")); for(Person2 p : treeSet2) { System.out.println(p); } } }
package cn.yqg.day4;
public class Person2 {
private int age ;
private String name;
public Person2(int age,String name) {
this.age=age; this.name=name; } @Override public String toString() { return "Person [age=" + age + ", name=" + name + "]"; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
執行結果:
Person [age=1, name=1]
Person [age=1, name=張三]
Person [age=4, name=1]
Person [age=4, name=3]
Person [age=5, name=小慶]
Person [age=5, name=小龍]
-------------------------------------------------------------------------------------------------------------------
Collection體系總結:
List:“特點”,存取有序,可存重複值,元素有索引。
ArrayList:陣列結構,查詢速度快,增刪慢,執行緒不安全,效率高。
Vector:陣列結構,查詢快,增刪慢,執行緒安全,效率低。
LinkedList:連結串列結構,增刪快,查詢慢,執行緒不安全,效率高。
Set:“特點”,存取無序,不可存重複值,無索引。
HashSet:雜湊表,儲存無序,元素不重複,無索引。
LinkedHashSet:連結串列加雜湊表,儲存有序,無索引,值不重複。
TreeSet:二叉樹,元素不重複,存取無序,但是可以進行排序。
兩種排序方式:
1.自然排序:我們的元素必須實現Comparable介面,實現CompareTo()方法。
2.比較器排序:我們自定義的類實現Comparator介面,比較器實現Compare方法。然後建立TreeSet的時候把比較器物件當做引數傳遞給TreeSet。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
現在我們來看看Map集合框架
Map是一個雙列集合,儲存的是鍵值對,鍵要求保持唯一性,值可以重複。鍵值一一對應。Map儲存是將鍵值傳入Entry,然後儲存Entry物件。
Map介面實現類有三個,分別為HashMap、TreeMap、LinkedHashMap。
HashMap:是基於hash表實現的,所以儲存自定義物件作為鍵時,必須重寫hashCode和equals方法。存取無序
package cn.yqg.day4;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class Test6 {
public static void main(String[] args) {
HashMap<Person,String> map=new HashMap<Person,String>();
map.put(new Person(1,"pp"),"java");
map.put(new Person(2,"kk"),"c"); map.put(new Person(1,"pp"),"c++"); map.put(new Person(3,"ll"),"java"); Set<Entry<Person,String>> entrySet=map.entrySet(); Iterator<Entry<Person,String>> it=entrySet.iterator(); while(it.hasNext()) { Entry<Person,String> entry=it.next(); System.out.println(entry.getKey()+"-----"+entry.getValue()); } } }
結果:
Person [age=1, name=pp]-----c++
Person [age=3, name=ll]-----java
Person [age=2, name=kk]-----c
我們發現如果鍵重複,後面的值會覆蓋前面的值。存取無序。
LinkedHashMap:用法基本和HashMap一致,基於連結串列和雜湊表來實現的,所以有存取有序,鍵不重複的特點。
package cn.yqg.day4;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class Test7 {
public static void main(String[] args) {
LinkedHashMap<Person,String> map=new LinkedHashMap<Person,String>();
map.put(new Person(1,"pp"),"java");
map.put(new Person(2,"kk"),"c"); map.put(new Person(1,"pp"),"c++"); map.put(new Person(1,"pp"),"R"); for(Entry<Person,String> entry : map.entrySet()) { System.out.println(entry.getKey()+"-----"+entry.getValue()); } } }
實現結果:
Person [age=1, name=pp]-----R
Person [age=2, name=kk]-----c
我們注意到鍵如果相同,值會被後面的覆蓋掉。而且存取有序。
TreeMap集合儲存自定義物件,自定義物件始終作為TreeMap的key值,由於TreeMap底層實現是二叉樹,所有存進去的資料都要進行排序,排序有兩種方法,一種自定義類實現Comparable介面,實現CompareTo方法,另一種實現Comparator介面,實現自定義比較器Compare方法。
package cn.yqg.day4;
import java.util.Comparator;
import java.util.TreeMap;
import java.util.Map.Entry;
public class Test8 {
public static void main(String[] args) {
TreeMap<Person,String> map=new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if(o1==o2) { return 0; } int result=o1.getAge()-o2.getAge(); if(result==0) { result=o1.getName().compareTo(o2.getName()); } return result; } }); map.put(new Person(1,"pp"),"java"); map.put(new Person(2,"kk"),"c"); map.put(new Person(6,"pp"),"c++"); map.put(new Person(0,"pp"),"R"); map.put(new Person(-7,"pp"),"jsp"); map.put(new Person(0,"pp"),"js"); for(Entry<Person,String> entry : map.entrySet()) { System.out.println(entry.getKey()+"-----"+entry.getValue()); } } }
執行結果:
Person [age=-7, name=pp]-----jsp Person [age=0, name=pp]-----js Person [age=1, name=pp]-----java Person [age=2, name=kk]-----c Person [age=6, name=pp]-----c++