Java-List.add

本文通过实例解析了Java中HashMap与List的使用误区,重点解释了引用传递导致的问题,并提供了理解HashMap更新机制及List元素变更原理的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写这篇博客目的是为了记录自己在学习过程当中填过的坑,如果能够给正在填坑的同学一点启示,荣幸之至。

一言不合,我们先来一段代码!- - 

public class TestAndHashMap{
List<HashMap<String,String>> mListMap = new Array<HashMap<String,String>>();
public static void main(String[] args) {

HashMap<String,String>  dataMap = new HashMap<String,String>();
  dataMap.put("name","暗夜猎手");
  dataMap.put("des","让我们来猎杀那些陷入黑暗的人吧吧吧!");
  mListMap.add(dataMap);dataMap.put("name","暗影之拳");
  dataMap.put("des","你们快点逃命不然人家就享受不到追杀的乐趣了!");
  mListMap.add(dataMap);
  }
 }


让我们猜一下现在mListMap中有几个元素,正确答案是两个。那么到底是那两个元素呢?分别是name为“暗夜猎手”和“暗影之拳”吗?当我们把程序跑起来之后,会发现两个都是name为“暗影之拳”的元素。

why?这是我在学习安卓期间碰到的一个问题,浪费了很多宝贵的时间,Java基础不过关呀!

因为从main函数开始到main函数结束,hashMap还是那个hashMap,hashMap是一个引用,从始至终都指向同一个地址。

我想有必要先了解一下HashMap

下面是Android工程部中一个代码片段和调试运行期间相关变量的值

dataMap.put("phone", "暗夜猎手");
dataMap.put("phone","暗影之拳");

程序运行到第一个put时的Watches值:

0 = {HashMap$HashMapEntry@4355} "name" -> "暗夜猎手"
 key = {String@4357} "name"
 value = {String@4358} "暗夜猎手"

程序运行到第二个put时的Watches值:

0 = {HashMap$HashMapEntry@4355} "name" -> "暗影之拳"
 key = {String@4357} "name"
 value = {String@4362} "暗影之拳"

到这里可以知道在HashMap里面只能有一个相同的key,一个key对应一个value,当put相同的key时,后面的value会覆盖之前的value。


再来看一看List

当我们给List添加由一个引用添加元素时候,在添加完成之后,我们对这个引用内容的更改都会导致List中元素的相应部分的更改。

和HashMap不同的是List没有key,也就是我们可以往List中添加“相同”的引用到不同的元素当中去。这里的相同指得是完成的相同,不仅是引用所指向内容的相同。包括引用的地址都是相同的。即这个引用根本没有任何变化(不存在销毁和创建)。


值得注意的是下面这段代码

while(1){
HashMap<String,String>  dataMap = new HashMap<String,String>();
}

dataMap是局部变量,属于{},所以每次这句代码执行完成之后,判断while内部条件是否成立的时候,dataMap都会销毁。当while为真的时候,dataMap重新创建。一般这两个引用的地址是不想同的。

回到最开始的那个问题

mListMap中共有两个元素,name字段都是“暗影之拳”,des字段都是“你们快点逃命不然人家就享受不到追杀的乐趣了!”。


如果有错误的地方,欢迎各位在评论去留下宝贵的意见。共同进步,共同学习。在此感谢!



<think>我们正在讨论JavaListadd()方法。根据引用[1]和引用[2],Listadd方法用于向列表中添加元素。下面我将详细解释add方法的使用。 在Java中,List接口提供了多个add方法的重载,用于向列表中添加元素。最常用的有两种形式: 1. `boolean add(E e)`:将指定的元素追加到此列表的末尾。 2. `void add(int index, E element)`:将指定的元素插入此列表中的指定位置。 下面我们通过示例代码来详细说明这两种方法的使用。 ### 1. 在列表末尾添加元素(`boolean add(E e)`) ```java import java.util.ArrayList; import java.util.List; public class ListAddExample { public static void main(String[] args) { // 创建一个ArrayList List<String> list = new ArrayList<>(); // 使用add(E e)方法在列表末尾添加元素 list.add("Apple"); list.add("Banana"); list.add("Orange"); // 打印列表 System.out.println(list); // 输出: [Apple, Banana, Orange] } } ``` ### 2. 在指定位置插入元素(`void add(int index, E element)`) ```java import java.util.ArrayList; import java.util.List; public class ListAddAtIndexExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Banana"); list.add("Orange"); // 在索引1的位置(即第二个位置)插入"Grape" list.add(1, "Grape"); System.out.println(list); // 输出: [Apple, Grape, Banana, Orange] } } ``` ### 注意事项 1. **索引范围**:使用`add(int index, E element)`时,索引`index`必须在0和当前列表大小(包含)之间。也就是说,`index`的有效范围是`[0, size()]`。如果索引超出这个范围,将抛出`IndexOutOfBoundsException`。 示例: ```java // 错误的索引 list.add(5, "Mango"); // 如果当前列表大小小于5,将抛出异常 ``` 2. **容量问题**:对于`ArrayList`,当添加元素时,如果内部数组已满,则会自动扩容(通常扩容为原来的1.5倍)。而`LinkedList`则没有容量限制,因为它是链表结构。 3. **返回值**:`boolean add(E e)`方法总是返回`true`(因为List允许重复元素,所以添加操作总是成功的)。这与`Set`的`add`方法不同,`Set`的`add`方法可能会因为元素已存在而返回`false`。 4. **允许null和重复元素**:List允许添加null元素,也允许添加重复元素。 示例: ```java List<String> list = new ArrayList<>(); list.add(null); // 添加null list.add("Apple"); list.add("Apple"); // 重复元素 System.out.println(list); // 输出: [null, Apple, Apple] ``` ### 不同List实现类的性能考虑 - **ArrayList**:在列表末尾添加元素(`add(E e)`)的时间复杂度是$O(1)$(分摊时间,因为可能涉及扩容)。在列表中间或开头插入元素(`add(int index, E element)`)需要移动元素,时间复杂度为$O(n)$。 - **LinkedList**:在列表末尾添加元素(`add(E e)`)的时间复杂度是$O(1)$。在指定位置插入元素(`add(int index, E element)`)需要先遍历到指定位置(时间复杂度$O(n)$),然后插入操作是$O(1)$。 因此,如果频繁在列表中间插入元素,使用`LinkedList`可能更高效。 ### 示例:比较ArrayList和LinkedList的插入性能 ```java import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class PerformanceComparison { public static void main(String[] args) { int size = 100000; // ArrayList在末尾添加 List<Integer> arrayList1 = new ArrayList<>(); long start = System.currentTimeMillis(); for (int i = 0; i < size; i++) { arrayList1.add(i); // 在末尾添加 } long end = System.currentTimeMillis(); System.out.println("ArrayList在末尾添加" + size + "个元素耗时: " + (end - start) + "ms"); // ArrayList在开头添加 List<Integer> arrayList2 = new ArrayList<>(); start = System.currentTimeMillis(); for (int i = 0; i < size; i++) { arrayList2.add(0, i); // 在开头添加 } end = System.currentTimeMillis(); System.out.println("ArrayList在开头添加" + size + "个元素耗时: " + (end - start) + "ms"); // LinkedList在末尾添加 List<Integer> linkedList1 = new LinkedList<>(); start = System.currentTimeMillis(); for (int i = 0; i < size; i++) { linkedList1.add(i); // 在末尾添加 } end = System.currentTimeMillis(); System.out.println("LinkedList在末尾添加" + size + "个元素耗时: " + (end - start) + "ms"); // LinkedList在开头添加 List<Integer> linkedList2 = new LinkedList<>(); start = System.currentTimeMillis(); for (int i = 0; i < size; i++) { linkedList2.add(0, i); // 在开头添加 } end = System.currentTimeMillis(); System.out.println("LinkedList在开头添加" + size + "个元素耗时: " + (end - start) + "ms"); } } ``` 运行结果可能类似于: ``` ArrayList在末尾添加100000个元素耗时: 10ms ArrayList在开头添加100000个元素耗时: 1500ms LinkedList在末尾添加100000个元素耗时: 15ms LinkedList在开头添加100000个元素耗时: 20ms ``` ### 总结 - 使用`add(E e)`在列表末尾添加元素。 - 使用`add(int index, E element)`在指定位置插入元素。 - 注意索引的有效范围(0到size())。 - 根据实际需求选择合适的List实现类(ArrayList或LinkedList)以优化性能。 §§相关问题§§ 1. JavaList的set()方法有什么作用? 2. 如何从List中删除元素? 3. ArrayList和LinkedList在内存使用上有何区别? 4. 如何避免Listadd操作导致的并发修改异常? 5. ListaddAll()方法如何使用?</think>### Java List `add()` 方法使用详解 在 Java 中,List 接口的 `add()` 方法是向集合添加元素的核心方法,主要分为两种形式: #### 1. 在列表末尾添加元素 ```java boolean add(E e) ``` - **功能**:将元素追加到列表末尾 - **返回值**:总是返回 `true`(因为 List 允许重复元素) - **示例**: ```java List<String> fruits = new ArrayList<>(); fruits.add("Apple"); // ["Apple"] fruits.add("Banana"); // ["Apple", "Banana"] fruits.add(null); // 允许添加 null ["Apple", "Banana", null] ``` #### 2. 在指定位置插入元素 ```java void add(int index, E element) ``` - **功能**:在指定索引位置插入元素 - **注意事项**: - 索引必须满足:$0 \leq \text{index} \leq \text{size()}$ - 索引越界会抛出 `IndexOutOfBoundsException` - **示例**: ```java List<String> colors = new ArrayList<>(); colors.add("Red"); // ["Red"] colors.add("Blue"); // ["Red", "Blue"] colors.add(1, "Green"); // 在索引1插入 ["Red", "Green", "Blue"] ``` #### 关键特性说明 1. **支持重复元素**: ```java list.add("A"); list.add("A"); // 允许重复 ["A", "A"] ``` 2. **允许 null 值**: ```java list.add(null); // 合法操作 ``` 3. **动态扩容**: - ArrayList 自动扩容(默认扩容1.5倍) - LinkedList 无容量限制 4. **时间复杂度**: - ArrayList- 末尾添加:$O(1)$(分摊时间) - 中间插入:$O(n)$(需要移动元素) - LinkedList- 末尾添加:$O(1)$ - 中间插入:$O(n)$(需要遍历到位置) #### 不同 List 实现的对比 | 操作 | ArrayList | LinkedList | |--------------------|-----------------|-----------------| | `add(e)` (末尾) | $O(1)$ 分摊时间 | $O(1)$ | | `add(index, e)` | $O(n)$ | $O(n)$ | | 内存占用 | 连续内存 | 分散内存+指针 | #### 使用示例:对象列表操作 ```java class Product { private int id; private String name; public Product(int id, String name) { this.id = id; this.name = name; } } List<Product> products = new ArrayList<>(); products.add(new Product(1, "Laptop")); // 添加对象 products.add(0, new Product(2, "Phone")); // 在开头插入 ``` #### 注意事项 1. **并发修改**: ```java // 遍历时修改会抛出 ConcurrentModificationException for (String item : list) { if (item.equals("A")) { list.add("B"); // 危险操作! } } ``` 2. **容量预分配**(针对 ArrayList): ```java // 优化:预先分配容量避免频繁扩容 List<String> largeList = new ArrayList<>(1000); ``` 3. **索引验证**: ```java // 安全写法:检查索引范围 if (index >= 0 && index <= list.size()) { list.add(index, element); } ``` #### 与 Set 的差异 | 特性 | Listadd() | Set 的 add() | |--------------|------------------------|------------------------| | 允许重复 | ✓ | ✗ | | 允许 null | ✓ | 取决于实现类 | | 返回值含义 | 总是 true | 元素不存在时返回 true | | 元素顺序 | 保持插入顺序 | 不保证顺序 | > List 作为 Java 集合框架中最基础的数据结构,其 `add()` 方法的正确使用是开发高效 Java 程序的基础[^2]。理解不同实现的底层原理有助于在特定场景做出最优选择[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值