集合详解(最全)
对各种Java集合做出解释整理主要有一下四种进行
是否线程安全
是否有序
是否是否唯一
是否为空
List:
ArrayList
查询快增删慢线
程不安全 效率高
有序
不唯一
可以为空
LinkedList
查询慢,增删快。
线程不安全,效率高
不唯一
可以为空
有序
Vector
Set:
HashSet
线程不安全
无序
唯一
不为空
LinkedHashSet
线程不安全
有序
唯一
可以为空
TreeSet
线程不安全
有序
唯一
不为空
Map:
HashMap
线程不安全
无序序
唯一
只有一个null
HashTable
线程安全
无序
唯一
不为空
TreeMap
线程不安全
有序
唯一
不为空
LinkedHashMap
线程不安全
有序
唯一
List有序可重复
Vector
和ArrayList几乎一样
Vector线程安全,效率低于ArrayList
ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
因为ArrayList底层是数组实现的,根据下标查询不需要比较,查询方式为,首地址+(元素长度*下标),基于这个位置读取相应的字节数就可以了,所以非常快;增删会带来元素的移动,增加数据会向后移动,删除数据会向前移动,所以影响效率
缺点: 线程不安全,效率高
线程问题:
- 线程安全
AA进程分为A和B, 线程C调用 A,对数据进行操作,线程D调用A对数据进行操作,这个时候线程就会出现不安全,要进行加线程锁,不让D调用A,等C调用完B(AA进程结束时),在让D调用A
-
线程不安全
-
在 Items[Size] 的位置存放此元素; 2. 增大 Size 的值。 在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1; 而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。 那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
LinkedList
优点: 底层数据结构是双向链表,查询慢,增删快。
查询慢:根据索引查询 , 判断索引的长度 集合数据一份两, 所以查询慢
增删快:
缺点: 线程不安全,效率高
和ArrayList一样
Set 无序,唯一 线程不安全
HashSet(无序)
底层数据结构是哈希表。(无序,唯一) HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持,
底层是HasHMap
如何来保证元素唯一性?
1.依赖两个方法:hashCode()和equals()
hashCode(快,效率高,但是不安全)通过Hash表对数据进行判断是否为空
再有equals(慢 ,安全)进行判断
继承HashMap 使用HashMap的key值确保唯一
linkedHashSet(有序)
底层数据结构是双向链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序(插入顺序)
2.由哈希表保证元素唯一
· 底层也是HashMap
·
TreeSet(有序)
底层数据结构是红黑树。(唯一,有序)
\1. 如何保证元素排序的呢?
自然排序
比较器排序
· TerrySet:排序
o 自然顺序:String Integer Char
o 自定义顺序:对象…
在对象类继承
public int compareTo(Student s) {
//return -1; //-1表示放在红黑树的左边,即逆序输出
//return 1; //1表示放在红黑树的右边,即顺序输出
//return o; //表示元素相同,仅存放第一个元素
//主要条件 姓名的长度,如果姓名长度小的就放在左子树,否则放在右子树
int num = this.name.length() - s.name.length();
//姓名的长度相同,不代表内容相同,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
//如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
//如果这两个字符串相等,则结果为 0
int num1 = num == 0 ? this.name.compareTo(s.name) : num;
//姓名的长度和内容相同,不代表年龄相同,所以还要判断年龄
int num2 = num1 == 0 ? this.age - s.age : num1;
return num2;
}
·
2.如何保证元素唯一性的呢?
红黑树决定的
根据比较的返回值是否是0来决定
Map: HashMap、TreeMap、HashTable和LinkedHashMap
总览:
HashMap:数组+链表+红黑树
TreeMap:
HashTable:
LinkedHashMap:
HashMap(无序+唯一):
数组+链表+红黑树
· TreeMap是有序的,HashMap和HashTable是无序的。
- HashMap效率较高,Hashtable效率较低。
如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。
/**
* 程序的入口|主函数;
* @param args
*/
public static void main(String[] args) {
// 初始化数据对象;
HashMap map = new HashMap();
// 数据转载;
map.put(null, "234");
map.put("a", "1223");
map.put(null, "665");
// 获取等式键值集合;
Set set = map.entrySet();
/**
* 打印数据内容;--[null=665, a=1223]
* 根据打印结果可以看出:
* 我们再map中存储的两个key为null的值仅仅打印了一个;
* 并且是最后一个;
* 也就是说:
* hashMap中允许存在key为null的值,只不过只能有一个;
* 后续出现的key为null的值会覆盖前一个;
*/
System.out.println(set);
}
TreeMap(有序+唯一):
底层是红黑树
o 自然顺序:String Integer Char
o 自定义顺序:对象…
在对象类继承
HashTable(无序):
· Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。
同步意味着在一个时间点只能有一个线程可以修改hash表,任何线程在执行HashTable的更新操作前都需要获取对象锁,其他线程需要等带锁的释放。
· Hashtable线程安全,HashMap 和 TreeMap 线程不安全 (Hashtable方法同步线程安全
-
Hashtable是线程安全的,HashMap是线程不安全的。
-
- 原因: 都被synchronized关键字修饰这种
jdk自带的内置锁可以使得被synchronized关键字修饰的方法体和代码块一次只能被一个线程执行,也就保证了线程安全的问题。
- 效率低:
让我们看看HashTable,HashTable本身是个容器,这也就说明了HashTable本身可以不断的放大,试想一下,HashTable如果本身如果存在1000个元素,那么在get()方法中就会将这1000个元素完全锁住,期间其他任何线程都得等待。这样就会造成容器越大,对容器数据操作的效率将越低。
-
Hashtable不允许null值,HashMap允许null值(key和value都允许,key只可以有一个,如果key有两个null值,第二个null值可以代替第一个null值)
-
父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap
LinkedHashMap(有序+唯一):
HashMap(数组+链表+红黑树)+双向链表(保证顺序)
LinkedHashMap有序,可分为插入顺序和访问顺序两种。
访问顺序:,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。
插入顺序: 调用get(key), 或者put(key, value)并不会对线性结构产生任何的影响。
LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
LinkedHashMap是线程不安全的。
红黑树(只要用到就是唯一的)
红黑树的主要特性:
(1)每个节点要么是黑色,要么是红色。(节点非黑即红)
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。(也就是说父子节点不能同时为红色) (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。(这一点是平衡的关键)
说简单也简单,其实就是一颗比较平衡的又红又黑的二叉树嘛。
TreeSet红黑树:
使用二叉树存储数据
//第一次存储数据没有树根,就创建一个树根,把这个元素赋值给树根;
//第二次进行存储的时候,首先和根节点的元素进行比较,比较有三种情况:
//第一种情况当前元素和根节点的元素相同,这是忽略该值,不存储该值;
//第二种情况当前元素和根节点的元素相比,小于根节点元素,那么将该元素放在根节点的左边
//第三种情况当前元素和根节点的元素相比,大于根节点元素,那么将该元素放在根节点的右边
举例:
存储下列元素: 20,18,23,22,17,24,19,18,24
treeSet.add(20);
treeSet.add(18);
treeSet.add(23);
treeSet.add(22);
treeSet.add(17);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rrSHbxf0-1597245100141)(file:///C:/Users/20297/AppData/Local/Temp/msohtmlclip1/01/clip_image014.png)]从上图可以看出,相同的元素18,24并没有存进集合中,并且存进去的元素 已经从小到大排好了序,所以TreeSet保证了元素的唯一以及有序。
HashMap红黑树:
HashMap是数组+链表+红黑树
因为红黑树需要进行左旋,右旋操作, 而单链表不需要,
如果元素小于8个,查询成本高,新增成本低(数组+链表)
如果元素大于8个,查询成本低,新增成本高(数组+链表+红黑树)
当数据大于链表的0.75(扩容因子)就会扩容2倍
7);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
[外链图片转存中...(img-rrSHbxf0-1597245100141)]从上图可以看出,相同的元素18,24并没有存进集合中,并且存进去的元素 已经从小到大排好了序,所以TreeSet保证了元素的唯一以及有序。
## HashMap红黑树:
HashMap是数组+链表+红黑树
因为红黑树需要进行左旋,右旋操作, 而单链表不需要,
如果元素小于8个,查询成本高,新增成本低(数组+链表)
如果元素大于8个,查询成本低,新增成本高(数组+链表+红黑树)
当数据大于链表的0.75(扩容因子)就会扩容2倍