一、Java集合框架概述
1、什么是集合
-
集合框架:用于存储数据的容器。
- 数组、集合等存储数据的结构,叫Java容器。
- 此时的存储,是指内存层面的存储,不涉及持久化的存储。
- 任何集合框架都包含三大块的内容:对外的接口、接口的实现、对集合运算的算法。
2、集合的特点
-
数组的特点/缺点:
-
长度固定。一旦初始化,长度不能修改。
-
类型确定。类型严格(算是一个好处),当然要想可以放多种类型的数据,也可以声明为Object类型。
-
方法有限。添加、删除元素效率低;获取元素个数不方便。
-
元素有序可重。对于无序、不重复的元素,不能满足。
-
集合的特点/优点
3、集合的体系
-
Collection接口和Map接口是所有集合框架的父接口。
- Collection接口继承树

说明:
- List接口:存储有序、可重复的数据。“动态数组”
- Set接口:存储无序、不可重复的数据。“数学中的集合”

说明:
- Map接口:双列集合,用于存储成对的数据(key-value)。“数学中的函数”
二、Collection接口中的方法(15个)
1、说明
- 因为Collection接口是List、Set、Queue接口的父接口,所以定义在Collection接口的中方法可以用于操作子接口的实现类的对象。
2、15个方法
方法 |
描述 |
add(Object obj) |
添加元素 |
addAll(Collection c) |
添加另一个集合中所以元素 |
size() |
元素个数 |
clear() |
清空集合 |
isEmpty() |
是否为空 |
contains(Object obj) |
包含某个元素 |
containsAll(Collection c) |
包含某个集合中所有元素 |
remove(Object obj) |
删除元素 |
removeAll(Collection c) |
差集 |
retainAll(Collection c) |
交集 |
equals(Object obj) |
判断集合是否相等 |
toArray() |
转换为数组 |
toArray(T[] a) |
|
hashCode() |
求集合的哈希值 |
iterator() |
返回迭代器对象 |
注意事项:
3、Iterator迭代器接口
-
Iterator是什么?
- Iterator对象成为迭代器,是设计模式的一种。主要用于遍历集合中的元素。
-
迭代器模式:提供一种方法访问一个容器(container)对象中各个元 素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
-
Iterator接口中方法
- hasNext()
- next()
- remove()遍历过程中移除某元素
- Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的 remove方 法,不是集合对象的remove方法。
- 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException
- 怎么用Iterator遍历集合
//用集合中的iterator方法得到迭代器对象
Iterator iterator = coll.iterator();
//遍历集合
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//当然我们也可以用foreach来遍历,其实foreach本质也是调用了迭代器。
-
Iterator原理
- 得到的迭代器对象,默认指针指向第一个元素的前面
- hasNext()来判断下一位是否有元素。(在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且 下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常)
- next():两个作用:①指针下移②取出指针指向的元素
-
注意
- 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前。因此下面这种写法不正确
while (coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
三、List接口
1、List接口概述
- List接口是Collection的子接口
- List被称为“动态数组”,是因为数组的局限性而代替数组的一种容器。
- 存储的元素有序、可重复。每个元素都有对应的顺序索引。
- 实现类有三个:ArrayList、LinkedList、Vector
2、ArrayList、LinkedList、Vector的异同(面试题)
- 同
- 三个类都是List接口的实现类,因此存储数据都是有序可重复的。
- 异
实现类 |
地位 |
since |
线程安全否 |
底层 |
应用场景 |
ArrayList |
主要实现类 |
1.2 |
线程不安全、效率高 |
Object[] |
遍历、查找 |
LinkedList |
|
1.2 |
|
双向链表 |
频繁插入、删除 |
Vector |
古老实现类 |
1.0 |
线程安全、效率低 |
Object[] |
|
3、源码分析(加分项)
-
ArrayList源码分析-----JDK7
- 创建:调用空参构造器时,底层创建一个长度为10的Object[]数组elementData
- 扩容:默认扩容为原来的1.5倍(使用移位操作),并将原来数组中的数据复制到新的数组中
- 结论:开发中使用带参构造器指定容量:
ArrayList list = new ArrayList(int capacity)
-
ArrayList源码分析-----JDK8
- 创建:调用空参构造器时,底层创建一个Object[]数组elementData,初始化为空{}
- 扩容:同JDK7
- 对比:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
-
LinkedList源码分析
- 创建:调用空参构造器时,内部声明了Node类型的first和last属性,默认值为null
- 添加:将数据封装在Node中,创建Node对象
-
Vector源码分析
- 创建:调用空参构造器时,底层创建一个长度为10的Object[]数组elementData
- 扩容:默认扩容为原来的2倍,并将原来数组中的数据复制到新的数组中
4、List常用方法
常用方法的作用 |
方法名 |
增 |
add(Object obj) |
删 |
remove(Object obj)、remove(int index) |
改 |
set(int index,Object obj) |
查 |
get(int index) |
插 |
add(int index,Object obj) |
长度 |
size() |
遍历 |
①、Iterator迭代器 ②、增强for循环 ③、普通for循环 |
- 区分List中的remove方法
- list.remove(2):删除索引为2的,因为有remove(int index)的方法,直接匹配上不用自动装箱就能匹配,name何必自动装箱呢?
- list.remove(new Integer(2)):删除数据2,匹配的是remove(Object obj)的方法
四、Set接口
1、Set接口概述
- Set接口是Collection的子接口。
- Set中没有额外的方法
- Set中的元素不可重复,判断是否相等用的是equals()方法
- 实现类有HashSet、LinkedHashSet、TreeSet
2、HashSet、LinkedHashSet、TreeSet的异同
- HashSet:是Set接口的主要实现类;线程不安全;可以存储null值;
- LinkedHashSet:是HashSet的子类;可以按照添加顺序遍历,遍历效率高;
- TreeSet:可以按照添加对象的指定属性排序
- 要求1:添加的对象类型相同
- 要求2:对象的类要实现Comparable接口来进行自然排序,或者,实现Comparator接口来进行定制排序
- 自然排序中,比较两个对象是否相同的标准为(是否可添加“相同对象”):compareTo()返回0.不再是equals().
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().
3、理解无序、可重复
- 无序
- 指的是存储在底层数组的数据,并不是按照数组索引的顺序添加的,而是根据数据的哈希值决定的。
- 无序性,不等于随机性
- 不可重复
- 相同的元素只能添加一个。元素按照equals()判断,不能返回true。
4、添加元素的过程(以HashSet为例)
-
添加过程并不会单独考察,但是对于理解HashMap有很大帮助
-
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
- 如果此位置上没有其他元素,则元素a添加成功。 --->情况1
- 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
- 如果hash值不相同,则元素a添加成功。--->情况2
- 如果hash值相同,进而需要调用元素a所在类的equals()方法:
- equals()返回true,元素a添加失败
- equals()返回false,则元素a添加成功。--->情况3
-
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储
- jdk 7 :元素a放到数组中,指向原来的元素。
- jdk 8 :原来的元素在数组中,指向元素a
- 总结:七上八下
-
HashSet底层:数组+链表的结构。
5、重写hashCode()和equals()方法
-
要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
-
重写要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
-
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
6、面试题
-
在List内去除重复数字值,要求尽量简单
-
//程序的输出结果
HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);//[Person{id=1002,name='BB'},Person{id=1001,name='CC'}]
/*
存放p1、p2时,根据p1和p2的属性算出了hashCode来确定存放位置
p1的name改为CC后,再删除p1--->根据此时p1的属性算hashCode来找set中和p1相同的元素,此时算出来的hashCode大概率不是p1的位置,相当于没有找到,所以删除并没有成功。
*/
set.add(new Person(1001,"CC"));
System.out.println(set); //[Person{id=1002,name='CC'},name='CC'}]
/*
存放新建的p,是根据1001和CC来算hashCode,此时数组中这个位置空的,所以存放成功
*/
set.add(new Person(1001,"AA"));
System.out.println(set);//[Person{id=1002,name='AA'}]
/*
存放新建的p,先hashCode后和p1是一样的,再equals发现不一样,加到链表上,存放成功
*/
五、Map接口
1、Map接口概述

-
Map接口:
- 存储key-value的键值对,类似于数学中的函数
- Map实现类对比
Map实现类 |
底层实现 |
线程 |
地位 |
特点 |
HashMap |
JDK7及以前:数组+链表 JDK8:数组+链表+红黑树 |
线程不安全 |
主要实现类 |
可以存储null的k-v |
LinkedHashMap |
同上 |
同上 |
同上 |
可按照添加顺序遍历 |
TreeMap |
--- |
--- |
--- |
可排序遍历 |
Hashtable |
--- |
线程安全 |
古老实现类 |
不可存储null的k-v |
Properties |
--- |
--- |
--- |
常用来处理配置文件 |
2、面试题
3、Map结构的理解

-
Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写equals()和hashCode() (以HashMap为例);当然如果是LinkedHashMap还要实现排序接口
-
Map中的value:无序的、可重复的,使用Collection存储所有的value --->value所在的类要重写equals()
-
一个键值对:key-value构成了一个Entry对象。
-
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
四、 HashMap的底层实现原理★★★★★(高频面试题)
1、JDK7
2、JDK8与JDK7的不同之处
-
HashMap map = new HashMap() 时,底层没有直接创建数组,而是在首次put()时创建
- 数组是Node[]类型,而非Entry[]类型
- JDK7底层是:数组+链表;而JDK8底层是:数组+链表+红黑树
- 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
- 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。
3、LinkedHashMap底层实现原理
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before,after;//能够记录添加的元素的先后顺序
Entry(int hash,K key,V value,Node<K,V> next) {
super(hash,key,value,next);
}
}
六、Map接口常用方法
1、添加、删除、修改操作:
-
Object put(Object key,Object value) :将指定key-value添加到(或修改)当前map对象中
void putAll(Map m) :将m中的所有key-value对存放到当前map中
-
Object remove(Object key) :移除指定key的key-value对,并返回value
void clear() :清空当前map中的所有数据
2、 元素查询的操作:
-
Object get(Object key) :获取指定key对应的value
-
boolean containsKey(Object key) :是否包含指定的key
boolean containsValue(Object value) :是否包含指定的value
-
int size() :返回map中key-value对的个数
-
boolean isEmpty() :判断当前map是否为空
-
boolean equals(Object obj) :判断当前map和参数对象obj是否相等
3、元视图操作的方法:
-
Set keySet() :返回所有key构成的Set集合
-
Collection values() :返回所有value构成的Collection集合
-
Set entrySet() :返回所有key-value对构成的Set集合
4、总结常用方法:
- 添加:
put(Object key,Object value)
- 删除:
remove(Object key)
- 修改:
put(Object key,Object value)
- 查询:
get(Object key)
- 长度:
size()
- 遍历:
keySet() / values() / entrySet()
七、Properties
1、配置文件
八、Collections工具类
1、Collections概述
- Collections是一个操作Set、List、Map等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
2、Collections常用方法
排序操作
- 排序操作都是针对List来讲的,因为Map本身无序,何谈排序
- 返回值都是void,说明对List本身做了修改
方法 |
解释 |
reverse(List) |
反转List中的元素 |
shuffle(List) |
随机排序 |
sort(List) |
自然排序 |
Sort(List,Comparator) |
定制排序 |
swap(List,int i,int j) |
交换 |
查询、替换
方法 |
解释 |
Object max/min(Collection) |
返回自然排序的最大、小值 |
Object max/min(Collection,Comparator) |
返回定制排序的最大、小值 |
int frequency(Collection,Object) |
返回某集合中某元素的出现次数 |
void copy(List dest,List src) |
复制 |
boolean replaceAll(List list,Object oldVal,Object newVal) |
替换 |
- void copy(List dest,List src)中,新List容量不能小于旧List,因此要用
List dest = Arrays.asList(new Object[list.size()]); 来创建
3、Collection和Collections的区别(面试题) (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|