本文2個方向java
先看下面的代碼算法
//鍵值對 Map map = new HashMap(); map.put("name","hong"); //不可重複的數組 Set set = new HashSet(); set.add("hong"); //可重複數組 List<String> list = new ArrayList<>(); list.add("hong"); 複製代碼
上面建立了3種不一樣的集合類,鍵值對,不可重複數組以及可重複數組。發現一個規律是左右2邊不一樣 Map
HashMap
、Set
HashSet
、List
ArrayList
那麼能夠看看左邊到底是什麼:數組
Map的結構
public interface Map<K,V> 是一個接口 其中定義了各類方法
Set的結構
public interface Set<E> extends Collection<E>
List的結構
public interface List<E> extends Collection<E>
public interface Collection<E> extends Iterable<E>
複製代碼
那麼這個類集的父接口就找到了就是Collection<E>
,全部類集的核心方法都在裏面了安全
咱們來看看這個接口裏提供了什麼方法:bash
int size(); 返回調用集合中元素的個數 boolean isEmpty(); 若是調用集合是空的就返回true不然返回false boolean contains(Object o); 若是o是調用集合的一個元素,就返回true,不然返回false Iterator<E> iterator();返回調用集合的迭代器 Object[] toArray();返回一個數組,這個數組包含了全部儲存在這個調用集合中的元素,做爲一個備份 <T> T[] toArray(T[] a);返回一個數組,該數組僅僅包含了那些類型與數組元素類型匹配的類集元素。 boolean add(E e); 將E類型的數據添加到集合中,返回true,若是不容許有重複的元素且包含了e就返回false boolean remove(Object o);從調用集合中刪除o,若是被刪除了就返回true不然返回false boolean containsAll(Collection<?> c);若是調用集合中包含了c中的全部元素就返回true不然返回false boolean addAll(Collection<? extends E> c);將c中的元素都加入到調用類集中,若是操做成功了就返回true不然false boolean removeAll(Collection<?> c); 從調用集合中刪除c中全部元素 boolean retainAll(Collection<?> c); 刪除調用集合中除了包含在c中的元素以外的所有元素,若是類集被改變了就返回true void clear();從調用類集中刪除全部元素 boolean equals(Object o);若是調用集合和o相等就返回true int hashCode();返回調用集合的hash值 複製代碼
對於這個接口,它是不會被直接使用的,而是被其子接口使用像:list / setmarkdown
知道了整個集合框架的繼承關係後,咱們來研究下,實現一個集合類要哪些部分呢? 咱們從他們的子接口出發去探究下。數據結構
List接口與Collection接口的不一樣點 增長方法: void add(int index, E element); 將element插入到調用列表中,插入位置的下標由index傳遞。任何已存在的,在插入點以及插入點以後的元素都將後移。沒有元素會被覆蓋 boolean addAll(int index, Collection<? extends E> c);將c中的全部元素插入到調用列表中,插入位置的下標由index傳遞 E remove(int index);刪除調用列表中index位置的元素並返回刪除的元素。刪除,被刪除元素後面的元素下標減1 int indexOf(Object o); 返回調用列表中o第一次出現的下標,若是不是列表中的元素就返回-1 int lastIndexOf(Object o);返回調用列表中obj最後一次出現的下標 E get(int index); 返回存儲在調用類集內指定下邊的對象 E set(int index, E element);用element對調用列表內由index之指定的位置進行賦值 List<E> subList(int fromIndex, int toIndex);返回一個列表,該表包括了調用列表中從start到end-1的元素。 特性:使用一個基於0的下標,元素能夠經過他們在列表中的位置被插入和訪問 複製代碼
Set<E>接口與Collection接口的不一樣點 增長方法:沒有,徹底集成了Collection接口 特性:類集中元素不容許重複,即在set中不能出現徹底相同的2個元素。 複製代碼
Map<K,V>接口 int size();返回映射中關鍵字/值對的個數 boolean isEmpty();若是調用映射是空的,則返回true boolean containsKey(Object key);若是調用映射中包含了做爲關鍵字的k,則返回true boolean containsValue(Object value);若是映射中包含了做爲值的v,則返回true V get(Object key);返回與關鍵字k相關聯的值 V put(K key, V value);將一個輸入加入調用映射,改寫原先與該關鍵字相關聯的值。 V remove(Object key);刪除關鍵字等於k的輸入 void putAll(Map<? extends K, ? extends V> m);將全部來自m的輸入加入到調用映射 void clear();從調用映射中刪除全部的關鍵字/值對 Set<K> keySet();返回一個包含調用映射中關鍵字的集合。 Collection<V> values(); Set<Map.Entry<K, V>> entrySet();將Map集合變爲Set集合返回 boolean equals(Object o);若是o是一個Map幷包含相同的輸入,則返回true int hashCode();返回調用映射的散列碼 interface Entry<K,V> 描述映射中的元素(關鍵字、值對) K getKey();返回該映射項的關鍵字 V getValue();返回該映射項的值 V setValue(V value);將這個映射輸入的值賦給v boolean equals(Object o);若是o是一個關鍵字的值都與調用對象相等的Map.Entry則返回true int hashCode();返回該映射項的散列值 特性:存儲鍵值對,key不可重複而value能夠 複製代碼
經過上面接口以及各個實現的觀察發現,一個集合類有3個部分組成:框架
對於數組,可使用以下方法進行遍歷:函數
List<String> names = new ArrayList<>(); names.add("趙"); names.add("錢"); names.add("孫"); names.add("李"); System.out.println("方式一"); //for循環依據索引來遍歷對象,因此在隨機訪問中比較快(好比ArrayList) for (int i = 0; i < names.size() ; i++) { System.out.println(names.get(i)); } System.out.println("方式一"); for (String name:names) { System.out.println(name); } 方式一和二是經過訪問代碼的方式從集合自己獲取數據,不一樣的集合對應不一樣的結構,因此這樣不能複用。因此纔有了下面的迭代器用同一種邏輯來遍歷集合,把訪問邏輯從不一樣類型的集合類中抽取出來,從而避免向外部暴露集合的內部結構 複製代碼
使用迭代器是怎麼樣的呢優化
System.out.println("方式三"); //迭代器的next()採用的是順序訪問方法,因此在順序訪問的集合中速度更快(好比LinkedList) Iterator iterator = names.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } Map map = new HashMap<String,Integer>(); map.put("趙",22); map.put("錢",33); map.put("孫",10); map.put("李",40); Iterator iterator2 = map.entrySet().iterator(); while (iterator2.hasNext()){ Map.Entry entry = (Map.Entry) iterator2.next(); System.out.println("Key : "+entry.getKey()); System.out.println("Value : "+entry.getValue()); } 複製代碼
那麼迭代器是什麼呢,先看其接口
public interface Collection<E> extends Iterable<E> 表示實現了Collection接口的類都是可迭代的 Iterable<E>迭代器接口有 Iterator<T> iterator();返回一個迭代器 default void forEach(Consumer<? super T> action) default Spliterator<T> spliterator() Iterator 迭代器接口 boolean hasNext(); 判斷是否有下一個元素,若是存在更多元素就返回true E next();取得下一個元素 default void remove()刪除當前元素 複製代碼
迭代的流程是什麼呢:
Iterator遍歷時不能夠刪除集合中的元素 對集合進行了add、remove操做就會出現ConcurrentModificationException異常。 public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("趙"); names.add("錢"); names.add("孫"); names.add("李"); Iterator iterable = names.iterator(); while (iterable.hasNext()){ Object obj = iterable.next(); if(obj.equals("趙")){ names.remove(obj); } } } 複製代碼
輸出報錯
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at com.neo.kotlin_test.collection.IteratorTest.main(IteratorTest.java:23) 複製代碼
由於在你迭代以前,迭代器已經被經過list.itertor()建立出來了,若是在迭代的過程當中,又對list進行了改變其容器大小的操做,那麼Java就會給出異常。由於此時Iterator對象已經沒法主動同步list作出的改變,Java會認爲你作出這樣的操做是線程不安全的,就會給出善意的提醒(拋出ConcurrentModificationException異常)
ArrayList的迭代器實現 private class Itr implements Iterator<E> { protected int limit = ArrayList.this.size; int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor < limit; } public E next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); int i = cursor; if (i >= limit) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } ... } 複製代碼
在next()
方法中檢查了modCount != expectedModCount
是否相等,不等就拋出異常。 expectedModCount
參數在構造迭代器時被賦值爲數組大小。而modCount
會在Arraylist的各類操做中++
因此2者會不相等。而使用迭代器中的remove()
方法就不會拋出異常,由於它會對expectedModCount
從新賦值。
public void remove() { if (lastRet < 0) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; limit--; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } 複製代碼
kotlin是在java的基礎上進行修改的,主要是增長了不可變集合類。 建立方式以下
//不可變數組/鍵值對 只讀 var list = listOf<String>("趙","錢","孫","李") var map = mapOf<String,Int>("趙" to 22,"錢" to 33,"孫" to 40,"李" to 55) var set = setOf<String>("趙","錢","孫","李") //set.add() 沒法添加 //set.remove() 沒有刪除方法 //可變數組/鍵值對 可變 val mutableList = mutableListOf<String>("趙","錢","孫","李") val mutableMap = mutableMapOf<String,Int>("趙" to 22,"錢" to 33,"孫" to 40,"李" to 55) val mutabSet = mutableSetOf<String>("趙","錢","孫","李") mutabSet.add("111") 複製代碼
在Kotlin中,集合類型包含三種類型:它們分別是:List、Set、Map,他們之間存在如下幾個異同點:
集合層級關係是:
map和flatmap方法map():遍歷每個元素 flatMap() :遍歷每個元素,並鋪平元素 降維 println("map") val maps = listOf(listOf(1,2), listOf(2,4), listOf(3,6), listOf(4,8)) maps.map { it.map { it * 100 } }.forEach { println(it) } println("flatMap") maps.flatMap { it.map { it * 100 } }.forEach { println(it) } 複製代碼
map
[100, 200]
[200, 400]
[300, 600]
[400, 800]
flatMap
100
200
200
400
300
600
400
800
複製代碼
如何遍歷 :
list.forEach { println(it) } list.forEachIndexed{ index, s -> println("$s in $index") } map.forEach { (key, value) -> println("key : $key , value : $value") } set.forEach { println(it) } 複製代碼
過濾函數
filter{...} : 把不知足條件的元素過濾掉 filterIndexed{...} : 和filter{}函數做用相似,只是能夠操做集合中元素的下標(index) val list = listOf(22,13,44,55,66,88,102) //過濾函數 無index list.filter { it > 40 }.forEach { println(it) } println("filterIndexed") //過濾函數 有index list.filterIndexed { index, i -> index == 2 }.forEach { println(it) } 複製代碼
排序函數
reversed() : 反序。即和初始化的順序反過來。 sorted() : 天然升序。 sortedBy{} : 根據條件升序,即把不知足條件的放在前面,知足條件的放在後面 println("reversed") //反向排序 list.reversed().forEach { println(it) } println("sorted") //升序 list.sorted().forEach { println(it) } println("sortedBy") //有條件升序 list.sortedBy { it > 50 }.forEach { println(it) } 複製代碼
去重函數
distinctBy() : 去重 println("distinctBy") val list1 = listOf(22,22,13,44,44,55,55,66,67,88,102) list1.distinctBy { "name$it" }.forEach { println(it) } 複製代碼