1、Coolection
1 概述
英文名称Collection,是用来存放对象的数据结构。其中长度可变,而且集合中可以存放不同类型的对象,并提供了一组操作成批对象的方法。
数组的缺点:长度固定不可变,访问方式单一,插入,删除等操作繁琐。可以说集合的出现就是为了弥补数组的缺陷
2 常用集合的结构关系
- 把共性的东西向上提取,最终形成容器的继承体系结构
3 子类特点
- List接口:数据有序,可以重复
- Set接口:数据无序,不可以存重复值
4 常用方法
返回值 | 方法 | 功能 |
---|---|---|
boolean | add(E e) | 确保此 collection 包含指定的元素(可选操作)。如果此 collection 由于调用而发生更改,则返回 true |
boolean | addAll(Collection<? extends E> c | 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
void | clear() | 移除此 collection 中的所有元素(可选操作)。 |
boolean | containsAll(Collection<?> c) | 如果此 collection 包含指定的元素,则返回 true。 |
boolean | containsAll(Collection<?> c) | 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
boolean | equals(Object o) | 返回此 collection 的哈希码值 |
boolean | isEmpty() | 如果此 collection 不包含元素,则返回 true。 |
Iterator | iterator() | 返回在此 collection 的元素上进行迭代的迭代器。 |
boolean | remove(Object o) | 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。 |
boolean | removeAll(Collection<?> c) | 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。 |
int | size() | 返回此 collection 中的元素数。 |
Object[] | toArray() | 返回包含此 collection 中所有元素的数组。 |
T[] | toArray(T[] a) | 返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 |
5 示例
public class test1113 {
@Test
public void test1(){
/*这个Object表示存入的类型必须是对象,不能是基本类型
*同时泛型里面也不能存入基本类型
*/
Collection <Object>c = new ArrayList();
c.add("学习");
c.add(1);//这里的1包装类,因为jdk5之后又自动装箱功能
//相当于使用了Integer.valueOf(10)
c.add(2.2f);
System.out.println(c);//输出c集合的内容
System.out.println(c.contains("学习"));//查看集合是否包含元素
System.out.println(c.size());//看集合元素的个数
System.out.println(c.remove(1));//移除元素的一个元素
System.out.println(c);
c.clear();//清除集合的类容
System.out.println(c);
}
}
//测试结果
[学习, 1, 2.2]
true
3
true
[学习, 2.2]
[]
6 迭代器
迭代器(Iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表和数组)上编访接口,设计人员无需关心关心对象的内存分配细节。
各种语言实现迭代器的方式皆不尽同,但有Java将迭代器的特性内置到语言当中,完美的跟语言集成
6.1 Iterator的方法
返回值 | 方法 | 功能 |
---|---|---|
boolean | hasNext() | 如果仍有元素可以迭代,则返回 true。 |
E | next() | 返回迭代的下一个元素 |
void | remove() | 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作) |
6.2 为什么使用迭代器
集合数组等容器内部结构不同,很多时候不知如何去遍历一个容器中的元素,所以为了使对容器中的元素操作更简单,因此才引入了迭代器,同时提取共性将迭代封装为一个接口,实现容器的遍历,这样能避免对外暴露容器的内部结构,到达程序员无序关心容器对象分配的实现细节的目的。
6.3 细节
迭代器在迭代之前就创建好了,这时会产生迭代器的版本号expectedModCount,集合也会有版本号modCount,如果对集合进行操作,会产生新的版本号,如果在使用迭代器的过程又对集合进行了操作,版本号冲突就会怕抛出ConcurrentModificationException,这是并发操作的错误
6.4 参考博客
二、List接口 集合
1 概念
有序的Collection(也称为序列),此接口的用户可以对列表中每个元素插入位置进行精确的控制。用户可以根据索引(在列表中的位置)访问元素,并搜索列表中的元素。
2 特点
- 数据有序
- 允许元素重复
- 元素都有索引(意味着查询效率高)
3 常用方法
返回值 | 方法 | 功能 |
---|---|---|
ListIterator | listIterator() | 返回此列表元素的列表迭代器(按适当顺序)。 |
ListIterator | listIterator(int index) | 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 |
boolean | add(E e) | 向列表的尾部添加指定的元素(可选操作)。 |
void | add(int index, E element) | 在列表的指定位置插入指定元素(可选操作)。 |
boolean | addAll(int index, Collection<? extends E> c) | 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
List | subList(int fromIndex, int toIndex) | 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
E | get(int index) | 返回列表中指定位置的元素。 |
int | indexOf(Object o) | 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
4 简单的实例
public class Test1114 {
@Test
public void test1(){
List<Object> list = new ArrayList<>();
list.add(111);
list.add(222);
list.add(333);
list.add("学习");
list.add("努力");
list.add(9.999f);
System.out.println(list);
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println(list.indexOf("努力"));
System.out.println(list.indexOf(111));//这两个获取索引的指令说明了List时有序的
System.out.println(list.subList(list.indexOf(222), list.indexOf("学习")));
}
}
//执行结果
[111, 222, 333, 学习, 努力, 9.999]
111
222
333
学习
努力
9.999
4
0
[222, 333]
二 1、ArrayList集合——Listh接口的实现类
1 概述
- 存在于java.util包中
- 内部用数组存放数据,封装了数组的操作,每个对象都有下标
- 内部数组的默认初始容量时10,如果容量不够会以1.5倍扩容。
- 查询快,增删数据效率会降低
- 线程安全,单线程使用,多线程会使用Vector
这里的>>2向有右移动一位,数值减小,乘以2^-1,<<1表示向左移动一位,数值增大,乘以2 ^1
2 创建对象
创建方式 | 功能 |
---|---|
ArrayList() | 构造一个初始容量为 10 的空列表。 |
ArrayList(Collection<? extends E> c) | |
ArrayList(int initialCapacity) | 构造一个具有指定初始容量的空列表。 |
3 常用方法
返回值 | 方法 | 功能 |
---|---|---|
boolean | add(E e) | 将指定的元素添加到此列表的尾部 |
void | add(int index, E element) | 将指定的元素插入此列表中的指定位置。 |
boolean | addAll(Collection<? extends E> c) | 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 |
void | clear() | 移除此列表中的所有元素。 |
Object | clone() | 返回此 ArrayList 实例的浅表副本。 |
boolean | contains(Object o) | 如果此列表中包含指定的元素,则返回 true。 |
E | get(int index) | 返回此列表中指定位置上的元素。 |
int | indexOf(Object o) | 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。 |
boolean | isEmpty() | 如果此列表中没有元素,则返回 true |
int | lastIndexOf(Object o) | 返回最后一次出现指定元素的索引,如果此列表不包含索引则返回-1 |
E | remove(int index) | 移除此列表中首次出现的指定元素(如果存在) |
boolean | remove(Object o) | 移除此列表中首次出现的指定元素(如果存在)。 |
protected void | removeRange(int fromIndex, int toIndex) | 移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。 |
E | set(int index, E element) | 用指定的元素替代此列表中指定位置上的元素。 |
int | size() | 用指定的元素替代此列表中指定位置上的元素。 |
T[] | toArray(T[] a) | 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 |
4 ArrayList体会
- add(int index,E element),和remove都会设计到数组的复制,也就是System.arraycopy()方法,因此ArrayList的增删慢
#5 示例
基础应用
public class Test1114 {
@Test
public void test1(){
ArrayList<Object> list = new ArrayList<>();
list.add("学习");
list.add("努力");
list.add(111);
list.add(222);
list.add(9.99f);
System.out.println(list);
System.out.println(list.size());//集合长度
System.out.println(list.get(0));//根据索引去取元素
System.out.println(list.indexOf("努力"));//根据元素区索引
System.out.println(list.remove((Float)9.99f));//更具元素删除
System.out.println(list);
}
//输出结果
[学习, 努力, 111, 222, 9.99]
5
学习
1
true
[学习, 努力, 111, 222]
集合遍历方式
public class Test1114 {
@Test
public void test1(){
ArrayList<Object> list = new ArrayList<>();
list.add("学习");
list.add("努力");
list.add(111);
list.add(222);
list.add(9.99f);
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()){
//没有iterator的话指针不会往下跳转,会形成一个死循环
System.out.println(iterator.next());
}
for (int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
for(Object a: list){
System.out.println(a);
}
}
}
二.2、LinkedList集合——List接口的实现类
1 概述
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端
2 特点
底层是双向链表,两端的效率高,增删比ArrayList效率高,但查询的效率比ArrayList的要低,和ArrayList一样,线程是不安全的
3 常用方法
返回值 | 方法 | 功能 |
---|---|---|
boolean | add(E e) | 将指定元素添加到此列表的结尾。 |
E | get(int index) | 返回此列表中指定位置处的元素。 |
int | size() | 返回此列表的元素数。 |
int | remove(int index) | 移除此列表中指定位置处的元素。 |
boolean | remove(Object o | 从此列表中移除首次出现的指定元素(如果存在)。 |
Iterator | iterator() | 返回在此列表中的元素上进行迭代的迭代器(按适当顺序)。 |
boolean | addFirst() addLast() | 将指定元素插入此列表的开头/结尾。 |
E | getFirst() getLast() | 返回此列表的第一个/最后一个元素 |
E | removeFirst() removeLast() | 移除并返回此列表的第一个/最后一个元素。 |
E | pollFirst() pollLast() | 获取并移除此列表的第一个/最后一个元素;如果此列表为空,则返回 null |
4 LinkedList的遍历
LinkedList的底层时双向链表,双向链表的遍历迭代器效率高,下标遍历的效率低。
5 数组和链表的区别
List是一个接口,它有两个常用的子类,ArrayList和LinkedList,看名字就可以看得出一种是基于数组实现另一个是基于链表实现的。
数组ArrayList遍历快,因为存储空间连续;链表LinkedList遍历慢,因为存储空间不连续,要去通过指针定位下一个元素,所以链表遍历慢。
数组插入元素和删除元素需要重新申请内存,然后将拼接结果保存进去,成本很高。例如有100个值,中间插入一个元素,需要数组重新拷贝。而这个动作对链表来说,太轻松了,改变一下相邻两个元素的指针即可。所以链表的插入和修改元素时性能非常高。
实际开发就根据它们各自不同的特点来匹配对应业务的特点。业务一次赋值,不会改变,顺序遍历,就采用数组;业务频繁变 化,有新增,有删除,则链表更加适合。
三、Set接口
1 概述
-
一个不包含重复元素的 collection。
-
数据无序(因为set集合没有下标)。
-
由于集合中的元素不可以重复。常用于给数据去重。
2 特点
-
HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复,也就是利用了HashMap的key值不能重复的原理实现了元素的不重复
-
TreeSet:底层就是TreeMap,也是红黑树的形式,便于查找数据。
-
HashMap实现中,当哈希值相同的对象,会在同一个hash值的位置存储不同属性的数据
3 常用方法
boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
三.1 HashSet集合
1 概述
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
2 示例
public class Test0_Map {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add("a");
set.add("e");
set.add("b");
set.add("a");
set.add("b");
System.out.println(set);//无序,不重复
Iterator it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
四、Map 接口
1、概述
java.util接口Map<K,V>
类型参数:K-此映射所维护的键的类型V -映射值的类型。
也叫哈希表、散列表。常用于存键值对结构数据。其中的键不能重复,值可以重复。
2、特点
- 可以根据键提取对应的值,这是相对于Collection的优势之一
- 键不允许重复,如果重复会被覆盖
- 存放的都是无序数据
- 初始容量是16,默认的加载因子是0.75
3、常用方法
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Set<Map.Entry<K,V>> entrySet()
返回此映射所包含的映射关系的 Set 视图。
4、示例
public class Test0_Map {
public static void main(String[] args) {
Map map = new HashMap ();
map.put("001", "钢铁侠");
map.put("002", "蜘蛛侠");
map.put("003", "绿巨人");
map.put("004", "灭霸");
map.put("005", "美国队长");
map.put("005", "凤姐");
System.out.println(map.containsKey("001"));
System.out.println(map.containsValue("美国队长"));
System.out.println(map.isEmpty());
System.out.println(map.get("003"));
System.out.println(map.remove("001"));
System.out.println(map.size());
Map map2 = new HashMap ();
map2.put("999", "刘德华");
map.put(null,null);//可以存入键为null,值也null的数据
map.putAll(map2);
System.out.println(map);
//keySet()返回键的set集合,把map的key形成set集合
Set set = map.keySet();
System.out.println(set);
//map集合的遍历,
//方式1:keySet():把map中的可以放入set集合
//遍历方式1:keySet ()
Set set = m.keySet();
Iterator it = set.iterator();
while(it.hasNext()) {
String key = (String) it.next();
String val = (String) m.get(key);
System.out.println(key+"="+val);
}
//遍历方式2:entrySet ()
Set set2 = m.entrySet();
Iterator it2 = set2.iterator();
while(it2.hasNext()) {
Entry en = (Entry) it2.next();
String key = (String) en.getKey();
String value = (String) en.getValue();
System.out.println(key+"=="+value);
}
}
}
四.1 HashMap
HashMap的键要同时重写hashCode()和equals()
hashCode()用来判断确定hash值是否相同
equals()用来判断属性的值是否相同
– equals()判断数据如果相等,hashCode()必须相同
– equals()判断数据如果不等,hashCode()尽量不同
1 概述
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。算法:hash(key)%n,n就是数组的长度。
当计算的位置没有数据时,就直接存放,当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
2 示例
public class Test0_Map {
public static void main(String[] args) {
HashMap map = new HashMap ();
map.put(100, "刘德华");
map.put(101, "梁朝伟");
map.put(102, "古天乐");
map.put(103, "周润发");
//遍历方式1:keySet ()
Set set = m.keySet();
Iterator it = set.iterator();
while(it.hasNext()) {
String key = (String) it.next();
String val = (String) m.get(key);
System.out.println(key+"="+val);
}
//遍历方式2:entrySet ()
Set set2 = m.entrySet();
Iterator it2 = set2.iterator();
while(it2.hasNext()) {
Entry en = (Entry) it2.next();
String key = (String) en.getKey();
String value = (String) en.getValue();
System.out.println(key+"=="+value);
}
}
}
- HashMap的遍历
利用构造器,不过在使用构造器之前,要使用keySet()和EntrySet(),两种方法,将HashMap为
public class test1111 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("test1", "1");
map.put("test2", "2");
map.put("test3", "3");
map.put("test4", "4");
map.put("test5", "5");
map.put("test6", "6");
System.out.println(map);
test1(map);
test2(map);
}
public static void test1(HashMap map) {
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry en = (Map.Entry)iterator.next();
String key = (String)en.getKey();//获取Entry映射项的键
String value = (String) map.get(key);//获取Entry映射项的值
System.out.println(en+"返回的是一个Entry");//结果形式test5=5
System.out.println("key="+key+" "+"value="+value);
}
}
public static void test2(HashMap map) {
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
String key = (String)iterator.next();
String value =(String) map.get(key);
System.out.println("key="+key+" "+"value="+value);
}
}
}
- HashMap的小测试
输入字符,测试每个字符的数目
public class test1111 {
public static void main(String[] args) {
System.out.println("请输入字符");
String s = new Scanner(System.in).nextLine();
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
Integer count = map.get(c);
if (count==null){
map.put(c,1);
}else {
map.put(c, count+1);
}
}
System.out.println(map);
}
3 拓展
接口 Map.Entry<K,V>
3.1 概念
映射项(键-值对)。Map.entrySet 方法返回映射的 collection 视图,其中的元素属于此类。获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。这些 Map.Entry 对象仅 在迭代期间有效;更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过 setValue 在映射项上执行操作之外。
可以浅显的理解为存着key和value,的一个容器,只能在迭代器使用的时候有效
3.2 常用方法
boolean equals(Object o)
比较指定对象与此项的相等性。
K getKey()
返回与此项对应的键。
V getValue()
返回与此项对应的值。
int hashCode()
返回此映射项的哈希码值。
V setValue(V value)
用指定的值替换与此项对应的值(可选操作)。
五、数组和集合的工具类
1、Arrays
1.1 概念
此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。
工具类里面的里面方法是是静态的方法,不是静态的类
1.2 Arrays.tostring(数组)
把数组里的数据,用逗号连接成一个字符串,格式:[1,2,3,4,5,6].(和直接输出集合的效果一样)
1.3 Arrays.sort(数组)
对数组排序,对于基本类型的数组使用优化后的快速排序算法,效率高
对应用类型,使用优化后的合并排序算法。
1.4 Arrays.copyOf(数组,新的长度)
把数组赋值成一个指定长度的新数组
新数组的长度大于原数组,相当于复制,并增加位置。--数组的扩容
新数组的长度小于原数组,相当于截取前一部分数据。--数组的缩容
1.5 asList(T… a) 返回值 static < T > List< T >
返回一个受指定数组支持的固定大小的列表。
1.6 方法的具体体现
int[] a = Arrays.copyOf(arr, 10);//数组的复制,大于原来长度相当于扩容
System.out.println(Arrays.toString(a));//[12, 30, 20, 90, 34, 0, 0, 0, 0, 0]
System.out.println(a.length);//10
int[] a2 = Arrays.copyOf(arr, 3);//数组的复制,晓宇原来长度相当于截取前几个数据
System.out.println(Arrays.toString(a2));//[12, 30, 20]
System.out.println(a2.length);//10
2、Collections
2.1 概念
此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。它包含在 collection 上操作的多态算法,即“包装器”,包装器返回由指定 collection 支持的新 collection,以及少数其他内容。
2.2 常用方法
Collections.sort(List<> list)
根据元素的自然顺序 对指定列表按升序进行排序。
Collections.max():
根据元素的自然顺序,返回给定 collection 的最大元素。
Collections.min():
根据元素的自然顺序 返回给定 collection 的最小元素。
Collections.swap(List,i,j):
在指定列表的指定位置处交换元素。
Collections.addAll():
将所有指定元素添加到指定 collection 中。可以分别指定要添加的元素,或者将它们指定为一个数组,此便捷方法的行为与 c.addAll(Arrays.asList(elements)) 的行为是相同的,但在大多数实现下,此方法运行起来可能要快得多
六、拓展
1 数组和集合的区别
- 数组的大小是固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型)
- Java集合可以存储和操作数目不固定的一组数据
- 若程序不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用集合,数组不在适用
集合数组可以使用toArray()和Arrays.asList()方法进行相互的转换
2 HashMap实现原理
思考工作原理就要考虑HashMap的原理,他是一个存储数据的工具,存的是Key——value结构的数据,因此就要从它的put和get方法说起;
1 put方法的原理
- 先对key的hashCode()做hash计算,最后取余,计算出index
- 如果没有发生碰撞,直接放到buket里
- 如果发生了碰撞就会以链表的形势存在于bukets后,以后再次碰撞的节点作为头节点
- 如果链表的长度大于8并且HashMap的容量大于64,链表就会变为红黑树
- 如果节点已经存在就会覆盖(保证唯一性)
- 如果bucket超过了容量和加载因子的乘积就会扩容
2 get方法的原理
- 根据key值计算hashcode取出value,如果在bucket里的第一个节点。直接命中
- 如果有冲突,通过key.equals(k)去查找对应的entry,如果在树中事件复杂度为O(logn)。如果再表中,事件复杂度为O(n)
3 集合的扩容
ArrayList
初始容量:10
扩容:oldCapacity + (oldCapacity >> 1) ,即扩容1.5倍
Vector
初始容量:10
加载因子:1
扩容 :2倍
HashMap和HashSet
初始容量:16
加载因子:0.75
扩容:oldThr << 1 ,即两倍
Hashtable
初始通量定义:capacity(11)
加载因子:0.75
扩容:int newCapacity = (oldCapacity << 1) + 1,即2倍+1
StringBuffer和StringBuilder
初始容量:16
加载因子:0.75
扩容:value=value*2+2,即2倍+2
4 集合的线程安全
- 线程安全
Hashtable和HashMap结构类似,只是public方法上加上了锁,同时key和value都不能存null
ConcurrentHash和hashtable类似的,加了更细密度的锁
Vector 和ArrayList类似但是多了同步机制,是线程安全的
Stack:栈,线程安全,继承于Vector
-线程不安全
HashMap
ArrayList
LinkedList