Java中常用的集合类

简介: Java中常用的集合类

1.Java中集合框架图

image.png

2.List 和 Set 的区别?

  1. List , Set 都是继承自Collection 接口
  2. List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
  3. Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是HashSet、LinkedHashSet 以及 TreeSet。
  4. List 支持for循环遍历,也可以用迭代器,但是set只能用迭代,因为它是无序的,所以无法用下标来取得想要的值。

  5. Set和List对比
    Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
    List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变

3.为什么HashMap中String、Integer这样的包装类适合作为key?

  1. String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率。内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范,不容易出现Hash值计算错误的情况。
  2. 都是final类型,即不可变性,保证key的不可更改性,不会存在获取 hash值不同的情况。

4.ArrayList的优缺点?

  1. ArrayList的优点如下:
    ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。
    ArrayList 在顺序添加一个元素的时候非常方便。
  2. ArrayList 的缺点如下:
    删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。
    插入元素的时候,也需要做一次元素复制操作,缺点同上。
  3. ArrayList适合使用的场景
    ArrayList 比较适合顺序添加、随机访问的场景。

5.ArrayList 和 Vector 的区别是什么?

  1. ArrayList和Vector都实现了List 接口,List 接口继承了 Collection 接口,它们都是有序集合

  2. 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。

  3. 性能效率:因为是否加锁会影响效率,所有不加锁的ArrayList 在性能方面要优于加锁的Vector。

  4. 扩容机制:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

  5. Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

6.HashMap 的长度为什么是2的幂次方?

  1. 为什么HashMap的长度是2的幂次方?
    为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。

  2. 这个算法应该如何设计呢?或者说这个算法该怎么实现呢?
    我们首先可能会想到采用%取余的操作来实现。重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 最后我们HashMap底层采用的就是通过二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。

  3. 那为什么是两次扰动呢?(jdk1.8源码)
    加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性, 最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的。

    在JDK 1.7中,更为简洁,相比在1.7中的4次位运算,5次异或运算(9次扰动),在1.8中,只进行了1次位运算和1次异或运算(2次扰动)

7. 能否使用任何类作为 Map 的 key?

  1. 可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点: 如果类重写了 equals() 方法,也应该重写 hashCode() 方法。类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则。如果一个类没有使用 equals(),不应该在 hashCode() 中使用它。

  2. 用户自定义 Key 类的最佳实践是使之为不可变的,也就是需要通过final关键字来修饰,这样 hashCode() 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode() 和 equals() 在未来不会改变,这样就会解决与可变相关的问题了。

8.Java集合的快速失败机制 “fail-fast”?

  1. fail-fast是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。
  2. 实操案例:
    例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中 的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出ConcurrentModificationException 异常,从而产生fail-fast机制。
  3. 原因解析:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出 异常,终止遍历。
  4. 解决办法:
    方法1:在遍历过程中,所有涉及到改变modCount值得地方全部加上 synchronized。
    方法2:使用CopyOnWriteArrayList来替换ArrayList

9.Iterator 和 ListIterator 有什么区别?

  1. Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。

  2. Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。

  3. ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元 素、替换一个元素、获取前面或后面元素的索引位置。

10.为什么 ArrayList 的 elementData 加上 transient 修饰?

  1. ArrayList 中的数组定义如下:
    transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现
    private transient Object[] elementData;
    
  2. 再看一下 ArrayList 的定义:
    可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
  3. 每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非transient 元素,然后遍历 elementData,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后的文件大小。

  4. 底层实操代码:
    ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;
/**
 * Save the state of the <tt>ArrayList</tt> instance to a stream (that
 * is, serialize it).
 *
 * @serialData The length of the array backing the <tt>ArrayList</tt>
 *             instance is emitted (int), followed by all of its elements
 *             (each an <tt>Object</tt>) in the proper order.
 */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
   
   
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
   
   
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
   
   
        throw new ConcurrentModificationException();
    }
}

反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData

/**
 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
 * deserialize it).
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
   
   
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
   
   
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
   
   
            a[i] = s.readObject();
        }
    }
}

总结:为什么不直接用elementData来序列化,而采用上述的方式来实现序列化呢?原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上述的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。

相关文章
|
3天前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
13 3
|
1天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
13 6
|
1天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
【10月更文挑战第16天】Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。通过 hashCode() 和 equals() 方法实现唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 添加和遍历元素,体现了 Set 的高效性和简洁性。
11 4
|
1天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
9 3
|
3天前
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
|
3天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。它通过 hashCode() 和 equals() 方法确保元素唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 实现这一特性。
11 5
|
1天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
6 2
|
1天前
|
Java 开发者
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素。通过哈希表和红黑树两种模式,Set能够高效地识别并拒绝重复元素的入侵,确保集合的纯净。无论是HashSet还是TreeSet,都能在不同的场景下发挥出色的表现,成为开发者手中的利器。
10 2
|
3天前
|
存储 Java 数据处理
在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出
【10月更文挑战第14天】在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出。本文通过两个案例展示了Set的实用性和高效性:快速去重和高效查找。通过将列表转换为HashSet,可以轻松实现去重;而Set的contains方法则提供了快速的元素查找功能。这些特性使Set成为处理大量数据时的利器。
11 4
|
2天前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
9 2