標籤:區別 each title contains type article 代碼 array string
基礎知識:
什麼是集合?
集合是一個容器。把多個對象放入容器中。有一個水杯,你可以選擇把水不斷往裡裝,也可以選擇裝牛奶。但是不能兩種不同的東西混合裝一個杯子。集合這個容器裡裝的一定是同一類型的東西。(參考型別,不能是基本類型)
看到這個介紹,我們可能想到數組,數組要求的也是裡面必須存放的是一種資料類型的結構。
但數組和集合的區別呢?
數組大小是固定的,集合的大小理論上是不限定。
數組裡的元素可以是基本類型,也可以是參考型別。集合只能放參考型別。
是集合家族的主要成員們(圖來自百度)
由圖可見,Collection介面和Map介面是兩個老大。Collection介面下面又生出來了Set介面(無序),List介面(有序),queue介面。Map介面儲存的是有映射關係的資料。Map裡的子類都有一個共同的特徵就是裡面資料都是key-value.舉例,語文-80,數學-78,科目是不能重複的,分數是可以重複的,所以,Map裡的key不能重複,value可重複。需要查分數(value),就通過科目(Key)來取。
具體實作類別,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等等,主要分為以下三類
從可知,Set集合是無序的,只能根據集合裡的元素本身訪問。
List集合是有序的,可以通過索引訪問。
Map集合可通過每個元素的Key訪問value.
下面列出了Collection介面下方法
大概看一眼,這些方法無非就是添加對象,移除對象,判斷集合是不是空,清空容器,所以無需記憶。
有方法值的一提的是 當你需要把集合元素轉成數組元素時候用Object[] toArray() ,有個陷阱:注意注釋部分,數組不能直接(String[])這樣強制將陣列變數轉換,只有在使用使將元素轉換為String,
首先看下List的這兩個方法的說明:
Object[ ] toArray() :返回按適當順序包含列表中的所有元素的數組(從第一個元素到最後一個元素)。
<T> T[ ] toArray(T[] a) :返回按適當順序(從第一個元素到最後一個元素)包含列表中所有元素的數組;返回數組的運行時類型是指定數組的運行時類型
Collection<String> arr = new ArrayList<String>(); arr.add("a"); arr.add("b"); arr.add("b");//可以添加重複的對象 // String[] str = (String[])arr.toArray();//error
Object[] obj = arr.toArray();//第一種方法
String[] obj1 = arr.toArray(new String[3]);//第二種方法
Db.query()第二個是多個不確定的參數,多個參數可以被作為數組傳進來。List集合轉數組舉例
/** * 封裝預先處理參數解析並執行查詢 * @param sqlId * @param param * @return */ public <T> List<T> query(String sqlId, Map<String, Object> param){ LinkedList<Object> paramValue = new LinkedList<Object>(); String sql = getSqlByBeetl(sqlId, param, paramValue); return Db.query(sql, paramValue.toArray()); }
補充下getSqlByBeetl如果要sql裡需要Map的話,如下參考:
/** * 驗證編碼是否存在 * @param operatorid * @param type * @return boolean * 描述:新增角色群組時operatorid為空白,修改角色群組時operatorid傳值 */ public boolean valiQbgjxwcqkxxno(String qbgjxwcqkxxno, String type){ Map<String, Object> param = new HashMap<String, Object>(); param.put("column", Jxwc.column_qbggyzfqkxxno); param.put("table", Jxwc.table_name); String sql = getSqlByBeetl(Jxwc.sqlId_select, param); List<Jxwc> list = Jxwc.dao.find(sql,qbgjxwcqkxxno); int size = list.size(); if("add".equals(type)){ if(size == 0){ return true; } }else{ if(size <= 1){ return true; } } return false; }
Collection介面是
List,Set,Queue介面的父介面,所以Collection圖方法都能操作下這三種集合。 Itertor介面
看集合主要成員圖可知Itertor介面不在圖上,但是他也是集合架構的成員,但是它與Map集合系列,C
ollection集合系列不同,它主要裝的是遍曆Collection集合裡的元素。Itertor對象也叫迭代器,依託Collection對象存在。提供遍曆Collection的統一編程介面。
主要的方法:
boolean hasNext() 要是被遍曆的集合還沒遍曆完,就返回true
Object next() 返回集合裡的下一個元素
void remove() 刪除上一次next()返回的元素
package Test01; import java.util.Collection;import java.util.HashSet;import java.util.Iterator; public class Test { public static void main(String[] args) { Collection arr = new HashSet<>(); arr.add("a"); arr.add("b"); arr.add("C");//可以添加重複的對象 Iterator iterator =arr.iterator(); while(iterator.hasNext()) { String next = (String) iterator.next(); System.out.println(next); if(next.equals("C")) { iterator.remove(); } next ="修改迭代變數的值看看有沒影響"; //注意下 } System.out.println(arr.toString()); }}
運行完發現“注意下”的地方本想改變集合裡的元素卻沒變。所以可得到:Iterator並不是得到集合本身的元素,而是得到元素的值而已,所以修改迭代變數的值並不會影響集合本身。
Iterator不像其他集合,沒有承裝對象的能力。如果他不依託集合存在,根本沒存在的價值。
想刪除集合元素,必須通過Iterator的remove() 刪除上一次next()返回的元素,不能集合自己remove(Object)
舉例,會發現異常
package Test01; import java.util.Collection;import java.util.HashSet;import java.util.Iterator; public class Test { public static void main(String[] args) { Collection arr = new HashSet<>(); arr.add("a"); arr.add("b"); arr.add("C");//可以添加重複的對象 Iterator iterator =arr.iterator(); while(iterator.hasNext()) { String next = (String) iterator.next(); System.out.println(next); if(next.equals("b")) { arr.remove(next); } next ="修改迭代變數的值看看有沒影響"; } System.out.println(arr.toString()); }}
異常原因是 迭代器採用快速-失敗原則(fast-fail),一旦迭代過程中發現Collection集合中元素被修改,就引發異常。偶爾發現,如果剛剛代碼改成
if(next.equals("C")) { arr.remove(next); }
也不會異常,----只有刪除特定元素才會這樣,但是不該冒險去做。
foreach也能迭代訪問集合,但是注意,他得到的也不是集合元素本身,系統只是把集合元素的值賦給迭代變數而已,也同樣同上會引發Java ConcurrentModificationException異常。
如下代碼(錯誤示範)
package Test01; import java.util.Collection;import java.util.HashSet;import java.util.Iterator; public class Test { public static void main(String[] args) { Collection arr = new HashSet<>(); arr.add("a"); arr.add("b"); arr.add("C");//可以添加重複的對象 for(Object a:arr) { if(a.equals("b")) { arr.remove(a); } } } }
Set集合
就像把對象隨意扔進罐子裡,無法記住元素的添加順序。Set某種程度就是Collection,方法沒有不同,只是行為稍微不同,(不允許重複元素),如果一定要往裡加兩個相同元素,添加失敗add()返回false;
上面的Set的一些共同點,Hashset,TreeSet,EunmSet三個實作類別還各有特色。
依次介紹下
Hashset
判斷Hashset 集合裡的兩個對象相等,過兩關,equal()比較相等,對象的hashcode()也相等
為什麼還得比較對象的hashcode()?
Hashset 集合收進一個對象時,會調用對象的hashcode()得到其Hashcode值來決定他的儲存位置。所以,即使是equal()比較相等的兩個對象,hashcode不同,存放在hashset裡的位置不同,依然能把這兩個對象添加成功。
注意:把對象裝進hashset時,如果要重寫equals方法,也得重寫hashcode 方法,因為equals()相等的兩對象hashcode 也是相同的。
提問:hashcode()對hashset是很重要的嗎?
答:hash演算法是快速尋找被檢索的對象。通過對象的hashcode定位集合裡的對象的儲存位置。定位該元素。對比下,數組是儲存一組元素最快的數組結構,數組通過索引找到它的組員,通過索引能計算元素在記憶體裡的儲存位置。
但是為嘛有了數組,還用hashset呢?數組也有局限性,索引是連續的,而且長度不可變。
hashset有了hashcode,所以能快速定位對象位置,而且任意增加對象。
重寫hashcode() 注意java.lang.Object中對hashCode的約定:
兩個對象通過equals()比較相等時,他們的hashcode 也應該是一樣的。
程式運行過程中,同一個對象多次調用hashcode方法返回應該是一樣的。
如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼在兩個對象中的任一對象上調用 hashCode 方法不一定會產生不同的整數結果。但是,為不相等的對象產生不同整數結果可以提高雜湊表的效能。 實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。
向hashset裡添加了一個可變對象後時,要注意:如果後面的程式修改了這個可變對象的執行個體變數時,可能會導致他與集合裡的其他元素相同,即兩個對象equals返回true,hashcode也相同。導致hashSet不能正確操作那些元素。
補充瞭解下,可變對象:建立後,對象的屬性值可能會變,也就是說,建立後對象的hash值可能會改變。
舉例:對象MutableKey的鍵在建立時變數 i=10 j=20,雜湊值是1291。然後我們改變執行個體的變數值,該對象的鍵 i 和 j 從10和20分別改變成30和40。現在Key的雜湊值已經變成1931。顯然,這個對象的鍵在建立後發生了改變。所以類MutableKey是可變的。
下面代碼是hashset裡添加了一個可變對象例子,
可看出,hashset已經添加了幾個成員後,修改一個成員的執行個體變數,會得到裡面有相同的成員,因此是不對的。
但是,對最後一行,不能準確訪問成員這個。有點疑問,待解決。
package Test01;import java.util.HashSet;import java.util.Iterator;class mutClass{ public int count; public mutClass(int count) { this.count =count; } public boolean equals(Object obj) { if(this == obj) { return true; } if(obj != null && obj.getClass() == mutClass.class) { mutClass m =(mutClass) obj; return this.count == m.count; } return false; } public int hashcode() { return this.count; } public String toString() { return "試試mutClass[count=" + count + "]"; } }public class TestHashSet { @SuppressWarnings("unchecked")public static void main(String[] args){ HashSet testHashSet =new HashSet(); mutClass a = new mutClass(3); mutClass b = new mutClass(1); mutClass c = new mutClass(-9); mutClass d = new mutClass(9); testHashSet.add(a); testHashSet.add(b); testHashSet.add(c); testHashSet.add(d); System.out.println("第一次"+testHashSet); Iterator iterator =testHashSet.iterator(); mutClass first = (mutClass) iterator.next(); first.count=9; /* testHashSet.remove(new mutClass(3)); testHashSet.remove(b); //與上一行的區別*/ System.out.println("第二次"+testHashSet); System.out.println(new mutClass(-9) == new mutClass(-9)); System.out.println("第四次"+testHashSet.contains(new mutClass(-9))); }}
hashset不能保證新增成員的順序,和自己的順序是一樣的,但是引入了一個LinkedHashSet子類,使得它能和hashset一樣,靠hashcode 找到他的儲存位置,又能維護新增成員的順序,內部靠一個鏈表實現,迭代訪問集合時有很好的效能。
Java集合(上)