本文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() :遍历每个元素,并铺平元素 降维 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) } 复制代码