Java集合系列(一)List集合
List的几种实现的区别与联系
List主要有ArrayList、LinkedList与Vector几种实现。
ArrayList底层数据结构是数组, 增删慢、查询快; 线程不安全, 效率高; 不可以设置扩展容量, 默认增长1.5倍; 无参构造器初始化时, 初始容量为0。
LInkedList底层数据结构是链表, 增删快、查询慢; 线程不安全, 效率高。
Vector底层数据结构是数组, 增删慢、查询快; 线程安全, 效率低; 可以设置扩展容量, 默认增长2倍; 无参构造器初始化时, 初始容量为10。
List转换及删除元素
数组转List 集合
对于一个数组, 可以通过 Arrays.asList(T... a) 方法转换成List集合, 需要注意的是, 此方法得到的ArrayList对象是基于Arrays内部类 java.util.Arrays$ArrayList 来创建的, 而非 java.util.ArrayList 。这就涉及到了一个问题, 通过 asList(T... a) 转换得到的List集合是不允许进行增删操作的, 我们先看如下代码:
@Test
public void convertList() {
List<String> list = Arrays.asList("张小凡", "陆雪琪", "碧瑶");
//list.add("qingshanli");
list.remove(0);
}
运行时会报异常 java.lang.UnsupportedOperationException

我们先来看看 java.util.Arrays$ArrayList 的方法层次结构:

可以看出, java.util.Arrays$ArrayList 并没有覆写父类AbstractList的 add() 和 remove() 方法, 根据Java的三大特性之多态性可知, 上面代码中的增删操作实际调用的是父类AbstractList的方法, 我们再来看看AbstractList的部分源代码:
public E set(int index, E element) {
    throw new UnsupportedOperationException();
}
public void add(int index, E element) {
    throw new UnsupportedOperationException();
}
public E remove(int index) {
    throw new UnsupportedOperationException();
} 
到此, 可以得知其实通过 Arrays.asList(T... a) 转换得到的List集合是一个固定长度的集合, 所以不能进行增删操作。
如何在遍历时删除ArrayList中元素
方式一: 普通循环
public void test(List<Integer> list) {
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i) % 2 == 0) {
            list.remove(list.get(i));
            i--; // 索引改变!
        }
    }
}
这种方式在删除操作时, 会改变集合的索引和size大小, 遍历时可能会产生角标越界异常, 因此不是特别推荐。
方式二: 高级for循环
public static void main(String[] args) {
    List<Integer> list = new ArrayList<Integer>();
    for (int i = 0; i < 5; i++){
        list.add(i);
    }
    for (Integer num : list) {
        System.out.print("value="+num);
        if (num % 2 == 0) {
            list.remove(num);
            System.out.println(" delete");
        }else{
            System.out.println(" not delete");
        }
    }
} 
运行结果如下, 第一个元素删除正常, 后面继续遍历删除则抛异常 java.util.ConcurrentModificationException

如下, 反编译上述代码, 可以看出高级for循环底层其实就是使用iterator迭代器来进行遍历
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<Integer>();
    for (int i = 0; i < 5; ++i) {
        list.add(Integer.valueOf((int)i));
    }
    Iterator i = list.iterator();
    while (i.hasNext()) {
        Integer num = (Integer)i.next();
        System.out.print((String)new StringBuilder().append((String)"value=").append((Object)num).toString());
        if (num.intValue() % 2 == 0) {
            list.remove((Object)num);
            System.out.println((String)" delete");
            continue;
        }
        System.out.println((String)" not delete");
    }
}
再来看看iterator迭代器实现类的部分源代码:
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;
    Itr() {}
    public boolean hasNext() {
        return cursor != size;
    }
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
可以看出, 迭代器内部的每次遍历操作 next() 、 remove() 方法都会记录List集合内部的modCount当做预期值expectedModCount, 然后在每次循环中判断预期值expectedModCount与List的成员变量modCount是否相等。但是因为上面 list.remove() 调用的是List集合的 remove() 方法, 继续跟踪源代码发现每次调用该方法就会 modCount++; , 但是迭代器内记录的预期值expectedModCount并没有跟着改变, 所以当第二次删除操作时就会发生异常。
方式三: iterator迭代器遍历
public void test(List<Integer> list) {
    Iterator<Integer> it = list.iterator();
    while (it.hasNext()) {
        int num = it.next();
        System.out.print("value="+num);
        if (num % 2 == 0) {
            it.remove();
            System.out.println(" delete");
        }else{
            System.out.println(" not delete");
        }
    }
}
原理与方式二基本类似, 但是这里使用的是迭代器iterator的 remove() 方法, 我们再回顾之前迭代器实现类的源代码, 发现 remove() 方法中有 expectedModCount = modCount; 这个操作, 即调用迭代器的 remove() 方法时会同步List集合的modCount到迭代器的预期值expectedModCount当中, 所以迭代器方式删除才不会产生。
SynchronizedList与Vector的区别
什么是快速失败, 安全失败机制
快速失败(fail-fast)
fail-fast机制是java集合中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
使用迭代器遍历一个集合对象时,如果遍历过程中对集合进行了增删改, 则会抛出 ConcurrentModificationException 。
for (Integer id : list) {
    if (id == 2) {
        list.remove(id);
    }
}
在前面我们已经介绍过, 迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount变量来作为预期值expectedmodCount。集合在被遍历期间如果内容发生变化,就会改变集合内部的modCount值。每当迭代器使用而而迭代遍历调用 next() 方法时每次都会检测 if(modCount==expectedmodCount) ,符合条件就返回遍历;否则将抛出异常终止遍历。
如果集合发生变化时修改modCount值, 并且又同步到expectedmodCount预期值, 比如前文中提到的iterator迭代器的 remove() 方法, 异常则不会抛出。因此, 不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。另外, 在java.util包下的集合类都是快速失败的, 是不能在多线程下发生并发修改的(即迭代过程中被修改)。
安全失败(fail-safe)
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是在开始遍历时先复制原有集合内容,在拷贝的集合上进行遍历, 即在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。java.util.concurrent包下的容器都是安全失败的, 可以在多线程下并发修改。
对象排序
实体类自身具备排序能力
Comparable接口用于使某个类具备可排序能力。实体类实现该接口后覆写其 compareTo() 方法,即可使实体类自身具备可排序的能力 。代码清单如下:
public class Student implements Comparable<Student> {
    private String name;
    private int age;
    @Override
    public int compareTo(Student o) {
        int flag = this.name.compareTo(o.name);
        if(flag == 0) {
            flag = this.age - o.age;
        }
        return flag;
    }
}
实体类具备了排序能力后, 调用List集合的 sort(Comparator<? super E> c) 或者Collections工具类的 sort(List<T> list) 方法即可实现排序。
List<Student> list = new ArrayList<Student>();
list.sort(null);
//Collections.sort(list);
使用比较器排序
Comparator是一个比较器接口,可以用来给不具备排序能力的对象进行排序。实现该比较器需覆写其 compare() 方法即可进行排序, 代码清单如下:
public class Student {
    private String name;
    private int age;
}
public class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        int flag = o1.getName().compareTo(o2.getName());
        if(flag == 0) {
            flag = o1.getAge() - o2.getAge();
        }
        return flag;
    }
}
public class Test {
    public void sortTest() {
        List<student> list = new ArrayList<Student>();
       list.sort(new StudentComparator());
        //Collections.sort(list, new StudentComparator());
    }
} 
参考资料
作者:张小凡 
	出处:https://www.cnblogs.com/qingshanli/
	
	本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】。
Java集合系列(一)List集合的更多相关文章
- java se系列(十二)集合
		
1.集合 1.1.什么是集合 存储对象的容器,面向对象语言对事物的体现,都是以对象的形式来体现的,所以为了方便对多个对象的操作,存储对象,集合是存储对象最常用的一种方式.集合的出现就是为了持有对象.集 ...
 - Java入门系列-19-泛型集合
		
集合 如何存储每天的新闻信息?每天的新闻总数是不固定的,太少浪费空间,太多空间不足. 如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象,可以使用Java集合框架. Java 集合框架提 ...
 - Java集合系列(一):集合的定义及分类
		
1. 集合的定义 什么是集合呢? 定义:集合是一个存放对象的引用的容器. 在Java中,集合位于java.util包下. 2. 集合和数组的区别(面试常问) 提到容器,就会想起数组,那么集合和数组的区 ...
 - Java集合系列:-----------01集合的整体框架
		
内容来自:http://www.cnblogs.com/skywang12345/p/3308498.html Java集合是java提供的工具包,包含了常用的数据结构:集合.链表.队列.栈.数组.映 ...
 - 【Java集合系列】---总体框架
		
个的组合,这些数据项可能共享某些特征,需要以某种操作方式一起进行操作,一般来说,这些数据项的类型都是相同的,或者基类相同(若使用的语言支持继承),列表或数组通常不认为是集合,因为其大小固定,但是事实上 ...
 - 深入java集合系列文章
		
搞懂java的相关集合实现原理,对技术上有很大的提高,网上有一系列文章对java中的集合做了深入的分析, 先转载记录下 深入Java集合学习系列 Java 集合系列目录(Category) HashM ...
 - Java 集合系列04之 fail-fast总结(通过ArrayList来说明fail-fast的原理、解决办法)
		
概要 前面,我们已经学习了ArrayList.接下来,我们以ArrayList为例,对Iterator的fail-fast机制进行了解.内容包括::1 fail-fast简介2 fail-fast示例 ...
 - Java 集合系列目录(Category)
		
下面是最近总结的Java集合(JDK1.6.0_45)相关文章的目录. 01. Java 集合系列01之 总体框架 02. Java 集合系列02之 Collection架构 03. Java 集合系 ...
 - Java多线程系列--“JUC集合”01之 框架
		
概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类.接下来,将展开对JUC包中的集合进行学习.在学习之前,先温习一下"Java ...
 - Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList
		
概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析, ...
 
随机推荐
- esxi虚机迁移到Workstation
			
虚拟机的文件管理由VMware Workstation来执行.一个虚拟机一般以一系列文件的形式储存在宿主机中,这些文件一般在由workstation为虚拟机所创建的那个目录中. 如下图所示:(< ...
 - Maven项目读取resources下文件的路径
			
要取编译后的路径,而不是你看到的src/main/resources的路径.如下: URL url = 类名.class.getClassLoader().getResource("conf ...
 - iOS13中presentViewController的问题
			
iOS13中presentViewController的问题 更新了Xcode11.0 beta之后,在iOS13中运行代码发现presentViewController和之前弹出的样式不一样. 会出 ...
 - POJ 3692:Kindergarten(二分图最大团)
			
题目链接 题意 已知班级有g个女孩和b个男孩,所有女生之间都相互认识,所有男生之间也相互认识,给出m对关系表示哪个女孩与哪个男孩认识.现在要选择一些学生来组成一个集合,使得里面所有人都认识,求此集合最 ...
 - HDU 5791:Two(DP)
			
http://acm.hdu.edu.cn/showproblem.php?pid=5791 Two Problem Description Alice gets two sequences A ...
 - find文件删除
			
find /root/title/test -type f -name '*.txt' -exec rm {} \; 查找并删除test文件夹下所有txt文件 find /root/title/t ...
 - Java线程池源码及原理
			
目录 1 说明 1.1类继承图 2 线程池的状态 3 源码分析 3.1完整的线程池构造方法 3.2 ctl 3.3 任务的执行 3.3.1 execute(Runnable command) 3.3. ...
 - C语言学习书籍推荐《学通C语言的24堂课》下载
			
下载地址:点我 编辑推荐 <学通C语言的24堂课>:用持续激励培养良好习惯以良好习惯铸就伟大梦想——致亲爱的读者朋友在开始学习<学通C语言的24堂课>的同时,强烈建议读者朋友同 ...
 - Autocad2017破解版下载|Autodesk Autocad 2017中文破解版下载 64位(附注册机/序列号)
			
Autocad2017是Autodesk公司开发的自动计算机辅助设计软件,可用于二维绘图.详细绘制.设计文档和基本三维设计,它具有良好的用户界面,允许用户通过交互菜单或命令行方式来进行各种操作,包括图 ...
 - fastdfs java client error
			
tracker,storage运行正常,利用fdfs_test程序做测试,可以正常上传下载文件. tracker的端口配置 # HTTP port on this tracker server htt ...