Java——集合
Java——集合
集合优点:
- 可以动态保存任意多个元素,且类型不限
- 提供一系列操作方法:add、remove、set、get等
集合分类:
- 单列集合(Collection)分为 List (ArrayList、LinkedList、Vector)和 Set (HashSet、TreeSet)
- 双列集合(Map): HashMap(使用频率最高) 、 TreeMap 、Hashtable 、 Properties
Collection接口
Collection常用方法
1 | CollectionMethod |
Collection接口遍历元素
1 | 方法一 : Iterator (迭代器) |
如果集合中存放的是对象元素,对集合遍历时,需要对象调用方法时,应注意向下转型(Object—>)
List接口
List接口是Collection接口的子接口
- List集合类中元素有序(添加顺序和取出顺序一致)、且可以重复
- List集合中的每一个元素都有对应的顺序索引
1 | 1、List集合类中元素有序(添加顺序和取出顺序一致)、且可以重复 |
ArrayList底层操作机制
ArrayList中维护了一个Object类型的数组elementData
transient Object[] elementData;
其中
transient修饰成员变量时,意味着该成员变量不会参与序列化过程。序列化:序列化是将对象的状态转换为字节流的过程,以便将其保存到文件、数据库或通过网络传输。
当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加时,则扩容elementData为10,如需再次 扩容,则扩容elementData为1.5倍
如果使用的是指定大小的构造器,则初始elementData容量大小为指定大小,如果需要扩容,则直接扩容到1.5倍
LinkedList
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表
- 所以LinkedList的元素添加和删除,不是通过数组完成的,相对来说效率较高
如何选择ArrayList和LinkedList:
- 如果改查操作比较多,选择ArrayList
- 如果增删操作比较多,选择LinkedList
- 大部分情况下查询比较多,因此一般选择ArrayList,也可以一个项目中,一个模块使用ArrayList,另一个模块使用LinkedList
ArrayList、LinkedList和Vector的区别
Java中的ArrayList、LinkedList和Vector都是用于存储集合元素的类,但它们在实现上有一些区别。
- 线程安全性:
ArrayList:是非线程安全的。在多线程环境下,如果不进行额外的同步处理,可能会导致并发问题。LinkedList:同样是非线程安全的。Vector:是线程安全的。它通过在每个方法上使用synchronized关键字来实现同步,因此可以在多线程环境中使用,但这也使得性能相对较低。
- 底层数据结构:
ArrayList:底层数据结构是动态数组,支持随机访问元素。LinkedList:底层数据结构是双向链表,不支持直接的随机访问,需要从头或尾开始遍历。Vector:底层数据结构也是动态数组,与ArrayList类似。
- 性能:
ArrayList:由于底层是数组,因此对于随机访问和按索引位置添加/删除元素的操作性能较好。但在插入和删除中间元素时,可能需要移动大量元素。LinkedList:在插入和删除中间元素时性能较好,因为只需修改相邻节点的引用。但对于随机访问性能较差。Vector:由于是线程安全的,性能相对较低,不推荐在单线程环境中使用,可以考虑使用ArrayList代替。
- 增长策略:
ArrayList和Vector:在元素数量超过当前容量时,它们会重新分配一个更大的数组,ArrayList通常1.5倍,Vector通常两倍。LinkedList:没有预定义的容量,每次插入都会动态分配内存。
- 迭代器(Iterator):
ArrayList和Vector:支持通过迭代器(Iterator)进行遍历。LinkedList:由于是双向链表,还支持从头到尾或从尾到头的遍历。
总体而言,选择使用哪个类取决于具体的需求。如果需要快速随机访问元素,且不涉及多线程操作,ArrayList是一个不错的选择。如果需要频繁插入和删除元素,或者需要支持多线程操作,可以考虑使用LinkedList或Vector,具体取决于对多线程支持的需求。在单线程环境下,通常推荐使用ArrayList。
Set接口
- 无序(添加和取出的顺序不一致,但取的时候位置固定),没有索引
- 不允许有重复的数据(取决于地址),最多包含一个null
- 用的比较多的是
HashSet和TreeSet
Set接口常用方法
Set常用方法和List一样
Set接口遍历方式
- 可以使用迭代器
- 增强for
- 不能使用索引的方式来获取
1 | Set set = new HashSet(); |
HashSet说明
- HashSet实现了Set接口(无序,不重复,无索引)
- HashSet实际上是HashMap
1 | HashSet set = new HashSet(); |
HashSet底层机制说明
- HashSet底层是HashMap
- 添加一个元素时,先得到hash值,然后将hash值转化为索引值
- 找到存储表table,看这个索引位置是否已经存放元素
- 如果没有,直接加入
- 如果有,调用equals比较,如果相同,就放弃添加,如果不同则添加到最后
- 在Java8中,如果一条链表的元素个数超过了TREEIFY_THRESHOLE(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
- 第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子,(loadFactor)是0.75 = 12
- 如果table数组使用到了临界值12,就会扩容到16*2 = 32,新的临界值就是32 *0.75 = 24,依次类推
LinkedHashSet
LinkedHashSet是HashSet的子类
LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序存储的。
LinkedHashSet不允许添加重复元素
在LinkedHashSet中维护了一个表和双向链表(LinkedHashSet中有head和tail)
每个结点中有pre和next属性,可以形成一个双向链表
在添加一个元素时,先求hash值,再求索引,确定该元素在table中的位置,然后将要添加的元素加入到双向链表中(需要用equals判断,如果规定的equals方法重复,则不能添加)
1 | tail.next = newElement; |
第一次添加时,直接将数组table扩容到16
HashMap$Node[] 中存放LinkedHashMap$Entry, 因为Entry extends HashMap;
TreeSet
TreeSet基于红黑树数据结构实现,因此具有有序性和高效的插入、删除和查找操作。
特性:
- 有序性(Ordered):
TreeSet是有序的集合,它按照元素的自然顺序或者使用提供的比较器(Comparator)进行排序。这使得元素在集合中以特定的顺序存储。 - 唯一性(Unique):
TreeSet不允许重复元素。如果试图插入重复元素,插入操作将被忽略。 - 基于红黑树(Red-Black Tree):
TreeSet使用红黑树作为底层数据结构,这确保了在对数时间内执行插入、删除和查找操作。
TreeSet比较器:
当使用无参构造器,创建TreeSet时,按从小到大排序
1
2
3
4
5
6
7
8TreeSet treeSet = new TreeSet();
treeSet.add("abc");
treeSet.add("zzz");
treeSet.add("asdfg");
treeSet.add("aaaa");
treeSet.add("a");
System.out.println(treeSet);
结果: [a, aaaa, abc, asdfg, zzz]当希望按照自定义方式进行排序时,可以用Comparator
1
2
3
4
5
6
7
8
9
10
11
12
13TreeSet treeSet = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
return ((String)o2).compareTo((String)o1);
}
});
treeSet.add("abc");
treeSet.add("zzz");
treeSet.add("asdfg");
treeSet.add("aaaa");
treeSet.add("a");
System.out.println(treeSet);
结果: [zzz, asdfg, abc, aaaa, a]1
2
3
4
5
6
7
8//按长度排序
TreeSet treeSet = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
return ((String)o2).length()-((String)o1).length();
}
});
[asdfg, aaaa, abc, a]
Map接口
Map接口实现类特点
- Map与collection并列存在,用于保存具有映射关系的数据 key-value
- Map中的key和value可以是任意引用类型的数据,会封装在HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样,但如果key重复,则用新的value代替旧的
- Map的value可以重复
- Map的key可以为null,但只能有一个,value也可以为null,可以有很多个
- 常用String类作为Map的key, (但其类型其实为 OBJECT,所以object类均可)
- key和value之间存在单向一对一的关系,即通过指定的key总能找到与之对应的value
- 一对k-v是放在一个HashMap$Node中,Node实现了Entry接口
Map接口常用方法
1 | Map hashMap = new HashMap(); |
Map遍历方法
- containsKey:查找键是否存在
- keySet:获取所有的键
- entrySet:获取所有关系k-v
- values:获取所有的值
1 | Map hashMap = new HashMap(); |
HashMap
- HashMap是Map接口使用频率最高的实现类
- 与HashSet一样,不保证映射顺序,因为底层是以hash表的方式存储的
- HashMap没有实现同步,因此是线程不安全的
底层机制
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75
- 当添加k-v时,通过key的哈希值得到下table 的索引,然后判断该索引处是否有元素,如果没有元素直接添加,如果该索引处有元素,则继续判断该元素的key是否和准备加入的key值一样,如果相等,则直接替换val,如果不相等需要判断是树结构还是链表结构,做出 相应的处理,如果添加时发现容量不够,则需要扩容
- 第一次添加时,扩容table容量为16,临界值(threshold)为12(16*0.75)
- 以后再次扩容,则需要扩容table容量为原来的两倍,临界值为原来的两倍,即24,以此类推
- 在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACIYU(默认64),就会树化(红黑树)
HashTable
- 存放的元素是键值对:即k-v
- hashTable的键和值都不能为null,否则会抛出NullPointerException
- hashTable使用方法基本上和HashMap一样
- hashTable是线程安全的,hashMap线程不安全
Properties
- Properties类继承自Hashtable类并实现了Map接口,也是使用一种键值对的形式来保存数据
- 使用特点和Hashtable类似
- Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
- 说明:工作后 xxx.properties文件通常作为配置文件
TreeMap
和TreeMap基本一致,此处元素为键值对。
Collections工具类
- Collections 是一个操作 Set 、List 和 Map 等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询、修改等操作
排序操作:
- reverse(List):反转 List 中元素的顺序
- shuffle(List):对List 集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
- sort(List,Compartor):根据指定的Compatator产生的顺序对List集合元素进行排序
- swap(List,int,int):将指定List 集合中的i处元素和j元素进行交换
查找、替换操作:
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Compartor):根据Compartor指定的顺序,返回给定集合中的最大元素
- Object min(Collection)
- Object min(Collection,Compartor)
- int frequency (Collection,Object):返回指定集合中指定元素出现的次数
- void copy (List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
小结:开发中如何选择集合实现类
主要取决于业务操作特点,然后根据集合实现类特性进行选择
先判断存储的类型(一组对象或一组键值对)
一组对象:Collection接口
允许重复:List 增删多:LinkedList(底层双向链表)
改查多:ArrayList(底层Object 类型的可变数组)
不允许重复:Set
无序:HashSet(底层HashMap,哈希表,数组+链表+红黑树)
排序:TreeSet
插入和取出顺序一致:LinkedHashSet(数组+双向链表)
一组键值对:Map
键无序:HashMap(底层:哈希表,jdk7:数组+链表,jdk8:数组+链表+红黑树)
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties



