学习集合整理的学习笔记
一、Collection(集合)
1.1 集合框架的概述
- 1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器
- 此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt)
- 2.数组在存储多个数据方面的特点:
- 一旦初始化以后,其长度就确定了
- 数组一旦定义好,其元素的类型也就确定了,我们也只能操作指定类型的数据了
- 3.数组在存储多个数据方面的缺点:
- 一旦初始化以后,其长度就不可以修改
- 数组中提供的方法非常有限,对于添加,删除,插入数据等操作,非常不便,同时效率不高
- 获取数组中实际元素的个数,数组没有现成的属性或方法可用
- 数组存储数据的特点:有序、可重复。对于无序,不可重复的需求,不能满足
1.2 分类
- Collection接口: 单列数据,定义了一组对象的方法的集合
- List: 元素有序、可重复的集合(动态数组)
- ArrayList、LinkedList、Vector
- Set: 元素无序,不可重复的集合
- HashSet、LinkedHashSet、TreeSet
- Map接口: 双列数据,保存具有映射关系“key-value”的集合【类似python的字典】
- HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
1.3 Collection中定义的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| add(Object e):集合里添加数据
size():返回一个int型,获取集合里数据的个数
addAll(Collection coll):将一个集合的元素,全部添加到另一个集合里
clear():清空集合里的元素
isEmpty():返回一个boolean型,判断当前集合是否为空
contains(Object obj):返回一个boolean型,判断当前集合中是否包含obj 注意:contains判断对象时,是调用该对象内equals方法,若此方法没重写,默认比较地址值。
containsAll(Collection coll):返回一个boolean型,判断当前集合里的数据是否全部包含了另一个集合的数据
remove(Object obj):返回一个boolean型,移除某个元素,移除成功true,否者false 注意:remove也是调用该对象的equals方法进行匹配,相同就进行移除
removeAll(Collection coll):返回一个boolean型,将一个集合里的元素从另一个集合里全部移除(差集)
retainAll(Collection coll):返回一个boolean型,保留两个集合的共同数据(交集)
equals(Object obj):返回一个boolean型,比较两个集合是否相同
hashCode():返回一个int型,计算哈希值并返回
toArray():返回一个Object[],将一个集合转变成数组
额外拓展: Arrays.asList(数组):将一个数组变成一个集合
|
1.4 集合遍历
- (1)迭代器
- 迭代器是Collocetion的其中一个方法。iterator(),返回一个迭代器对象。用于遍历数组
- 迭代器操作:
- 1.内部的方法:hasNext() 和 next()
- 2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
- 3.内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
1 2 3
| while(iterator.hasNext()){ System.out.println(iterator.next()); }
|
- (2)增强for循环
- 格式:
for(集合中元素的类型 局部变量 :集合对象)
,类似python的for xx in xx
- 内部仍然调用了迭代器
1 2 3
| for(Object obj : coll){ System.out.println(obj); }
|
二、List
2.1 分类
|—-Collection接口
|—-List接口:存储有序的,可重复的数据
|—-ArrayList:作为List接口的主要实现类(1.2出现),线程不安全,效率高;底层使用Object[] elementData存储
|—-LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList效率高。底层使用双向链表存储
|—-Vector:作为List接口的古老实现类(1.0出现),线程安全,效率低;底层使用Object[] elementData存储
2.2 ArrayList源码分析
1 2 3 4 5 6 7 8 9
| ArrayList list = new ArrayList();
list.add(123);
... list.add(11);
|
- 结论:建议开发中使用带参的构造器:
ArrayList list = new ArrayList(int capacity)
1 2 3 4 5 6 7
| ArrayList list = new ArrayList();
list.add(123);
... 后续的添加和扩容操作与JDK7一样
|
总结:
JDK7中的ArrayList的对象创建,类似于单例模式的饿汉式
JDK8中的ArrayList的对象创建,类似于单例模式的懒汉式,延迟了数组的创建,节省内存
2.3 LinkedList源码分析
1 2 3 4 5
| LinkedList list = new LinkedList();
list.add(213);
|
1 2 3 4 5 6 7 8 9 10 11 12
| 其中,Node定义为:体现了LinkedList的双向链表的说法 private static class Node<E> { E item; Node<E> next; Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
|
2.4 List接口中的常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 除了Collection中的方法外: void add(int index, Obejct else):在指定的位置中插入数组
boolean addAll(int index, Collection else):从index位置将else集合中所有数据插入
Object get(int index):获取index位置的数据
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在当前集合末次出现的位置
Object remove(int index):移除指定index位置的元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
|
1 2 3 4 5 6 7 8 9 10
| 总结常用方法: 增:add(Object obj) 删:remove(int index) / remove(Object obj) 改:set(int index, Object else) 查:get(int index) 插:add(int index, Object else) 长度:size() 遍历:(1)Iterator迭代器 (2)增强for循环 (3)普通循环
|
三、Set
3.1 分类
|—-Collection接口
|—-Set接口:单列集合,用来存储一个一个的对象
|—-HashSet:作为Set接口的主要实现类;线程不安全;可以存放nll值
|—-LinkedSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
|—-TreeSet:可以按照添加对象的指定属性,进行排序
3.2 无序、不可重复性理解
- (1)无序性
- (1)不等于随机性。
- (2)存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值进行存储
- (2)不可重复性
- 保证添加的元素,按照equals()判断时,不能反回true。即:相同的元素只能添加一个
- (3)以HashSet为例,来了解存储过程
- 我们向HashSet中添加元素b,首先调用元素b所在类的hashCode()方法,计算元素b的哈希值
- 此哈希值通过某种算法计算出HashSet底层数组中存放位置(即:索引位置),判断数组在此位置上是否已经有元素:
- 如果此位置上没有其他元素,则元素b添加成功 (情况一)
- 如果此位置上有其他元素b(或以链表形式存在多个元素),则比较元素a与元素b的hash值
- 如果hash值不相同,以链表的形式将元素b添加(情况二)
- 如果hash只相同,进而需要调用元素b所在类的equals()方法:
- equals()返回false,以链表的形式将元素b添加(情况三)
- equals()返回true,则元素b添加失败
- 注意:
- JDK7以链表添加数据,是将新数据放进数组,并用指针指向原数组数据;
- JDK8以链表添加数据,是数组里面的数据,用指针指向新添加的数据
- 口诀:七上八下
- (4)总结
- 向Set中添加数据,其所在的类一定要重写hashCode() 和 equals()
- 重写hashCode() 和 equals() 尽可能保持一致性:相等的对象必须具有相等的散列码
- HashSet底层:数组 + 链表
3.3 LinkedHashSet
- 说明
- LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录次数据前一个数据和后一个数据。本质上和HashSet区别不大
- 优点:对于频繁的遍历操作,LinkedHashSet的效率比HashSet高
3.4 TreeSet
- (1)使用说明
- 向TreeSet中添加的数据,要求是相同类的对象
- 存储的形式类似于二叉树
- 访问数据是从小到大访问,需要用到Java比较器
- (2)比较方法
- 自然排序
- 类实现Comparable接口,重写compareTo()方法
- TreeSet判断是否存储数据是否相同,使用的是compareTo()方法,不再是equals
- 定制排序
- 创建comparator实现类对象,该实现类重写compare()方法,将此对象作为参数传入TreeSet的创建中
- TreeSet判断是否存储数据是否相同,使用的是compare()方法,不再是equals
四、Map
4.1 Map分类
|—-Map:双列数据,存储key-value对的数据 —类似于高中函数:y=f(x)
|—-HashMap:作为主要实现类;线程不安全,效率高;存储null的key和value;
|—-LinkedHashMap:保证在遍历map元素是,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
对于频繁的遍历操作,此类的执行效率高于HashMap
|—-TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序、定制排序
底层使用红黑树
|—-Hashtable:作为古老实现类;线程安全,效率低;不能存储null的key和value;
|—-Properties:常用来处理配置文件。key和value都是String类型
4.2 Map结构理解
- Map中的key是无序的、不可重复的,使用Set存储所有的key —> key所在的类要重写equals()和hashCode()[以hashMap为例]
- Map中的value:无序的。可重复的,使用Collection存储所有的value —> value的所在类要重写equals()
- 一个键值对:key-value构成了一个Entry对象
- Map中的entry:无序的,不可重复的,使用Set存储所有的entry
4.3 底层实现原理
- 基本与HashSet没有太大区别(以HashMap为例):
- 比较key的hash值,看位置上是否有其他数据
- 有就再比较该位置上链表的key的hash值,看是否相同
- key的hash值相同,就调用equals方法
- equals()返回false,添加成功
- equals()返回true,修改该key的value值—不同点
- 在不断添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)。默认的扩容方式:扩容到原来容量的2倍,并将原有的数据复制过来。
4.4 JDK7 和 JDK8 底层区别
- 1.new HashMap():JDK7底层直接创建长度为16的数组;
- JDK8底层没有创建一个长度为16的数组,添加数据时再创建
- 3.JDK7底层结构只有:数组+链表
- JDK8底层结构:数组+链表+红黑树
- 当数组的某一个索引位置上的元素一链表形式存在的数组个数>8,且当前数组长度>64时,此时此索引位置上的所有数组改为使用红黑树
4.5 其他实现类
- LinkedHashMap: 原理与LinkedHashSet无太大差异,就是在HashMap基础上,多两个指针指引数据前后顺序
- TreeMap: 原理与TreeSet无太大差异,key存储的数据必须是同一对象,并提供Java比较器(自然排序,定制排序)来进行对数据的排序
- Properties: 配置文件,涉及文件的读写操作(暂不细讲)
- (1)创建Properties对象
- (2)对象.load(流)//加载流对应的文件
- (3)对象.getProperty(String key)//获取对应key相应的value
- 注意: 配置文件里面的内容为:
"key=value"
,不要在”=”之间添加空格
4.6 常用方法
Map接口定义方法
1 2 3 4 5
| 添加、删除、修改操作: 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中所有的数据
|
1 2 3 4 5 6 7
| 元素查询操作: 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是否相等
|
1 2 3 4
| 元视图操作(用于遍历): Set KeySet():返回所有Key构成的Set集合 Collection value():返回所有value构成的Collection集合 Set entrySet():返回所有Key-value对构成的集合
|
总结
1 2 3 4 5 6 7
| 总结:常用方法 添加:Object put(Object key, Object value) 删除:Object remove(Object key) 修改:Object put(Object key, Object value) 查询:Object get(Object key) 长度:int size() 遍历:Set KeySet() / Collection value() / Set entrySet()
|
五、Collections工具类
5.1 常用方法
1 2 3 4 5 6
| (1)排序操作: void reverse(List):反转List中元素的顺序 void shuffle(List):对List集合元素进行随机排序 void sort(List):根据元素的自然顺序对指定List集合元素按升序排序 void sort(List, Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序 void swap(List, int, int):将指定list集合中的i处元素和j处元素进行交换
|
1 2 3 4 5 6 7 8
| (2)查找、替换 Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素 Object min(Collection):同理,找最小 Object min(Collection, Comparator):同理,找最小 int frequency(Collection, Object):返回指定集合中指定元素的出现次数 void copy(List dest, List src):将src中的内容赋值到dest中 boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值
|
1 2 3
| (3)Collections类提供了多个synchronizedXxx()方法, 该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题 例:List list1 = Collections.synchronizedList(list);
|
5.2 注意点
1 2 3 4
| 错误示范: List list1 = new ArrayList(list.size()); Collections.copy(list1, list); 新建的List中并没有元素,不能进行赋值
|
1 2 3 4
| 正确操作: List list2 = Arrays.asList(new Object[list.size()]); Collections.copy(list2, list); 数组转变成集合,默认赋予null值
|
六、面试题
- 面试题:Collection 和 Collections 的区别?
- Collection是集合的接口
- Collections是操作Collection和Map的工具类
- 面试题:ArrayList、LinkedList、Vector三者的异同?
- 同:三个类都是实现了List接口,存储数据的特点相同:存储有序的,可重复的数据
- 异:
- ArrayList:作为List接口的主要实现类(1.2出现),线程不安全,效率高;底层使用Object[] elementData存储
- Vector:作为List接口的古老实现类(1.0出现),线程安全,效率低;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList效率高。底层使用双向链表存储
- 面试题:HashMap的底层实现原理?
- 存储数据时,先通过获取该数据的key的hash值,通过某种算法计算在数组的位置,查看该位置是否有数据:
- 若无,存储成功。若有,比较该位置上链表元素的hash值是否相同:
- 若相同,链表形式存储(JDK7:头插法;JDK8:尾接法)。若不同,调用equals()方法:
- 若equals()返回false,链表形式存储。若equals返回true,则修改该key上的value值。
- 面试题:HashMap 和 Hashtable的异同?
- 同:双列数据,存储key-value对的数据
- 异:
- HashMap:作为主要实现类;线程不安全,效率高;存储null的key和value
- Hashtable:作为古老实现类;线程安全,效率低;不能存储null的key和value