【Java基础】Java集合List遇到的坑

news/2024/11/18 11:46:30/

在Java的开发过程中,List集合是最常用的一种集合类型。它除了具有基本数组的所有特性外,还具备了动态扩容和元素添加、删除等基础操作。作为Java程序员,我们经常使用List来存储和操作数据。然而,在处理List时,有许多常见的问题和陷阱,可能会导致程序出现错误或性能下降。在本文详细介绍Java List中的10个常见问题和陷阱,以及如何解决这些问题。


1、使用非线程安全的List

Java中有两种类型的List:线程安全和非线程安全。如果您的应用程序是多线程并发的,则必须使用线程安全的List,否则会出现竞态条件和数据不一致的情况。例如:

List<String> list = new ArrayList<>(); // 非线程安全
List<String> syncList = Collections.synchronizedList(list); // 线程安全

使用以上代码可以创建一个线程安全的List,但要注意使用synchronizedList()方法需要进行同步,因此可能会影响性能。

2、使用大量的remove方法

List的remove方法是非常耗费资源的,因为它会导致所有的元素向前移动一个位置。因此,当您需要删除大量元素时,最好使用迭代器进行删除。

Iterator<String> it = list.iterator();
while (it.hasNext()) {String str = it.next();if (str.equals("xxx")) {it.remove();}
}

 使用迭代器进行删除可以避免所有元素的移动,从而提高性能。

3、使用错误的equals方法

在List中比较元素时,必须使用正确的equals方法。如果使用错误的equals方法,可能会出现逻辑错误或程序崩溃的情况。例如:

class Person {String name;int age;public boolean equals(Person p) {return this.name.equals(p.name) && this.age == p.age;}
}List<Person> list = new ArrayList<>();
Person person1 = new Person("Tom", 20);
Person person2 = new Person("Tom", 20);
list.add(person1);
list.remove(person2); // 删除失败

这里的问题是Person的equals方法没有重写Object的equals方法,因此无法正确比较两个对象。要解决这个问题,需要重写equals方法并确保其符合equals的规范。

4、不使用泛型

List可以存储任何类型的对象,但如果不使用泛型,则可能会出现类型转换问题和编译时错误。例如:

List list = new ArrayList(); // 没有使用泛型
list.add("Hello");
String str = list.get(0); // 编译错误:需要强制转换

在这种情况下,需要使用泛型来避免类型转换和编译时错误。

List<String> list = new ArrayList<>(); // 使用了泛型
list.add("Hello");
String str = list.get(0); // 正常运行

5、使用错误的List实现

Java中有许多种类型的List实现,例如LinkedList和ArrayList。如果您需要快速随机地访问列表中的元素,则应该使用ArrayList。如果您需要在列表中进行频繁的插入或删除操作,则应该使用LinkedList。

List<String> arrayList = new ArrayList<>(); // 快速随机访问
List<String> linkedList = new LinkedList<>(); // 频繁插入或删除

6、不适当地使用subList方法

List的subList方法可以返回原始列表的子列表。但是,如果您不小心修改了子列表,则可能会影响原始列表。因此,建议使用subList方法创建一个新的列表。

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");List<String> subList = list.subList(0, 2); // 原始列表为[A, B, C],子列表为[A, B]subList.add("D"); // 修改子列表System.out.println(list); // 输出:[A, B, D, C]

在这个例子中,当我们修改子列表时,原始列表也被修改了。如果您需要对子列表进行更改,请使用一个新的列表。

7、使用错误的排序方法

List的排序方法可以按照不同的方式排序元素,但是必须使用正确的排序方法来避免出现逻辑错误或程序崩溃的情况。例如:

List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.sort(); // 默认使用升序排序System.out.println(list); // 输出:[1, 2]

在这个例子中,我们使用默认的sort方法对整数列表进行排序,因此它将按升序排列。如果您需要按降序排序,请使用Collections.reverseOrder()方法。

Iterator<String> it = list.iterator();
while (it.hasNext()) {String str = it.next();if (str.equals("xxx")) {it.remove();}
}

使用迭代器进行删除可以避免并发修改异常和数据不一致的情况。

9、使用错误的容量

List的容量是指它可以存储多少个元素。如果您的应用程序需要存储大量元素,则必须为List设置适当的容量,否则会导致内存溢出或性能下降。

List<String> list = new ArrayList<>(100000); // 设置初始容量为100000

10、不使用ListIterator进行双向遍历

ListIterator是一种特殊类型的迭代器,它可以从前向后或从后向前遍历列表,并且可以在任何位置插入或删除元素。如果您需要在双向遍历期间更改列表,请使用ListIterator。

ListIterator<String> it = list.listIterator();
while (it.hasNext()) {String str = it.next();System.out.println(str);
}while (it.hasPrevious()) {String str = it.previous();System.out.println(str);
}

 使用ListIterator从前向后和从后向前遍历列表,并输出每个元素。


下面是完整的示例代码,展示了如何使用Java List处理数据,并避免上述10个常见问题和陷阱:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;public class JavaListExample {public static void main(String[] args) {// 1. 使用线程安全的ListList<String> list = new ArrayList<>();List<String> syncList = Collections.synchronizedList(list);// 2. 使用迭代器进行删除Iterator<String> it = list.iterator();while (it.hasNext()) {String str = it.next();if (str.equals("xxx")) {it.remove();}}// 3. 使用正确的equals方法class Person {String name;int age;public boolean equals(Object o) {if (o == this) return true;if (!(o instanceof Person)) return false;Person p = (Person) o;return this.name.equals(p.name) && this.age == p.age;}}List<Person> personList = new ArrayList<>();Person person1 = new Person();person1.name = "Tom";person1.age = 20;Person person2 = new Person();person2.name = "Tom";person2.age = 20;personList.add(person1);personList.remove(person2);// 4. 使用泛型List<String> stringList = new ArrayList<>();stringList.add("Hello");String str = stringList.get(0);// 5. 使用正确的List实现List<String> arrayList = new ArrayList<>(); // 快速随机访问List<String> linkedList = new LinkedList<>(); // 频繁插入或删除// 6. 使用subList方法时创建新的列表List<String> list2 = new ArrayList<>();list2.add("A");list2.add("B");list2.add("C");List<String> subList = new ArrayList<>(list2.subList(0, 2));subList.add("D");System.out.println(list2); // 输出:[A, B, C]// 7. 使用正确的排序方法List<Integer> integerList = new ArrayList<>();integerList.add(2);integerList.add(1);integerList.sort(Collections.reverseOrder());System.out.println(integerList); // 输出:[2, 1]// 8. 使用迭代器进行修改Iterator<String> it2 = list.iterator();while (it2.hasNext()) {String str2 = it2.next();if (str2.equals("xxx")) {it2.remove();}}// 9. 设置适当的容量List<String> list3 = new ArrayList<>(100000);// 10. 使用ListIterator进行双向遍历ListIterator<String> it3 = list.listIterator();while (it3.hasNext()) {String str3 = it3.next();System.out.println(str3);}while (it3.hasPrevious()) {String str3 = it3.previous();System.out.println(str3);}}
}

在这个示例中,我们使用ArrayList作为List实现,并展示了如何解决上述10个常见问题和陷阱。希望这篇文章能够帮助您更好地理解Java List,并帮助您构建更高效和可靠的应用程序。


总结

Java List是非常有用的数据结构,但是在使用它们时必须小心,以避免常见问题和陷阱。通过遵循最佳实践,并使用正确的代码技巧,可以帮助您构建更高效和可靠的应用程序。

最后,还有一些其他的小技巧和建议,可以帮助您更好地使用Java List:

  • 当您需要在列表中添加或删除元素时,请使用add()和remove()方法,而不是直接操作底层数组,以避免出现逻辑错误和数据不一致的情况。
  • 为了提高性能,尽可能使用基本类型而不是包装类型。例如,如果您需要存储整数,则应该使用List<Integer>而不是List<int>。
  • 如果您需要对大量元素进行排序,请使用Collections.sort()方法,并提供一个自定义的Comparator来指定排序顺序。
  • 如果您需要在多个线程中并发访问列表,请考虑使用CopyOnWriteArrayList代替普通的ArrayList。
  • 如果您需要对列表进行频繁的查找操作,请考虑使用HashMap或TreeMap代替List,因为它们具有更快的查找时间。

 


http://www.ppmy.cn/news/51950.html

相关文章

Selenium安装及环境配置

目录 一、Selenium 简介1. 组件2. 特点 二、安装Selenium✨三、下载对应版本的Chromedriver1.查看Chrome的版本号2.下载驱动 chromedriver和配置3.解压到本地4.复制文件放入python安装目录的Scripts文件夹中5.Selenium启动Chrome 一、Selenium 简介 1. 组件 Selenium IDE&…

023:Mapbox GL加载mp4视频文件

第023个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载MP4视频文件。一个视频源。 “urls”值是一个数组。 对于数组中的每个 URL,将创建一个视频元素源。 要支持跨浏览器的视频,请提供多种格式的 URL。“坐标”数组包含按顺时针顺序列出的视频角的 [longi…

瑞萨开发环境搭建

使用keil环境&#xff0c;开发瑞萨renase A4M2 下载MDK 下载MDK&#xff0c;5.37 其它版本 最好使用5.30以上 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5k3XGSK5-1682182139410)(https://secure2.wostatic.cn/static/reEunrWa2vsfrcpVZC1nbo…

Haproxy负载均衡集群

1.Haproxy支持四层和七层 2.haproxy常用的调度算法&#xff1f; 3.LSV/NGINX/HAPROXT的区别&#xff1f; 4. 5.Haproy负载均衡部署 实验需求 利用Haproxy的运用配置出负载均衡调度器&#xff0c;以此来调用两台Nginx服务器进行工作 实验所需组件 Haproxy服务器&#xff1a;192…

【Java数据结构】二叉树

二叉树 树型结构概念树中的概念树的表现形式 二叉树两种特殊的二叉树二叉树的性质二叉树的存储二叉树基本操作 树型结构 概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像…

命令执行漏洞概述

命令执行漏洞概述 命令执行定义命令执行条件命令执行成因命令执行漏洞带来的危害远程命令执行漏洞相关函数assert()preg_replace()call_user_func() a ( a( a(b)可变函数远程命令执行漏洞的利用系统命令执行漏洞相关函数system()exec()shell_exec()passthru&#xff08;&#x…

UE4/5 行为树使用教程

使用行为树需确保目标蓝图继承自Character基类&#xff0c;然后根据本文下面的流程操作即可。 1.创建AIController 首先需要在角色自身蓝图之外创建一个新的蓝图&#xff0c;继承自AIController&#xff1a; 2.挂载AIController 找到角色自身蓝图类设置中的Pawn一栏&#…

Redis入门学习笔记【一】

目录 一、Redis是什么 二、Redis数据结构 2.1 Redis 的五种基本数据类型 2.1.1String&#xff08;字符串&#xff09; 2.1.2字符串列表&#xff08;lists&#xff09; 2.1.3字符串集合&#xff08;sets&#xff09; 2.1.5哈希&#xff08;hashes&#xff09; 2.2 Red…