什么是集合
集合框架:用于存储数据的容器,集合框架是为表示和操作集合而规定的一种统一标准的体系结构。任何集合框架都包含三大快快内容:对外的接口、接口的实现和对集合运算的算法
接口:表示集合的抽象数据类型。接口允许我们操作集合时不必关注具体实现,从而达到“多态”。在面向对象编程语言中,接口通常用来形成规范。
实现:集合接口的具体实现,是重用性很高的数据结构。
算法:在一个实现了某个集合框架中的对象身上完成某种有用计算的方法,例如查找、排序等。这些算法通常是多态的,因为相同的方法可以在同一个接口被多个类实现时有不同的表现。事实上,算法是可复用的函数。它减少了程序设计的辛劳。
集合框架通过提供有用的数据结构和算法使你能集中注意力于你的程序的重要部分上,而不是为了让程序能正常运转,而是将集中注意力于底层设计上。
通过这些在无关 API 之间的简易的互用性,使你免除了为改变对象或转换代码以便联合这些 API 而去写大量的代码,它提高了程序的速度和质量。
集合的特点
集合的特点主要有如下两点:
- 对象封装数据,对象多了也需要存储。集合用于存储对象。
- 对象的个数确定可以使用数组,对象的个数不确定的可以用集合。因为集合是可变长度的。
集合和数组的区别
- 数组是固定长度的;集合是可变长度的
- 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型
- 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型
数据结构
数据结构就是容器中存储数据的方式。
对于集合容器,有很多种。因为每一个容器的自身特点不同,其实原理在于每个容器的内部数据结构不同。
集合容器在不断向上抽取过程中,出现了集合体系。在使用一个体系的原则:参阅顶层内容,建立底层对象
使用集合框架的好处
- 容量自增长
- 提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量
- 允许不同 API 之间的相互操作, API 之间可以来回传递集合
- 可以方便地扩展或改写集合,提高代码复用性和可操作性
- 通过使用 JDK 自带的集合类,可以降低代码维护和学习新 API 成本
常用的集合类有哪些
Map 接口和 Collection 接口是所有集合框架的父接口
- Collection 接口的子接口包括:Set接口和List接口
- Map 接口的实现类主要有:HashMap、TreeMap、HashTable、ConcurrentHashMap 以及 Properties 等
- Set 接口的实现类主要有:HasdSet、TreeSet、LinkedHashSet 等
- List 接口的实现类主要有:ArrayList、LinkedList、Stack 以及 Vector 等
List、Set、Map 三者的区别?List、Set、Map 是否继承自 Collection 接口?List、Map、Set 三个接口存取元素时,各有什么特点?
Java 容器分为 Collection 和 Map 两大类,Collection 集合有子接口有 Set、List、Queue 三种子接口。我们比较常用的是 Set、List,Map 接口不是 Collection 的子接口。
Collection 集合主要有 List 和 Set 两大接口
- List: 一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个 null 元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
- Set: 一个无序(存入和去除顺序有可能不一致)容器,不仅而已存储重复元素,只允许存入一个 null 元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
Map 是一个键值对集合,存储键、值和之间的映射。Key 无序,唯一;value 不要求有序,允许重复。Map 没有继承与 Collection 接口,从 Map 集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map 的常用实现类: HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap
集合框架底层数据结构
List
- ArrayList: Object 数组
- Vector: Object 数组
- LinkedList: 双向循环链表
Set
- HasehSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
- LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的 LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点去别的。
- TreeSet(有序,唯一): 红黑树(自平衡二叉树)
- HashMap: JDK 1.8 之前 HashMap 由数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的("拉链法"解决冲突)。JDK 1.8 以后在解决哈希冲突时有了比较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
- LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使的上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
- HashTable: 数组+链表组成的,数组是 HashMap的主体,链表则是主要为了解决哈希冲突存在的
- TreeMap: 红黑树(自平衡的排序二叉树)
哪些集合类是线程安全的?
- Vetor: 就比 ArrayList 多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在 Web 应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
- Stack: 堆栈类,先进后出
- HashTable:就比 HashMap 多了个线程安全
- Enumeration: 枚举,相当于迭代器
Java集合的快速失败机制“fail-fast”?
是 Java 集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。
例如: 假设存在两个线程(线程1、线程2),线程 1 通过 Iterator 在遍历集合 A 中的元素,在某个时候线程 2 修改了集合 A 的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 一场,从而产生 fail-fast 机制。
原因:迭代器在便利时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用 hashNext()/next() 便利下一个元素之前,都会检查 modCount 变量的值,是的话就返回便利;否则抛出异常,终止便遍历。
解决办法:
- 在遍历过程中,所有涉及到改变 modCount 值的地方全部加上 synchronized.
- 使用 CopyOnWriteArrayList 来替换 ArrayList
怎么确保一个集合不能被修改?
可以使用 Collections.unmodifiableCollection(Collection c)方法来创建一个只读集合,这样改变集合的任何操作都会抛出 java.lang.UnsupportedOperationException() 异常。示例代码如下:
List<String> list = new ArrayList<>();
list.add("x");
Collection<String> clist = Collections.unmodifiableCollection(list);
clist.add("y"); // 运行时此行报错
System.out.println(list.size());