JAVA集合详解(一)

17 篇文章 0 订阅
5 篇文章 0 订阅

1.List,Map,Set存取的特点

  1. List
    • 可以有重复元素
    • 以特定索引保存
  2. Map和Set
    • 可以一对一和多对一
    • 有哈希存储和排序树两个版本
      在这里插入图片描述

2.ArrayList、Vector、LinkedList的存储性能和特性

  • ArrayList 和Vector
    • 都是使用数组方式存储数据
    • 都允许直接按序号索引元素
    • Vector是线程安全的容器,添加了synchronized修饰,但性能上较ArrayList差
  • LinkedList使用双向链表实现存储
    • 可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高
    • 插入数据时只需要记录本项的前后项即可,所以插入速度较快

3.ArrayList和LinkedList的区别

都实现了List接口,LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

  1. ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
  2. LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。

4.HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:

4.1继承的父类不同

Hashtable继承自Dictionary类。
HashMap继承自AbstractMap类。
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {}
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {}

4.2线程安全性不同

  • HashMap是线程不安全的
  • HashTable是线程安全的
public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

    /**
     * Clears this hashtable so that it contains no keys.
     */
    public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

4.3key和value是否允许null值

  • Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
  • HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。

4.4内部实现使用的数组初始化和扩容方式不同

  • HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。

  • Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

    • 将哈希表的大小固定为了2的幂,因为是取模得到索引值,故这样取模时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
    • 扩容resize()的时候,原来哈希表中,有接近一半的节点的下标是不变的,而另外的一半的下标为 原来的length + 原来的下标。具体要看hash值对应扩容后的某一位是0还是1.

4.5迭代器不同

  • HashMap 中的 Iterator 迭代器是 fail-fast 的
  • Hashtable 的 Enumerator 不是 fail-fast 的。

快速失败(fail—fast)是java集合中的一种机制, 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception

5.快速失败(fail-fast)和安全失败(fail-safe)的区别

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。

快速失败(fail-fast )是java集合中的一种机制,在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加,删除,修改),则会抛出 ConcurrentModification Exception。

  • java.util包下面的所有的集合类都是快速失败的
  • java.util.concurrent包下面的所有的类都是安全失败的。
  • 快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。

找一下HashMap的源码,有一个int 类型的变量modCount。

 /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;
  • 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。

  • 集合在被遍历期间如果内容发生变化,就会改变modCount的值。

  • 每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

Tip:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。

因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

在这里插入图片描述

6.Iterator和ListIterator的区别

  • Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
  • Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
  • ListIterator继承了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

6.1ListIterator 继承自 Iterator 接口,在 Iterator 的基础上增加了 6 个方法:

在这里插入图片描述

  • void hasPrevious()
    • 判断游标前面是否有元素;
  • Object previous()
    • 返回游标前面的元素,同时游标前移一位。游标前没有元素就报 java.util.NoSuchElementException 的错,所以使用前最好判断一下;
  • int nextIndex()
    • 返回游标后边元素的索引位置,初始为 0 ;遍历 N 个元素结束时为 N;
  • int previousIndex()
    • 返回游标前面元素的位置,初始时为 -1,同时报 java.util.NoSuchElementException 错;
  • void add(E)
    • 在游标 前面 插入一个元素
      注意,是前面
  • void set(E)
    • 更新迭代器最后一次操作的元素为 E,也就是更新最后一次调用 next() 或者 previous() 返回的元素。
    • 注意,当没有迭代,也就是没有调用 next() 或者 previous() 直接调用 set 时会报 java.lang.IllegalStateException 错;
  • void remove()
    • 删除迭代器最后一次操作的元素,注意事项和 set 一样。

6.2ListIterator 有两种获取方式

  • List.listIterator()
  • List.listIterator(int location)

第二种可以指定 游标的所在位置。

7.什么是迭代器

  • Iterator提供了统一遍历操作集合元素的统一接口, Collection接口也是继承Iterable接口.
    每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例,然后对集合的元素进行迭代操作.
    有一点需要注意的是:在迭代元素的时候不能通过集合的方法删除元素, 否则会抛出ConcurrentModificationException 异常. 但是可以通过Iterator接口中的remove()方法进行删除.

8.类和接口的关系

  • 类和类:类继承类,单继承,多层继承
  • 类和接口:类实现接口,可实现多个接口,接口名之间用逗号隔开
  • 接口和接口:接口继承接口,多继承,为了弥补java中类的单继承特点
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值