java集合(List,Set,Map)詳細總結
集合的由來:
數組是長度是固定的,當添加的元素超過數組的長度時需要對數組重新定義,太麻煩了,java內部給我們提供了集合類,能存儲任意對象,長度是可以改變的,隨著元素的增加而增加,隨著元素的減少而減少。
數組與集合的區別:
數組既可以存儲引用數組類型,又可以存儲引用數據類型,基本數據類型存儲的是值,引用數據類型存儲的是地值。 集合只能存儲引用數據類型(對象),集合中也可以存儲基本數據類型,但是在存儲的時候會自動裝箱變成對象。
集合的結構:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口介紹:
Collection是最基本的集合接口,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子接口”如List和Set。
所有實現Collection接口的類都必須提供兩個標準的構造函數:無參數的構造函數用於創建一個空的Collection,有一個Collection參數的構造函數用於創建一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個構造函數允許用戶復制一個Collection。
如何遍歷Collection中的每一個元素?不論Collection的實際類型如何,它都支持一個iterator()的方法,該方法返回一個叠代子,使用該叠代子即可逐一訪問Collection中每一個元素。典型的用法如下:
1Iterator it = collection.iterator(); // 獲得一個叠代子 2 while(it.hasNext()) { 3 Object obj = it.next(); // 得到下一個元素 4 }
叠代器原理:
叠代器是對集合進行遍歷,而每一個集合內部的存儲結構都是不同的,所以每一個集合存和取都是不一樣,那麽就需要在每一個類中定義hasNext()和next()方法,這樣做是可以的,但是會讓整個集合體系過於臃腫,叠代器是將這樣的方法向上抽取出接口,然後在每個類的內部,定義自己叠代方式,這樣做的好處有二,第一規定了整個集合體系的遍歷方式都是hasNext()和next()方法,第二,代碼有底層內部實現,使用者不用管怎麽實現的,會用即可
List介紹:
List是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List中的位置,類似於數組下標)來訪問List中的元素,這類似於Java的數組。
和下面要提到的Set不同,List允許有相同的元素。
除了具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator接口,和標準的Iterator接口相比,ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向後遍歷。
實現List接口的常用類有LinkedList,ArrayList,Vector和Stack。
1 //List數據遍歷,以及基本操作 2 List list =new ArrayList(); 3 list.add("a"); 4 list.add("b"); 5 list.add("c"); 6 list.add("d"); 7 list.set(2, "a"); //根據索引,往2索引Set一個值 8 System.out.println(list.get(2)); //根據索引獲取結果 9 Object remove = list.remove(100); //刪除索引,將刪除的元素返回 10 for (int i = 0; i < list.size(); i++) { 11 System.out.println(list.get(i)); //獲取索引中的值 12 } 13 }
LinkedList介紹:
LinkedList實現了List接口,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可 被用作堆棧(stack),隊列(queue)或雙向隊列(deque)。
註意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
1 //LinkedList的基本使用 2 public static void main(String[] args) { 3 LinkedList list = new LinkedList(); 4 list.addFirst("a"); 5 list.addFirst("b"); 6 list.addFirst("c"); //添加數據先進先出的模式 7 list.addFirst("d"); 8 list.addLast("e"); //往尾部添加數據 9 // System.out.println(list); //查看全部數據 10 System.out.println(list.getFirst()); //查看頭部數據 11 System.out.println(list.getLast()); //查看尾部數據 12 System.out.println(list.removeFirst()); //刪除頭部添加的數據 13 System.out.println(list.removeLast()); //刪除尾部添加的數據 14 System.out.println(list.get(1)); 15 System.out.println(list);
ArrayList介紹:
ArrayList實現了可變大小的數組。
它允許所有元素,包括null。ArrayList沒有同步。
size,isEmpty,get,set方法運行時間為常數。但是add方法開銷為分攤的常數,添加n個元素需要O(n)的時間。其他的方法運行時間為線性。
每個ArrayList實例都有一個容量(Capacity),即用於存儲元素的數組的大小。這個容量可隨著不斷添加新元素而自動增加,但是增長算法並沒有定義。 當需要插入大量元素時,在插入前可以調用ensureCapacity方法來增加ArrayList的容量以提高插入效率。
和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。
1 public static void main(String[] args) { 2 ArrayList list=new ArrayList(); 3 list.add(new Student("jy",17)); //數據添加 4 list.add(new Student("jy",17)); 5 list.add(new Student("jj",171)); 6 list.add(new Student("jj",171)); 7 //list.add(5, new Student("jj",171));// 在集合的某一個位置添加對象 8 // list.addAll(2, list2); // 在集合的某一個位置添加一個集合 9 // list.remove(2); //根據索引刪除 10 //list.set(2, new Student("jj",171)); //修改 11 //Object[] array = list.toArray(); //轉數組 12 //使用叠代器 13 Iterator it=list.iterator(); 14 it=list.iterator(); 15 //遍歷集合 16 while (it.hasNext()) { 17 Student type = (Student) it.next(); 18 System.out.println("name:"+ type.getName() + "---> Age:" + type.getAge()); 19 } 20 }
Vector介紹:
Vector 是矢量隊列,它是JDK1.0版本添加的類。繼承於AbstractList,實現了List, RandomAccess, Cloneable這些接口。
Vector 繼承了AbstractList,實現了List;所以,它是一個隊列,支持相關的添加、刪除、修改、遍歷等功能。
Vector 實現了RandmoAccess接口,即提供了隨機訪問功能。RandmoAccess是java中用來被List實現,為List提供快速訪問功能的。在Vector中,我們即可以通過元素的序號快速獲取元素對象;這就是快速隨機訪問。
Vector 實現了Cloneable接口,即實現clone()函數。它能被克隆。
和ArrayList不同,Vector中的操作是線程安全的。(Vector功能都不及ArrayList與LinkedList好用,了解就好)
1 public static void main(String[] args) { 2 Vector v = new Vector(); 3 v.addElement("1"); 4 v.addElement("11"); 5 v.addElement("111"); 6 v.addElement("1111"); 7 v.addElement("11111"); 8 Enumeration en =v.elements(); //獲取枚舉,遍歷 9 while(en.hasMoreElements()){ //判斷集合中是否有元素 10 System.out.println(en.nextElement()); //獲取集合中的元素 11 } 12 }
總結:
ArrayList:
底層數據結構是數組,查詢快,增刪慢。
線程不安全,效率高。
Vector:
底層數據結構是數組,查詢快,增刪慢。
線程安全,效率低。
Vector相對ArrayList查詢慢(線程安全的)
Vector相對LinkedList增刪慢(數組結構)
LinkedList:
底層數據結構是鏈表,查詢慢,增刪快。
線程不安全,效率高。
Vector和ArrayList的區別:
Vector是線程安全的,效率低
ArrayList是線程不安全的,效率高
共同點:都是數組實現的
ArrayList和LinkedList的區別:
ArrayList底層是數組結果,查詢和修改快
LinkedList底層是鏈表結構的,增和刪比較快,查詢和修改比較慢
List有三個兒子,我們到底使用誰呢?
查詢多用ArrayList
增刪多用LinkedList
如果都多ArrayList
Set介紹:
父接口 Collection
2.特點 唯一 無序(插入順序跟遍歷順序不一致,沒下標)
唯一:Set集合中不允許出現重復的元素,如果向Set集合中存儲重復的元素是無效的,但不會報錯
無序:Set集合不會維護集合中元素的插入順序,即不存在下標或索引
3.Set接口中的功能方法 (Set接口中沒有自有方法,全部繼承自Collection接口)
4.凡是帶有下標的方法都不支持
HashSet介紹:
我們使用Set集合都是需要去掉重復元素的, 如果在存儲的時候逐個equals()比較, 效率較低,哈希算法提高了去重復的效率, 降低了使用equals()方法的次數
當HashSet調用add()方法存儲對象的時候, 先調用對象的hashCode()方法得到一個哈希值, 然後在集合中查找是否有哈希值相同的對象
如果沒有哈希值相同的對象就直接存入集合
如果有哈希值相同的對象, 就和哈希值相同的對象逐個進行equals()比較,比較結果為false就存入, true則不存
1 HashSet<String> hs = new HashSet<String>(); //創建HashSet對象 2 boolean b1 = hs.add("a"); 3 boolean b2 = hs.add("a"); //當向set集合中存儲重復元素的時候返回為false 4 hs.add("a"); 5 hs.add("b"); 6 hs.add("c"); 7 hs.add("d"); //HashSet的繼承體系中有重寫toString的方法 8 System.out.println(hs); 9 System.out.println(b1); 10 System.out.println(b2); 11 for (String string : hs) { //只要用叠代器的,就可以使用增強for循環遍歷 12 System.out.println(string); 13 } 14 }
LinkedHashSet介紹:
底層是鏈表實現的,時Set集合唯一一個能保證怎麽存就怎麽取的集合對象
因為HashSet的子類,所以也是保證元素的唯一的,與HashSet的原理一樣
1 public static void main(String[] args) { 2 LinkedHashSet<String> lhs = new LinkedHashSet<String>(); 3 lhs.add("a"); 4 lhs.add("aa"); 5 lhs.add("aaa"); 6 lhs.add("aaa"); 7 lhs.add("a"); 8 System.out.println(lhs); 9 }
TreeSet介紹:
TreeSet存儲Integer類型的元素並遍歷
當compareTo方法返回()的時候集合中只有一個元素
當compareTo方法返回正數的時候集合回怎麽存就怎麽取
當compareTo方法返回符數的時候集合回倒序存儲
1 public static void main(String[] args) { 2 //按照字符串長度進行排序 3 TreeSet<String> ts = new TreeSet<String>(new ComareByLen()); 4 ts.add("aaa"); //Comparator c = new Comparator(); 5 ts.add("aa"); 6 ts.add("aaaa"); 7 ts.add("aaaaa"); 8 System.out.println(ts); 9 } 10 class ComareByLen implements Comparator<String>{ 11 12 @Override 13 public int compare(String o1, String o2) { 14 int num = o1.length() - o2.length(); //定義一個比較器 15 return num == 0 ? o1.compareTo(o2):num; 16 } 17 18 }
總結:
Set:存取無序,無所索引,不可以重復
HashSet:
底層是哈希算法實現
LinkedHashset:
底層是鏈表實現的,但是也是可以保證元素唯一,和Hashset原理一樣
TreeSet:
底層是二叉樹算法實現
一般在開發中的時候不需要對存儲元素進行排序,所以開發的時候大多HashSet,HaShSet的效率比較高
TreeSet在面試的時候問的比較多,問你集中排序方式,和集中排序方式的區別
並發修改異常:
1.在遍歷一個集合時,如果需要刪除一個數據,那麽會容易出現並發修改異常
2.誰遍歷,就讓誰刪除(例如在遍歷Set的時候不允許刪除Set(叠代器是單獨一個線程,涉及到數據同步的問題),可以通過叠代器的remove方法來解決)
3.就必須使用原始的叠代器代碼
Map介紹:
|--Hashtable:底層是哈希表數據結構,是線程同步的。不可以存儲null鍵,null值。
|--HashMap:底層是哈希表數據結構,是線程不同步的。可以存儲null鍵,null值。替代了Hashtable.
|--TreeMap:底層是二叉樹結構,可以對map集合中的鍵進行指定順序的排序。
Map集合存儲和Collection有著很大不同:
Collection一次存一個元素;Map一次存一對元素。
Collection是單列集合;Map是雙列集合。
Map中的存儲的一對元素:一個是鍵,一個是值,鍵與值之間有對應(映射)關系。
特點:要保證map集合中鍵的唯一性。
1,添加。
put(key,value):當存儲的鍵相同時,新的值會替換老的值,並將老值返回。如果鍵沒有重復,返回null。
void putAll(Map);
2,刪除。
void clear():清空
value remove(key) :刪除指定鍵。
3,判斷。
boolean isEmpty():
boolean containsKey(key):是否包含key
boolean containsValue(value) :是否包含value
4,取出。
int size():返回長度
value get(key) :通過指定鍵獲取對應的值。如果返回null,可以判斷該鍵不存在。當然有特殊情況,就是在hashmap集合中,是可以存儲null鍵null值的。
Collection values():獲取map集合中的所有的值。
5,想要獲取map中的所有元素:
原理:map中是沒有叠代器的,collection具備叠代器,只要將map集合轉成Set集合,可以使用叠代器了。之所以轉成set,是因為map集合具備著鍵的唯一性,其實set集合就來自於map,set集合底層其實用的就是map的方法。
★ 把map集合轉成set的方法:
Set keySet();
Set entrySet();//取的是鍵和值的映射關系。
Entry就是Map接口中的內部接口;
為什麽要定義在map內部呢?entry是訪問鍵值關系的入口,是map的入口,訪問的是map中的鍵值對。
---------------------------------------------------------
取出map集合中所有元素的方式一:keySet()方法。
可以將map集合中的鍵都取出存放到set集合中。對set集合進行叠代。叠代完成,再通過get方法對獲取到的鍵進行值的獲取。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = map.get(key);
System.out.println(key+":"+value);
}
--------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法。
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator();
while(it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+"::::"+me.getValue());
}
--------------------------------------------------------
使用集合的技巧:
看到Array就是數組結構,有角標,查詢速度很快。
看到link就是鏈表結構:增刪速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();
看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到該結構的中的元素必須覆蓋hashCode,equals方法。
看到tree就是二叉樹,就要想到排序,就想要用到比較。
比較的兩種方式:
一個是Comparable:覆蓋compareTo方法;
一個是Comparator:覆蓋compare方法。
LinkedHashSet,LinkedHashMap:這兩個集合可以保證哈希表有存入順序和取出順序一致,保證哈希表有序。
集合什麽時候用?
當存儲的是一個元素時,就用Collection。當存儲對象之間存在著映射關系時,就使用Map集合。
保證唯一,就用Set。不保證唯一,就用List。
------------------------------------------------------------------------------------------------
Collections:它的出現給集合操作提供了更多的功能。這個類不需要創建對象,內部提供的都是靜態方法。
靜態方法:
Collections.sort(list);//list集合進行元素的自然順序排序。
Collections.sort(list,new ComparatorByLen());//按指定的比較器方法排序。
class ComparatorByLen implements Comparator<String>{
public int compare(String s1,String s2){
int temp = s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
}
}
Collections.max(list); //返回list中字典順序最大的元素。
int index = Collections.binarySearch(list,"zz");//二分查找,返回角標。
Collections.reverseOrder();//逆向反轉排序。
Collections.shuffle(list);//隨機對list中的元素進行位置的置換。
將非同步集合轉成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定義一個類,將集合所有的方法加同一把鎖後返回。
Collection 和 Collections的區別:
Collections是個java.util下的類,是針對集合類的一個工具類,提供一系列靜態方法,實現對集合的查找、排序、替換、線程安全化(將非同步的集合轉換成同步的)等操作。
Collection是個java.util下的接口,它是各種集合結構的父接口,繼承於它的接口主要有Set和List,提供了關於集合的一些操作,如插入、刪除、判斷一個元素是否其成員、遍歷等。
-------------------------------------------------------
Arrays:
用於操作數組對象的工具類,裏面都是靜態方法。
asList方法:將數組轉換成list集合。
String[] arr = {"abc","kk","qq"};
List<String> list = Arrays.asList(arr);//將arr數組轉成list集合。
將數組轉換成集合,有什麽好處呢?用aslist方法,將數組變成集合;
可以通過list集合中的方法來操作數組中的元素:isEmpty()、contains、indexOf、set;
註意(局限性):數組是固定長度,不可以使用集合對象增加或者刪除等,會改變數組長度的功能方法。比如add、remove、clear。(會報不支持操作異常UnsupportedOperationException);
如果數組中存儲的引用數據類型,直接作為集合的元素可以直接用集合方法操作。
如果數組中存儲的是基本數據類型,asList會將數組實體作為集合元素存在。
集合變數組:用的是Collection接口中的方法:toArray();
如果給toArray傳遞的指定類型的數據長度小於了集合的size,那麽toArray方法,會自定再創建一個該類型的數據,長度為集合的size。
如果傳遞的指定的類型的數組的長度大於了集合的size,那麽toArray方法,就不會創建新數組,直接使用該數組即可,並將集合中的元素存儲到數組中,其他為存儲元素的位置默認值null。
所以,在傳遞指定類型數組時,最好的方式就是指定的長度和size相等的數組。
將集合變成數組後有什麽好處?
限定了對集合中的元素進行增刪操作,只要獲取這些元素即可。
java集合(List,Set,Map)詳細總結