首页 > 其他分享 >List接口相关问题

List接口相关问题

时间:2024-11-09 14:45:54浏览次数:3  
标签:遍历 Iterator list ArrayList 元素 List 接口 相关

目录

1.迭代器 Iterator 是什么

2.Iterator 怎么使用?有什么特点?

3.如何边遍历边移除 Collection 中的元素?

4.Iterator 和 ListIterator 有什么区别?

5.遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List遍历的最佳实践是什么?

6.RandomAccess

6.1什么是RandomAccess

6.2两种遍历方法性能测试

7.ArrayList 的优缺点

8.如何实现数组和 List 之间的转换?

9.ArrayList 和 LinkedList 的区别是什么?

10.ArrayList 和 Vector 的区别是什么?

11.多线程场景下如何使用ArrayList?

12.为什么 ArrayList 的 elementData 加上 transient 修饰?

12.1transient关键字的作用

12.2ArrayList中的transient

13.List 和 Set 的区别


1.迭代器 Iterator 是什么

        Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。因为所有Collection接继承了Iterator迭代器

2.Iterator 怎么使用?有什么特点?

Iterator 使用代码如下:

	List<String> list = new ArrayList<>();
	Iterator<String> it = list. iterator();
	while(it. hasNext()){
		String obj = it. next();
		System. out. println(obj);
	}

Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

3.如何边遍历边移除 Collection 中的元素?

边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法,如下:

	Iterator<Integer> it = list.iterator();
	while(it.hasNext()){
		*// do something*
		it.remove();
	}

一种最常见的错误代码如下:

	for(Integer i : list){
		list.remove(i)
	}

运行以上错误代码会报 ConcurrentModificationException 异常。这是因为当使用for(Integer i : list) 语句时,会自动生成一个iterator 来遍历该 list,但同时该 list 正在被Iterator.remove() 修改。Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它。

4.Iterator 和 ListIterator 有什么区别?

(1)Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。

(2)Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。

(3)ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

5.遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List遍历的最佳实践是什么?

遍历方式有以下几种:

        (1)for循环遍历,基于计数器在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止;

        (2)迭代器遍历,IteratorIterator是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口Java在Collections中支持了Iterator模式;

        (3)foreach循环遍历foreach内部也是采用了Iterator的方式实现,使用时不需要显式声明Iterator或计数器优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换;

最佳实践:

        Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均时间复杂度为 O(1),如ArrayList。如果没有实现该接口,表示不支持 Random Access,如LinkedList。

        推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或foreach 遍历。

6.RandomAccess

6.1什么是RandomAccess

        RandomAccess和Cloneable、Serializable接口一样,本质上都是一种标志性接口,无具体实现,意在告知JVM此类(在恒定时间内)可支持快速随机访问。

     (1)给List使用的标记型接口,目的是使其支持(在恒定时间内)的快速随机访问

     (2)推荐在遍历集合的时候检查该List是否实现了RandomAccess接口,以便让不同的集合使用更优的遍历算法(ArrayList用for循环遍历快一些,LinkedList用迭代器遍历快一些)

     (3)通常来说,如果一个List用for循环遍历比用迭代器遍历的速度快,那么推荐实现RandomAccess接口。

6.2两种遍历方法性能测试

        100W条数据存入ArrayList和LinkedList后,分别用for循环和迭代器来遍历这两个集合记录遍历时间。通过结果可知,ArrayList for循环遍历的时间小于迭代器遍历时间,而LinkedList for循环遍历的时间远超迭代器遍历时间。

原因就和这两个集合的底层实现有关了:

        ArrayList底层动态扩容数组,查询时间复杂度O(1),所以两种方式遍历区别不大,快速随机访问略胜一筹。

        LinkedList底层基于双向循环链表,迭代器的next方法返回下一个元素属于顺序访问,list中的get获取指定位置元素属于随机访问。因为链表访问下一个元素时间复杂度是O(1),而随机访问复杂度是n,所以使用迭代器要快。

public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        int listSize = 1000000;
        for (int i = 0; i < listSize; i++) {
            arrayList.add(i);
        }
        List<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < listSize; i++) {
            linkedList.add(i);
        }
        computingTime(arrayList);
        computingTime(linkedList);
    }

    private static void computingTime(List list) {
        long startTime;
        long endTime;
        if (list instanceof RandomAccess) {
            System.out.println(list.getClass() + "实现了RandomAccess接口");
        } else {
            System.out.println(list.getClass() + "未实现RandomAccess接口");
        }
        startTime = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("采用for循环的方式遍历集合耗时:" + (endTime - startTime) + "ms");
        startTime = System.currentTimeMillis();
        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
            Object o = iter.next();
        }
        endTime = System.currentTimeMillis();
        System.out.println("采用迭代器的方式遍历集合耗时:" + (endTime - startTime) + "ms");
    }

7.ArrayList 的优缺点

ArrayList的优点如下:

        (1)ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。

        (2)ArrayList 在顺序添加一个元素的时候非常方便。

ArrayList 的缺点如下:

        (1)删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。

        (2)插入元素的时候,也需要做一次元素复制操作,缺点同上。

        (3)ArrayList 比较适合顺序添加、随机访问的场景。

8.如何实现数组和 List 之间的转换?

(1)数组转 List:使用 Arrays. asList(array) 进行转换。

(2)List 转数组:使用 List 自带的 toArray() 方法。

代码示例:

	// list to array
	List<String> list = new ArrayList<String>();
	list.add("123");
	list.add("456");
	list.toArray();
	// array to list
	String[] array = new String[]{"123","456"};
	Arrays.asList(array);

9.ArrayList 和 LinkedList 的区别是什么?

(1)数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

(2)随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

(3)增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。

(4)内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

(5)线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

(6)综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。LinkedList 的双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

10.ArrayList 和 Vector 的区别是什么?

(1)这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合

(2)线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。

(3)性能:ArrayList 在性能方面要优于 Vector。

(4)扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

(5)Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

11.多线程场景下如何使用ArrayList?

        ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样:

	List<String> synchronizedList = Collections.synchronizedList(list);
	synchronizedList.add("aaa");
	synchronizedList.add("bbb");
	for (int i = 0; i < synchronizedList.size(); i++) {
		System.out.println(synchronizedList.get(i));
	}

12.为什么 ArrayList 的 elementData 加上 transient 修饰?

12.1transient关键字的作用

        在持久化对象时,对于一些特殊的数据成员(如用户的密码,银行卡号等),我们不想用序列化机制来保存它。为了在一个特定对象的一个成员变量上关闭序列化,可以在这个成员变量前加上关键字transient。注意static修饰的静态变量天然就是不可序列化的。

12.2ArrayList中的transient

        ArrayList 中的数组定义如下:transient Object[] elementData;

        再看一下 ArrayList 的定义:

public class ArrayList<E> extends AbstractList<E> 
	implements List<E>, RandomAccess, Cloneable, java.io.Serializable

        可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现:

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioral compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

        每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,然后遍历 elementData,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后的文件大小。

13.List 和 Set 的区别

List , Set 都是继承自Collection 接口

(1)List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。

(2)Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及TreeSet。

        另外 List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。

Set和List对比

(1)Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。

(2)List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变

标签:遍历,Iterator,list,ArrayList,元素,List,接口,相关
From: https://blog.csdn.net/YouPromise/article/details/143642117

相关文章

  • 网卡速度突破瓶颈;网卡的速度突破瓶颈与硬盘并不是直接相关的,尽管它们都涉及到数据传输
    网卡的速度突破瓶颈与硬盘并不是直接相关的,尽管它们都涉及到数据传输和网络性能,但它们的瓶颈和优化方向是不同的。1. 网卡速度突破瓶颈网卡(NetworkInterfaceCard)用于计算机与网络之间的通信,网卡的速度突破瓶颈通常指的是网络传输速率的提升。目前,常见的网卡速率有1Gbps(千兆)、......
  • 进制与二进制及相关转换
    阅读学习:1、图解二进制,带你揭开二进制的神秘面纱!2、一文读懂“二进制基础”及“位运算”学习视频:V1V2V3V4......
  • 浅谈 PHP 与手机 APP 开发(API 接口开发)
    一、先简单回答两个问题:1、PHP可以开发客户端?答:不可以,因为PHP是脚本语言,是负责完成B/S架构或C/S架构的S部分,即:服务端的开发。(别去纠结GTK、WinBinder)2、为什么选择PHP作为开发服务端的首选?答:跨平台(可以运行在UNIX、LINUX、WINDOWS、MacOS下)、低消耗(PHP消耗相当少的系统......
  • ffmpeg问题解决:Unrecognized option 'preset'. Error splitting the argument list: O
    来到这里,十有八九是手动编译安装的ffmpeg,在跑视频流程序或命令时出现这个问题。跟这个报错:ffmpeg:errorwhileloadingsharedlibraries:libx264.so.164:cannotopensharedobjectfile:Nosuchfileordirectory的错误本质是一样的,都是由于ffmpeg时使用到了libx264,而在......
  • vue3组件应用 + 以及组件相关知识应用
    文章目录vue组件化开发一、什么是Vue组件化开发二、组件的创建方式三、组件的数据传递四、组件的生命周期五、组件的插槽(Slot)数据传递的方式实例组件生命周期应用场景插槽应用define相关应用vue组件化开发一、什么是Vue组件化开发概念Vue组件化开发是一种将用......
  • 动态内存的相关知识点
    今天学了动态内存管理的相关知识点,首先什么是动态内存呢,我的理解是可大可小的,能够动态变化的。1.为什么存在动态内存分配我们已经掌握的内存开辟方式有:intmain(){ inta=10; intarr[10]={0}; intn; scanf("%d",&n); intarr1[n]; return0;}向上面......
  • D61【python 接口自动化学习】- python基础之数据库
    day61数据库定义学习日期:20241107学习目标:MySQL数据库--130:MySQL入门使用学习笔记:在命令提示符内先试用MySQL使用图形化工具操作MySQLDBeaver安装DBeaver连接MySQL总结MySQL安装成功后,可以使用命令提示符查看数据库安装使用图形化工具DBeaver操作MySQL......
  • 在Windows操作系统中,HKEY_CURRENT_USER\Console 是注册表中的一个键路径,它用于存储与
    在Windows操作系统中,HKEY_CURRENT_USER\Console是注册表中的一个键路径,它用于存储与控制台窗口(例如命令提示符窗口,CMD)的配置和设置相关的数据。以下是HKEY_CURRENT_USER\Console的详细说明:1. 位置路径:HKEY_CURRENT_USER\Console\2. 作用这个注册表项包含了当前用户对控制......
  • fitter&listener
    一、过滤器(Filter)过滤器是JavaWeb应用中的一种组件,用于在请求到达Servlet之前或响应返回客户端之前,对请求或响应进行特定的处理。它可以用于数据的预处理、后处理、日志记录、权限验证等。生命周期过滤器的生命周期与Servlet类似,但不同于Servlet,过滤器在Web应用启动时即被加......
  • 原木、实木和家具是常见的木材相关术语,它们之间有一定的区别,但容易让人混淆。下面我将
    原木、实木和家具是常见的木材相关术语,它们之间有一定的区别,但容易让人混淆。下面我将为你详细解释如何区分这三者以及它们的不同之处,帮助你更好地理解和区分这些概念。1. 原木(RawWood)定义:原木是指直接从树木中砍下来的粗大木材,未经任何加工。它通常是树干或大树枝,外形不规则,......