一文详解集合的不同之处 置顶!
序言
集合这个东西吧,说简单也简单,但是你想把它用溜呢,也还是需要下点功夫的。
Collection分两种:
List
元素是有序的,元素可以重复,因为该集合体有索引
ArrayList:
- 底层数据结构是数组,查询快,增删慢。
- 线程不安全,效率高。
- 当元素放满了后,默认以原长度的50%+1的长度加长集合容器的长度。
Vector:
- 底层数据结构是数组,查询快,增删慢。
- 线程安全,效率低。
- 当元素放满了后,默认以原长度100%的长度加长集合容器的长度
LinkedList:
- 底层数据结构是链表,查询慢,增删快。
- 线程不安全,效率高。
- Vector(线程安全的)相对ArrayList查询慢
- Vector相对LinkedList增删慢(数组结构)
Set:
元素是无序的,元素不可以重复。
- a)、HashSet:不能保证元素的排列顺序,线程不同步。
- b)、TreeSet:可以set集合中的元素进行排序,线程不同步。
使用注意事项:
使用Vector举例:
public class VectorDemo {
public static void main(String[] args) {
testone();
testtwo();
}
public static void testone() {
// 初始大小为3;自动扩容增量为2
Vector v = new Vector(3, 2);
System.out.println("初始化大小: " + v.size());
System.out.println("初始化容量: " +
v.capacity());
v.addElement(new String("one"));
v.addElement(new Integer(1));
v.addElement(new Integer(2));
v.addElement(new Integer(3));
System.out.println("增加4个元素后的容量: " + v.capacity());
Enumeration vEnum = v.elements();
while(vEnum.hasMoreElements())
//依次输出Vector数组中的元素
System.out.print(vEnum.nextElement() + " ");
System.out.println();
}
public static void testtwo() {
// 初始大小为3;默认自动扩容增量100%,此处即为3
Vector v = new Vector(3);
System.out.println("初始化大小: " + v.size());
System.out.println("初始化容量: " +
v.capacity());
v.addElement(new String("two"));
v.addElement(new Integer(2));
v.addElement(new Integer(3));
v.addElement(new Integer(4));
System.out.println("增加4个元素后的容量: " +
v.capacity());
Enumeration vEnum = v.elements();
while(vEnum.hasMoreElements())
//依次输出Vector数组中的元素
System.out.print(vEnum.nextElement() + " ");
System.out.println();
}
}
使用List举例
问题:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
- 1)请问上述操作如何?
- 2)若把list.remove(item)换成list.add(“3”);操作如何?
- 3)若在第6行添加list.add("3");那么代码会出错吗?
- 4)若把if语句中的“1”换成“2”,结果你感到意外吗?
运行结果:
1)没错,后面2)3)4)都会报ConcurrentModificationException
的异常;
注意: 不要在foreach
循环里进行元素的remove/add
操作。remove
元素请使用 Iterator
方式,如果并发操作,需要对Iterator对象加锁。
二者本质是一样的,都是通过Iterator
迭代器来实现的遍历,foreach
是增强版的for
循环,可以看作是如下方式二的简化形式:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) { //方式二
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
首先,这涉及多线程操作,Iterator是不支持多线程操作的,List类会在内部维护一个modCount
的变量,用来记录修改次数
举例:ArrayList源码
protected transient int modCount = 0;
每生成一个Iterator,Iterator就会记录该modCount
,每次调用next()
方法就会将该记录与外部类List的modCount进行对比,发现不相等就会抛出多线程编辑异常。
为什么这么做呢?我的理解是你创建了一个迭代器,该迭代器和要遍历的集合的内容是紧耦合的,意思就是这个迭代器对应的集合内容就是当前的内容,我肯定不会希望在我冒泡排序的时候,还有线程在向我的集合里插入数据对吧?所以Java用了这种简单的处理机制来禁止遍历时修改集合。
至于为什么删除“1
”就可以呢,原因在于foreach
和迭代器的hasNext()
方法,foreach这个语法,实际上就是
while(itr.hasNext()){
itr.next()
}
所以每次循环都会先执行hasNext()
,那么看看ArrayList的hasNext()是怎么写的:
public boolean hasNext() {
return cursor != size;
}
cursor是用于标记迭代器位置的变量,该变量由0开始,每次调用next执行+1操作,于是:
你的代码在执行删除“1”后,size=1
,cursor=1
,此时hasNext()返回false,结束循环,因此你的迭代器并没有调用next查找第二个元素,也就无从检测modCount了,因此也不会出现多线程修改异常;但当你删除“2”时,迭代器调用了两次next,此时size=1
,cursor=2
,hasNext()返回true,于是迭代器傻乎乎的就又去调用了一次next(),因此也引发了modCount不相等,抛出多线程修改的异常。
当你的集合有三个元素的时候,你就会神奇的发现,删除“1”是会抛出异常的,但删除“2”就没有问题了,究其原因,和上面的程序执行顺序是一致的.
标题:一文详解集合的不同之处
作者:mmzsblog
地址:https://www.mmzsblog.cn/articles/2019/08/08/1565255569239.html
如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议!
本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!
