Java與Kotlin的類集

本文2個方向java

  1. Java類集的介紹以及代碼分析
  2. Kotlin中類集的使用以及相關優化

Java類集的介紹以及代碼分析

  1. 找到Collection接口
  2. Collection接口有什麼
  3. 整個類集框架的結構是什麼
  4. List接口是什麼樣的 ArrayList LinkedList怎麼使用
  5. Set接口是什麼樣 HashSet TreeSet SortedSet怎麼使用
  6. Map<K,V>接口是什麼樣 HashMap TreeMap怎麼使用
  7. 迭代器是什麼,怎麼使用

1.找到Collection接口

先看下面的代碼算法

//鍵值對
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 HashMapSet HashSetList 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>,全部類集的核心方法都在裏面了安全

2.Collection接口有什麼

咱們來看看這個接口裏提供了什麼方法: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

3.整個類集框架的結構是什麼

從上圖分析,集合框架分爲2部分:

  1. Collection集合,存儲一個元素集合;
  2. Map圖,存儲鍵值對 Collection 接口又有 3 種子類型,List、Set 和 Queue, 再下面是一些抽象類,最後是具體實現類,經常使用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等

知道了整個集合框架的繼承關係後,咱們來研究下,實現一個集合類要哪些部分呢? 咱們從他們的子接口出發去探究下。數據結構

4. List接口是什麼樣的 ArrayList LinkedList怎麼使用

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的下標,元素能夠經過他們在列表中的位置被插入和訪問
複製代碼

5. Set接口是什麼樣 HashSet TreeSet SortedSet怎麼使用

Set<E>接口與Collection接口的不一樣點
增長方法:沒有,徹底集成了Collection接口

特性:類集中元素不容許重複,即在set中不能出現徹底相同的2個元素。
複製代碼

6. Map<K,V>接口是什麼樣 HashMap TreeMap怎麼使用

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個部分組成:框架

  1. 接口 表明抽象的數據類型,例如 Collection、List、Set、Map 等。之因此定義多個接口,是爲了以不一樣的方式操做集合對象
  2. 實現 接口的具體實現,可重複使用的數據結構,例如:ArrayList、LinkedList、HashSet、HashMap
  3. 算法 實現類中的一些計算,例如搜索和排序

7.迭代器是什麼,怎麼使用

  1. 迭代是什麼:就是提供一種方法對一個容器對象中的各個元素進行訪問,而又不暴露該對象容器的內部細節。
  2. 爲何會有迭代器: 由於容器的內部結構不一樣(ArrayList底層維護的是一個數組;LinkedList是鏈表結構的;HashSet依賴的是哈希表),不少時候可能不知道該怎樣去遍歷一個容器中的元素。因此爲了使對容器內元素的操做更爲簡單,Java引入了迭代器模式!

對於數組,可使用以下方法進行遍歷:函數

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()刪除當前元素
複製代碼

迭代的流程是什麼呢:

  1. 取得迭代方法Iterator iterator();
  2. 創建一個調用hasNext()方法的循環,只要返回true就進行循環迭代
  3. 在循環內部,經過調用next()方法獲取每個元素
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中類集的使用以及相關優化

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,他們之間存在如下幾個異同點:

  1. 它們都是接口,並非實際的類。
  2. 它們只實現了isEmpty()、size、contains()等函數以及屬性。
  3. List和Set都繼承至Collection接口,且Collection繼承於Iterable接口。 而Map<K,V>是獨立出來的一個接口。這一點和Java相同。
  4. 這三種集合類型分別有存在MutableList、MutableSet、MutableMap<K,V>接口,這些接口中提供了改變、操做集合的方法。例如add()、clear()、remove()等函數。

集合層級關係是:

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)
}
複製代碼
相關文章
相關標籤/搜索