这段时间一直在总结过去的知识,今天来总结一下Java中常用的集合,在工作中使用最多的可能就在这里
下面来看下Java集合中总的框架结构
Collection系列
Map系列
这张图可以很好的了解各个集合之间的关系
下面是最常用的集合
Java集合大致可以分为Set、List、Queue和Map四种体系
1, Set代表无序、不可重复的集合;
2, List代表有序、重复的集合;
3, Map则代表具有映射关系的集合;
4, Java 5 又增加了Queue体系集合,代表一种队列集合实现
下面开始分别介绍四大派系
Set系列
Collection是父接口,Set,Queue,List都是继承Collection接口的。
HashSet
HashSet是Set接口的实现类,它实现了Set接口中的所有方法,并没有添加额外的方法,大多数时候使用Set集合时就是使用这个实现类。
特点
1,HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
2,不可以存放重复元素,元素存取是无序的
3,集合元素值可以是null。
4, HashSet不是同步的。因此是多线程访问时必须通过代码来同步
4, HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等
注意:HashSet底层是HashMap实现的,HashSet的所有集合元素,构成了HashMap的key,其value为一个静态Object对象。因此HashSet的所有性质,HashMap的key所构成的集合都具备
List 集合
List是一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
ArrayList和Vector
ArrayList和Vector都是List实现类。
ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList或Vector对象使用initalCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超过了该数组的长度时,它们的initalCapacity会自动增加。
因此 ArrayList是一个动态扩展的数组,Vector也同样如此
ArrayList和Vector的区别
1, ArrayList是线程不安全的,Vector是线程安全的。
2, Vector的性能比ArrayList差
LinkedList
LinkedList类也是List的实现类,so可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,可以被当作成双端队列来使用
注意:==LinkedList底层的数据结构是链表结构==
基于链表特性,新增的一些方法
/**
* 将指定元素插入此列表的开头。
*/
public void addFirst(E e) {
throw new RuntimeException("Stub!");
}
/**
* 将指定元素添加到此列表的结尾。
*/
public void addLast(E e) {
throw new RuntimeException("Stub!");
}
/**
* 返回此列表的第一个元素
*/
public E getFirst() {
throw new RuntimeException("Stub!");
}
/**
* 返回此列表的最后一个元素
*/
public E getLast() {
throw new RuntimeException("Stub!");
}
/**
* 在此列表的开头插入指定的元素
*/
public boolean offerFirst(E e) {
throw new RuntimeException("Stub!");
}
/**
* 在此列表末尾插入指定的元素
*/
public boolean offerLast(E e) {
throw new RuntimeException("Stub!");
}
/**
* 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null
*/
public E peekFirst() {
throw new RuntimeException("Stub!");
}
/**
* 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
*/
public E peekLast() {
throw new RuntimeException("Stub!");
}
/**
* 获取并移除此列表的第一个元素;如果此列表为空,则返回 null
*/
public E pollFirst() {
throw new RuntimeException("Stub!");
}
/**
* 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null
*/
public E pollLast() {
throw new RuntimeException("Stub!");
}
/**
* 移除并返回此列表的第一个元素。
*/
public E removeFirst() {
throw new RuntimeException("Stub!");
}
/**
* 移除并返回此列表的最后一个元素
*/
public E removeLast() {
throw new RuntimeException("Stub!");
}
/**
* 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)
*/
public boolean removeFirstOccurrence(Object o) {
throw new RuntimeException("Stub!");
}
/**
* 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)
*/
public boolean removeLastOccurrence(Object o) {
throw new RuntimeException("Stub!");
}
集合 | 数组结构 | 线程安全 | 性能 | 使用场景 |
---|---|---|---|---|
List | 需要保留存储顺序, 并且保留重复元素, 使用List | |||
ArrayList | 动态数组 | 非线程安全 | 随机访问元素性能出色 | 查询较多, 使用ArrayList |
Vector | 动态数组 | 线程安全 | 比ArrayList差 | 需要线程安全,使用Vector |
LinkList | 链表 | 非线程安全 | 在插入、删除元素时性能比较出色但随机访问集合元素时性能较差 | 存取较多,使用LinkedList |
Queue集合
Queue用户模拟队列这种数据结构,队列通常是指“先进先出”(FIFO,first-in-first-out)的容器。队列的头部是在队列中存放时间最长的元素,队列的尾部是保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素
PriorityQueue
- PriorityQueue是Queue的实现类,本质也是一个动态数组,在这一方面与ArrayList是一致的。
- PriorityQueue中的元素可以默认自然排序(也就是数字默认是小的在队列头,字符串则按字典序排列)或者通过提供的Comparator(比较器)在队列实例化时指定的排序方式
特点:
PriorityQueue保存队列元素的顺序不是按加入队列的顺序,而是按队列元素的大小进行重新排序。
所以调用peek()或pool()方法取出队列中头部的元素时,并不是取出最先进入队列的元素,而是取出队列中的最小的元素
PriorityQueue不是线程安全的
不允许插入 null 元素
接下来看下Map,说到Map当然离不开他的两个重要的实现类HashMap和Hashtable
Map集合存放具有映射关系的数据,
因此Map集合里保存着两组数,一组值中保存Map里的key,另一组值中保存Map里的value,key和value都可以是任何引用类型的数据。
特点:Map的key不允许重复,即同一个Map对象中任何两个key通过equals方法比较返回的一定是false
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
理解HashMap本质
public HashMap() {
throw new RuntimeException("Stub!");
}
public HashMap(int initialCapacity) {
throw new RuntimeException("Stub!");
}
public HashMap(int initialCapacity, float loadFactor) {
throw new RuntimeException("Stub!");
}
public HashMap(Map extends K, ? extends V> m) {
throw new RuntimeException("Stub!");
}
构造函数中有两个重要的参数:容量大小(initialCapacity)和加载因子(loadFactor)
容量(initialCapacity)是: 哈希表的容量,初始容量是哈希表在创建时的容量(即DEFAULT_INITIAL_CAPACITY = 1 << 4)
加载因子(loadFactor): 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。默认加载因子是 0.75(即DEFAULT_LOAD_FACTOR = 0.75f),
注:
HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor。
table是一个Node[]数组类型,而Node实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Node数组中的。
size是HashMap的大小,它是HashMap保存的键值对的数量。
threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
loadFactor 为加载因子
HashMap遍历方式
Map map = new HashMap<>();
map.put(1, "acfgdws");
map.put(2, "bwdfrx");
map.put(3, "abrgcs");
map.put(4, "absrfgr");
map.put(5, "abcderf");
/**
* 第一种:通过Map.keySet遍历key和value
*/
for (Integer key : map.keySet()) { //map.keySet()返回的是所有key的值
String str = map.get(key);//得到每个key多对用value的值
Log.d("Map", key + " " + str);
}
/**
*第二种:通过Map.entrySet使用iterator遍历key和value
*/
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
Log.d("Map", "key= " + entry.getKey() + " and value= " + entry.getValue());
}
/** 第三种:推荐,尤其是容量大时 通过Map.entrySet遍历key和value
Map.entry 映射项(键-值对)
map.entrySet() 返回此映射中包含的映射关系的 Set视图。
*/
for (Map.Entry entry : map.entrySet()) {
Log.d("Map", "key= " + entry.getKey() + " and value= " + entry.getValue());
}
/**
* 第四种:通过Map.values()遍历所有的value,但不能遍历key
*/
for (String v : map.values()) {
Log.d("Map", "value= " + v);
}
Hashtable
是一个散列表,它存储的内容是键值对(key-value)映射
构造函数
public Hashtable() {
throw new RuntimeException("Stub!");
}
public Hashtable(int initialCapacity) {
throw new RuntimeException("Stub!");
}
public Hashtable(int initialCapacity, float loadFactor) {
throw new RuntimeException("Stub!");
}
public Hashtable(Map extends K, ? extends V> t) {
throw new RuntimeException("Stub!");
}
从构造函数可以看出和HashMap一样有两个重要的参数:容量大小(initialCapacity)和加载因子(loadFactor)
遍历方式
Hashtable table = new Hashtable<>();
table.put("a", 10);
table.put("b", 20);
table.put("c", 30);
table.put("d", 40);
/**
* 方式1: 键值对遍历entrySet()
*/
for (Map.Entry entry : table.entrySet()) {
String key = entry.getKey();
int value = entry.getValue();
Log.d("Hashtable", "entrySet:" + key + " " + value);
}
/***
* 方式2: key键的遍历
*/
for (String key : table.keySet()) {
int value = table.get(key);
Log.d("Hashtable", "keySet:" + key + " " + value);
}
/**
* 方式3: 通过Enumeration来遍历Hashtable
*/
Enumeration enu = table.keys();
while (enu.hasMoreElements()) {
Log.d("Hashtable", "Enumeration:" + table.keys() + " " + enu.nextElement());
}
HashMap与Hashtable的区别
集合类型 | 数据结构 | 基类 | 线程安全 | 性能 | key或value是否可以为null |
---|---|---|---|---|---|
HashMap | 哈希表来存储键值对 | AbstractMap | 线程安全 | 比Hashtable性能好 | 可以 |
Hashtable | 哈希表来存储键值对 | Dictionary | 不安全 | 多个线程访问同一个map性能好 | 不可以 |
注:
Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作