对JDK8集合框架中一些常用接口和类的总结和理解

目录:


集合

  用来存储一组同一类型的数据。
  与数组相比的优势:①集合的大小可以随着存储数据的多少动态改变。②集合的实现类中实现了增删查改等方法,方便操作数据。
  集合分为单列集合和双列集合。单列集合指存储的数据元素是单个数据;双列集合指存储的数据元素是一对数据,即包含一个Key值和一个Value值的一个键值对数据。

Collection<E>(单列集合)

  Collection接口是单列集合的总接口,下面有两个主要的子接口:

  • List:主要的实现类添加元素时会记录元素的添加顺序和元素本身的信息,并且允许元素可以重复
  • Set:主要的实现类(除LinkedHashSet外)添加元素时不会记录元素的添加顺序,都会记录元素本身的信息,并且不允许元素重复。

  Collection接口中规定了各种单列集合都应当具有的共有的一些方法,例如:

  • boolean add(E e);//向集合中添加某个元素,返回添加是否成功
  • boolean remove(Object obj);//删除集合中的某个元素,返回删除是否成功

  一些理解:没有查找和更改方法的原因:Collection接口的某些实现类不能实现有意义的查找方法和更改方法。
  元素存储在单列集合中时具有两个信息,一个是元素本身的信息,一个是元素在集合中的位置信息。查找时,一方面会根据位置信息查找元素信息(例如:List接口中的E get(int index)方法),另一方面会根据元素信息查找位置信息(例如:List接口中的int indexOf(Object o)方法)。更改时,一般期望在不改变位置信息的前提下,只更改元素信息,那么会先根据位置信息或元素信息先找到元素,再改变元素信息(例如:List接口中的E set(int index,E e))。
  而在Collection接口的主要实现类中,Set接口中的HashSet实现类中元素的位置信息是无意义的(ArrayList、LinkedList、LinkedHashSet中元素的位置信息包含了元素存储时的顺序,TreeSet中元素的位置信息包含了根据比较器排列后的排列顺序),所以无法实现有意义的查找方法和更改方法,因此不能在Collection接口中规定所有实现类都要具有查找方法和更改方法

  • boolean contains(Object obj);//判断集合是否包含某个元素
  • boolean isEmpty();//判断集合是否为空
  • int size();//获取集合中元素的个数
  • void clear();//清空集合
  • Object[] toArray();//将集合转换为对应类型的数组
  • Iterator< E > iterator();//获取迭代器,用来遍历集合

  Iterator是一个接口,其中规定了几个用于遍历的方法

  • hasNext();//是否有下一个元素
  • next();//获取下一个元素
  • remove();//删除上一个获取的元素

  用迭代器遍历集合时出现ConcurrentModificationException异常的原因:Collection接口的主要实现类中都定义了一个modCount变量用于记录集合的操作次数,对集合进行新增、删除等操作会改变modCount的值。在创建迭代器对象时,迭代器的构造方法中会将创建时的modCount记录下来(expectedModCount = modCount;),每次调用next()方法时都会调用checkForComodification()方法检查当前集合的操作次数是否改变,如果改变就会抛出ConcurrentModificationException异常。
  使用迭代器的remove()方法删除元素不会抛出异常是因为,在remove方法中调用当前集合的remove方法后,会重新给迭代器对象中的expectedModCount变量赋值。


List<E>

  List接口是各种列表的总接口,主要有两个实现类:

  • ArrayList:底层通过数组存储元素。根据索引值查找元素很快,在指定位置增删元素较慢
  • LinkedList:底层通过链表存储元素,根据索引值查找元素较慢,在指定位置增删元素较快

  List接口中规定了列表集合应该具有的一些方法:

  • void add(int index,E e);//在指定位置添加元素
  • E remove(int index);//删除指定位置的元素
  • E get(int index);//获取指定位置的元素
  • E set(int index,E e);//更改指定位置的元素
  • List< E > subList(int beginIndex,int endIndex);//截取指定索引值范围内的元素,构成一个新的集合
  • int indexOf(Object o);//获取指定元素第一次出现的位置
  • int lastIndexOf(Object o);//获取指定元素最后一次出现的位置
ArrayList<E>

  ArrayList集合是以数组存储元素的,相比于数组,它实现了定义后容量的动态增长。
  扩容:调用ArrayList的无参构造创建一个ArrayList对象时,它的容量为0,第一次调用add方法时,数组容量会扩增到10,以后每次扩容后新容量为旧容量的1.5倍(int newCapacity = oldCapacity + (oldCapacity >> 1);)。此外,如果在创建对象时指定容量,会直接创建一个指定容量的数组(this.elementData = new Object[initialCapacity];)。
  ArrayList集合根据索引值查找元素很快的原因:可以根据数组的地址、索引值以及元素所占的内存大小快速计算出要查找元素的地址,进而直接访问对应元素。
  在指定位置增删元素慢的原因:为了保持数组所有元素的地址是连续的,增删元素后,在确保容量足够的情况下,需要将指定位置后的所有元素后移/前移一个位置。

LinkedList<E>

  LinkedList 集合是以双向链表来存储元素的:双向链表由一个个的节点连接而成,每个节点由element、prev、next三部分组成,element是元素本身,next指向下一个节点,prev指向上一个节点。
双向链表
  相比于ArrayList集合LinkedList实现了真正意义上的动态容量,新增元素时,集合会新建一个节点,链接到链表最后面。
  LinkedList集合根据索引值查找元素较慢的原因:对于双向链表无法根据索引值快速得知对应元素的地址,只能从头结点/尾结点不断获取下一个/上一个节点的地址,直到对应元素所处的节点。从头结点还是尾结点开始取决于索引值与元素个数的相对大小(index < (size >> 1)
  LinkedList集合在指定位置增删元素较快的原因:在查找到指定位置的元素节点后,只需要改变当前节点与前一个节点的next、prev的值就可以完成增删。
  由于LinkedList中存储了头结点(first)和尾结点(last)的信息,所以有一些独有的与首尾操作有关的方法:

  • void addFirst(E e);//在第一个元素前插入一个元素
  • void addLast(E e);//在链表最后增加一个元素
  • String removeFirst();//删除第一个元素
  • String removeLast();//删除最后一个元素
  • String getFirst();//获取第一个元素
  • String getLast();//获取最后一个元素

Set<E>

  Set是集的总接口,它的三个主要的实现类(HashSet、LinkedHashSet、TreeSet)都是基于Map的主要实现类实现的。不同的是在Map中每个元素结点中存储了key和value一对键值对数据,而Set中value的值为PRESENT,PRESENT是一个静态常量,并没有实际意义,用来填充value只是为了保证元素结点的结构与Map相同,进而可以通过Map来完成数据的存储等功能。此外,Set集合可以通过迭代器来遍历集合,而Map没有实现迭代器接口(因为Map的核心是映射,更多是为了查找迅速;而单列集合则更多是收集数据,处理数据)。

HashSet<E>

  底层是HashMap
  迭代器的迭代顺序:先从数组索引值为0找起,如果有元素,则按照对应位置下链表与红黑树中元素结点的next依次遍历,遍历完成后,继续在数组上寻找,直到遍历完数组。

LinkedHashSet<E>

  底层是LinkedHashMap
  迭代器的迭代顺序:从双向链表的头结点开始,根据每个节点的after依次遍历整个链表

TreeSet<E>

  底层是TreeMap
  迭代器的迭代顺序:按照以比较器或者存储元素的compareTo方法比较后的排列顺序迭代

Map<K ,V>(双列集合)

  Map是双列集合的总接口,主要的实现类通过哈希表+单向链表/红黑树或者红黑树构建了从key到key-value键值对的一一映射:

  • HashMap:以哈希表+单向链表/红黑树来存储元素,元素不会重复,不会记录元素的存储顺序
  • LinkedHashMap:基于HashMap的存储原理来存储元素,元素不会重复,不过每个元素结点中加入了afterbefore两个引用,用于记录元素的存储顺序
  • TreeMap:以红黑树来存储元素,元素不会重复,不会记录元素的存储顺序,但在存储每个元素时,会根据比较器或key的compareTo方法将元素插入到对应的位置

Map中规定了双列集合应该具有的一些方法:

  • V put(K k,V v);//如果k存在,则新的v替换旧的v,返回被替换的v;如果k不存在,则返回null
  • V remove(Object key);//根据key删除整个元素节点,返回被删除元素的v;如果Key不存在,则返回null
  • V get(Object key);//根据key获取v,如果key不存在则返回null
  • boolean containsKey(Object key);//集合是否包含key
  • boolean containsValue(Object value);//集合是否包含value
  • Set<K>keySet();//获取存储所有key的Set类型的集合
  • Collection<V> values();//获取存储所有value的Collection类型的集合
  • Set<Entry<K,V>> entrySet();//获取存储所有键值对的Set类型的集合
  • boolean isEmpty();//判断集合是否为空
  • void clear();//清空集合
  • int size();//获取集合中元素的个数

HashMap<K ,V>

  通过哈希表+单向链表/红黑树存储元素,每个元素结点由hash(key的哈希值)、key、value、next /(parent、left、right、prev、next)几部分组成。

hashmap
  1、HashMap新增元素过程:

  • 根据key计算hash值,再通过hash%数组长度((tab.length - 1) & hash)找到元素在数组中的存储位置
  • 判断该位置是否有元素
    • 如果没有元素,则根据元素信息创建新结点,使数组对应位置指向该节点;
    • 如果有元素,则判断头结点是否是树结点
      • 是树结点,则按照红黑树的规则找到添加元素,key重复时,会更改原来的value值
      • 不是树结点,则判断key是否重复,重复会更改原来的value值;没有重复时,则添加到链表最后,并判断当前链表的个数是否大于8个,大于8个时会试图将链表转换为红黑树
        • 当整个数组的长度大于等于64时,会转换为红黑树
        • 数组长度小于64时,会扩容数组
  • 新增元素后会判断集合中元素个数是否大于数组长度的75%,如果大于,则扩容数组,以保证数组每个索引值下的元素不会太多,减少查找时间

  2、判断元素重复的规则:哈希值相同&&((地址值相同)||(equals方法返回值为true))(e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))

  3、HashMap扩容的规则:如果集合是刚创建的,则将数组长度设置为16;如果已经创建,则扩容一倍(newCap = oldCap << 1),并将链表或红黑树拆分为两部分,分别挂在原来索引值位置下和间隔旧数组长度的索引值位置下。
  这样扩容可以保证数组长度始终是2的n次幂,一方面可以将元素哈希值%数组长度的运算转换为位运算(只有在tab.length等于2的幂时(tab.length-1)&hash==tab.length%hash),节省时间;另一方面在拆分链表或红黑树时也可以使用位运算,加快速度。

LinkedHashMap<K ,V>

  LinkedHashMap是HashMap的子类,它的元素结点比HashMap多了两个引用:after指向下一个加入集合的结点、before指向上一个加入集合的结点
LinkedHashM
  对于每个元素结点来说,从hash(key的哈希值)、key、value、next的角度看,它属于哈希表;从before、after的角度看,它属于双向链表
  LinkedHashMap按照HashMap的规则添加元素,在添加完成后,会更改新增结点的after和before值,将它挂在双向链表最后。此外,根据key更改value后,也会调整更改结点的位置到链表最后。

TreeMap<K ,V>

  TreeMap通过红黑树来存储元素。存储元素时会对元素进行排序。
  在创建TreeMap时,需要传入比较器或者要保证Key类型实现了Comparable接口,否则会抛出ClassCastException异常。此外,如果Key类型实现了Comparable接口,也传入了比较器,那么会根据比较器的规则进行排序。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值