List接口

An ordered collection (also known as a sequence).  The user of this interface has precise control over where in the list each element is inserted.  The user can access elements by their integer index (position in the list), and search for elements in the list.

上面这段引用就是jdk8对List的定义-一个有序的集合。

ArrayList、LinkedList、Vector的异同

  • 三个实现类都存储有序的、可重复的数据
  • ArrayList相当于数据结构中的线性表,是List接口的主要实现类;具有线程不安全、效率高的特点;底层使用Object[] elementData存储数据。
  • LinkedList相当于数据结构中的链表;处理插入、删除操作时,效率比ArrayList高;底层使用双向链表存储数据。
  • Vector是List接口的比较老的实现类;线程安全但是效率低;底层使用Object[] elementData存储数据。

由于List下面的三个实现类都比较简单,这里就不做过多的赘述。主要是注意一下如果遍历的过程中要对List进行删除操作的时候最好是使用Iterator,不要使用for(不管是普通for还是增强for),会出现错误,具体错误如下:

@Test
public void test4(){
    List<Employee> list = new ArrayList<>(16);
    for(int i = 0; i < 5; i++){
        list.add(new Employee("test",i,i));
    }

    for(int i = 0; i < list.size(); i++){
        Employee employee = list.get(i);
        if(employee.age == employee.salary) {
            list.remove(i);
        }
    }
    for(Employee employee:list){
        System.out.println(employee);
    }
    /**
     * 打印结果:
     * Employee{name='test', age=1, salary=1}
     * Employee{name='test', age=3, salary=3}
     */
}

上面的代码使用普通for循环进行删除操作,按照我们的本意是会将所有元素都删除,但是结果还剩下2个,原因是因为每次删除i位置的元素后,List中i之后的元素向前走一步,然后执行了一次i++,于是乎跳过了一个元素,慢慢的会积累多个出来。再看下增强for:

@Test
public void test4(){
    List<Employee> list = new ArrayList<>(16);
    for(int i = 0; i < 5; i++){
        list.add(new Employee("test",i,i));
    }
    for(Employee employee:list){
        if(employee.age == employee.salary){
            if(list.remove(employee)){
                System.out.println("remove success");
            }
        }
    }
    for(Employee employee:list){
        System.out.println(employee);
    }
    /**
     * 打印结果:
     * run equals()....
     * remove success
     * 
     * java.util.ConcurrentModificationException
     * 	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907)
     * 	at java.util.ArrayList$Itr.next(ArrayList.java:857)
     * 	at com.chapter12_collection.CollectionTest.test4(CollectionTest.java:70)
     */
}

发现增强for的确删除一个元素成功了,但是删除第二个元素的时候报错ConcurrentModificationException,于是乎增强for中删除元素也是不可行的。那我们再来看下正确的做法:

@Test
public void test4(){
    List<Employee> list = new ArrayList<>(16);
    for(int i = 0; i < 5; i++){
        list.add(new Employee("test",i,i));
    }
    Iterator<Employee> iterator = list.iterator();
    while (iterator.hasNext()){
        Employee employee = iterator.next();
        if(employee.age == employee.salary){
            iterator.remove();
        }
    }
	/**
     * 打印结果为空
     */
}

@Test
public void test5(){
    List<Employee> list = new ArrayList<>(16);
    for(int i = 0; i < 5; i++){
        list.add(new Employee("test"+i,i*2,i*3));
    }
    Iterator<Employee> iterator = list.iterator();
    while (iterator.hasNext()){
        Employee employee = iterator.next();
        if(employee.age == 0){
            iterator.remove();
            continue;
        }
        System.out.println(employee);
    }
    /**
     * 打印结果:
     * Employee{name='test1', age=2, salary=3}
     * Employee{name='test2', age=4, salary=6}
     * Employee{name='test3', age=6, salary=9}
     * Employee{name='test4', age=8, salary=12}
     */
}

踩坑一 List中所有元素指向同一个地址内存

ArrayList中的每一个元素存储的实际上是对象引用(之前在公司写代码的时候,做过类似下面的事),假如按照下面的方式使用ArrayList,则最后list中存储的元素都相同且都是最后一个元素,原因是list中所有的元素都指向同一块内存。

@Test
public void test1(){
    List<Person> list = new ArrayList<>(16);
    Person p = new Person();
    for(int i = 0; i < 10; i++){
        p.setName("test"+i);
        p.setAge(i);
        list.add(p);
    }
    System.out.println(list);
}

结果所有list中的元素都指向同一个Person实例

[Person{name='test9', age=9},  Person{name='test9', age=9}, ...,Person{name='test9', age=9}, Person{name='test9', age=9}]

踩坑二 List中存储了一个元素null

这个坑出现了两次,一次是使用stream之后collect转为list,但是结果为List[null],另外一次就是使用Mybatis查询数据库返回的结果为List[null];

  1. 使用stream之后collect转为list结果为List[null] stream通过流映射出list中为null的值,转为为list,此时就会出现List中有null的值。
@Test
public void testStream(){
    List<Person> list = Arrays.asList(new Person("cc"), new Person("abc"));
    List<Long> collect = list.stream().map(Person::getAge).collect(Collectors.toList());
    System.out.println("stream操作之后:"+collect);
    collect.removeAll(Collections.singleton(null));
    System.out.println("removeAll之后:"+collect);
}
  1. 上面代码执行结果: stream操作之后:[null, null] removeAll之后:[]
  2. 数据库返回的结果为List[null]
SELECT
    NULL AS a.col1, 
    b.*
FROM
    a LEFT JOIN b ON a.id = b.id
where a.col2= 'AAA';
  1. 假如此时a表查到了数据,而b表没有查询到数据,那么此时返回的是每一行是[null],具体行数则由a的行数来决定;如果使用的是mybatis查询,返回的结果应该是一个list,只不过这个list里面所有的元素都是null。

对于上面说的两种List[null]情况,如果是不需要list中有null值得话,都可以借助collect.removeAll(Collections.singleton(null));来去除list中的null。