欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

List相关面试题--这一篇全了解

程序员文章站 2024-01-21 14:44:22
...

Java 中 Set 与 List 有什么不同?

List,Set都是继承自Collection接口。都是用来存储一组相同类型的元素的。

List特点:元素有放入顺序,元素可重复 。有顺序,即先放入的元素排在前面。

Set特点:元素无放入顺序,元素不可重复。无顺序,即先放入的元素不一定排在前面。不可重复,相同元素在set中只会保留一份。所以,有些场景下,set可以用来去重。

Java中的List有几种实现,各有什么不同?

List主要有ArrayList、LinkedList与Vector几种实现。

这三者都实现了List 接口,使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率。

ArrayList 是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.

LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.

Vector 和ArrayList类似,但属于强同步类。如果你的程序本身是线程安全的(thread-safe,没有在多个线程之间共享同一个集合/对象),那么使用ArrayList是更好的选择。

Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.

而 LinkedList 还实现了 Queue 接口,该接口比List提供了更多的方法,包括 offer(),peek(),poll()等.

注意: 默认情况下ArrayList的初始容量非常小,所以如果可以预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。

知道什么是synchronizedList吗?他和Vector有何区别?

Vector是java.util包中的一个类。 SynchronizedList是java.util.Collections中的一个静态内部类。在多线程的场景中可以直接使用Vector类,也可以使用Collections.synchronizedList(List list)方法来返回一个线程安全的List。

1.如果使用add方法,那么他们的扩容机制不一样。

2.SynchronizedList可以指定锁定的对象。即锁粒度是同步代码块。而Vector的锁粒度是同步方法。

3.SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。

4.使用SynchronizedList的时候,进行遍历时要手动进行同步处理。

5.SynchronizedList可以指定锁定的对象。更多内容参见我的详细文章介绍:链接:SynchronizedList和Vector的区别-HollisChuang's  

通过Array.asList获得的List有何特点,使用时应该注意什么?

1. asList 得到的只是一个 Arrays 的内部类,一个原来数组的视图 List,因此如果对它进行增删操作会报错

2. 用 ArrayList 的构造器可以将其转变成真正的 ArrayList

什么是fail-fast,什么是fail-safe,有什么区别吗?链接:链接:https://t.zsxq.com/rBEaAiy

一:快速失败(fail—fast)         

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出ConcurrentModificationException。        

    如以下代码,会抛出ConcurrentModificationException:       

for (Student stu : students) {
          if (stu.getId() == 2)
          students.remove(stu);
 }

          原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

      注意:这里异常的抛出条件是检测到 modCount=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

      场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

相关文章:链接:Java中的增强for循环(for each)的实现原理与坑-HollisChuang's Blog  

二:安全失败(fail—safe)

      采用安全失败机制的集合容器,在遍历时不是直接在集合内容*问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

      原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

      缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

      场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

如何在遍历的同时删除ArrayList中的元素。

常见的删除有以下几种:

/**
  * 使用增强的for循环
  * 在循环过程中从List中删除非基本数据类型以后,继续循环List时会报
  ConcurrentModificationException
  */
public void listRemove() {
    List<Student> students = this.getStudents();
    for (Student stu : students) {
        if (stu.getId() == 2)
            students.remove(stu);
    }
}

/**
 * 像这种使用增强的for循环对List进行遍历删除,但删除之后马上就跳出的也不会出现异常
 */
public void listRemoveBreak() {
    List<Student> students = this.getStudents();
    for (Student stu : students) {
        if (stu.getId() == 2) {
            students.remove(stu);
            break;
        }
    }
}

/**
* 这种不使用增强的for循环的也可以正常删除和遍历,
* 这里所谓的正常是指它不会报异常,但是删除后得到的
* 数据不一定是正确的,这主要是因为删除元素后,被删除元素后
* 的元素索引发生了变化。假设被遍历list*有10个元素,当
* 删除了第3个元素后,第4个元素就变成了第3个元素了,第5个就变成
* 了第4个了,但是程序下一步循环到的索引是第4个,
* 这时候取到的就是原本的第5个元素了。
*/
public void listRemove2() {
    List<Student> students = this.getStudents();
    for (int i=0; i<students.size(); i++) {
        if (students.get(i).getId()%3 == 0) {
            Student student = students.get(i);
            students.remove(student);
        }
    }
}

/**
* 使用Iterator的方式可以顺利删除和遍历
*/
public void iteratorRemove() {
    List<Student> students = this.getStudents();
    System.out.println(students);
    Iterator<Student> stuIter = students.iterator();
    while (stuIter.hasNext()) {
        Student student = stuIter.next();
        if (student.getId() % 2 == 0)
            stuIter.remove();
    //这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException
    }
    System.out.println(students);
}

建议使用最后一种,即使用Iterator的方式在遍历过程中删除元素

相关标签: list面试题