具体信息请查看 API 帮助文档
1. Collection
单列集合
单列集合(Collection)的特点:
-
无序性:一般情况下,单列集合不保证元素的顺序,每次遍历的顺序可能不同。
-
可重复性:单列集合中可以包含重复的元素,允许存储多个相同的对象。
-
动态大小:单列集合的大小是动态可变的,可以根据需要增加或减少元素的个数。
-
提供统一的访问方式:可以使用迭代器(Iterator)遍历元素,或者通过索引访问元素。
1.1 迭代器遍历
1.1.1 概述
迭代器遍历是一种在编程中常用的集合遍历方式。迭代器是一种对象,用于按顺序访问集合中的元素,并且可以进行元素的增删操作。迭代器提供了一种统一的访问集合元素的方式,不依赖于集合的具体实现。
使用迭代器进行集合遍历的基本步骤如下:
-
获取迭代器:通过调用集合的
iterator()
方法来获取迭代器对象。不同的集合类具有不同的迭代器实现,因此需要根据具体的集合类型来获取相应的迭代器。 -
遍历元素:使用迭代器对象的
hasNext()
方法判断是否还有下一个元素,使用next()
方法获取当前元素,并将迭代器移动到下一个位置。 -
执行操作:对每个元素执行相应的操作,例如读取元素的值、修改元素、删除元素等。
-
可选地移除元素:如果需要从集合中移除元素,可以使用迭代器的
remove()
方法。该方法删除的是上一次调用next()
方法返回的元素。 -
循环迭代:重复以上步骤,直到遍历完所有元素,或达到自定义的终止条件。
迭代器遍历的优点是:
-
可以适用于各种类型的集合,不依赖于集合的具体实现。
-
在遍历过程中可以进行元素的增删操作,不会引发并发修改异常。
-
通过迭代器可以实现不同遍历方式,如正向遍历、反向遍历、部分遍历等。
1.1.2 方法
Iterator中的常用方法 :
方法 | 描述 |
---|---|
Iterator<E> iterator() | 获取一个迭代器对象,用于遍历集合中的元素。 |
boolean hasNext() | 判断当前位置是否有元素可以被取出。如果集合还有下一个元素,则返回 true ;否则返回 false 。 |
E next() | 获取当前位置的元素,并将迭代器对象移向下一个位置。 |
void remove() | 删除迭代器对象当前位置的元素。 |
例如:
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
//next方法的作用:获取元素并移动指针
String str = it.next();
System.out.print(str );
}
细节:
-
如果迭代器已经指向空元素了,依旧强行调用next方法,则会报错NoSuchElementException;
-
迭代器遍历完毕,指针不会复位,依旧指向集合最后,如果想要再次将集合遍历一遍,只能再次创建一个新的迭代器对象,用新的迭代器对象再次遍历;
-
循环中只能用一次next方法;
-
迭代器遍历时,不能用集合的方法进行增加或者删除(例如:集合提供的remove方法)。
如果非要删除,可以用迭代器提供的remove方法进行删除。
1.1.3 代码示例
package text.text02;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
迭代器遍历:
Iterator<E> iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
细节:
1.如果迭代器已经指向空元素了,依旧强行调用next方法,则会报错NoSuchElementException;
2.迭代器遍历完毕,指针不会复位,依旧指向集合最后,如果想要再次将集合遍历一遍,只能再次创建一个新的迭代器对象,用新的迭代器对象再次遍历;
3.循环中只能用一次next方法;
4.迭代器遍历时,不能用集合的方法进行增加或者删除(例如:集合提供的remove方法)。
如果非要删除,可以用迭代器提供的remove方法进行删除。
*/
public class text24A {
public static void main(String[] args) {
//创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
coll.add("eee");
//调用iterator()方法创建迭代器对象(返回值为迭代器对象)
Iterator<String> it = coll.iterator();
//利用循环遍历集合,获取集合中的每一个元素
while (it.hasNext()) {
//next方法的作用:获取元素并移动指针
String str = it.next();
System.out.print(str + " "); //aaa bbb ccc ddd eee
}
System.out.println();
//1.如果迭代器已经指向空元素了,依旧强行调用next方法,则会报错NoSuchElementException;
//System.out.println(it.next()); //Exception in thread "main" java.util.NoSuchElementException
//at java.util.ArrayList$Itr.next(ArrayList.java:864)
//at text.text02.text24A.main(text24A.java:45)
//2.迭代器遍历完毕,指针不会复位,依旧指向集合最后
System.out.println(it.hasNext()); //false,说明该位置没有元素,即指针没有复位
//如果想要再次将集合遍历一遍,只能再次创建一个新的迭代器对象,用新的迭代器对象再次遍历;
Iterator<String> iterator = coll.iterator(); //新的迭代器对象
while (iterator.hasNext()) {
String str = iterator.next();
System.out.print(str + " "); //aaa bbb ccc ddd eee
}
System.out.println();
//3.循环中只能用一次next方法;
/*Iterator<String> iterator1 = coll.iterator();
while (iterator1.hasNext()) {
String str1 = iterator1.next();
String str2 = iterator1.next();
System.out.println(str1); //aaa ccc Exception in thread "main" java.util.NoSuchElementException at java.util.ArrayList$Itr.next(ArrayList.java:864) at text.text02.text24A.main(text24A.java:65)
System.out.println(str2); //bbb ddd
}
*/
//4.迭代器遍历时,不能用集合的方法进行增加或者删除。
//出现的问题
/*Iterator<String> iterator2 = coll.iterator();
while (iterator2.hasNext()) {
String str = iterator2.next();
if (str.equals("aaa")) {
coll.remove(str);
}
}
System.out.println(coll); //Exception in thread "main" java.util.ConcurrentModificationException(并发修改异常) at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911) at java.util.ArrayList$Itr.next(ArrayList.java:861) at text.text02.text24A.main(text24A.java:75) */
//解决方法
Iterator<String> iterator3 = coll.iterator();
while (iterator3.hasNext()) {
String str = iterator3.next();
if (str.equals("aaa")) {
iterator3.remove();
}
}
System.out.println(coll); //[bbb, ccc, ddd, eee]
}
}
1.1.4 输出结果
-
利用循环遍历集合,获取集合中的每一个元素
-
如果迭代器已经指向空元素了,依旧强行调用next方法,则会报错NoSuchElementException;
-
迭代器遍历完毕,指针不会复位,依旧指向集合最后
-
如果想要再次将集合遍历一遍,只能再次创建一个新的迭代器对象,用新的迭代器对象再次遍历;
-
循环中只能用一次next方法;
-
迭代器遍历时,不能用集合的方法进行增加或者删除
-
出现的问题
-
解决方法
-
1.1.5 注意事项
-
并发修改异常:在迭代器遍历过程中,如果直接使用集合的方法(如
add()
、remove()
)修改集合的结构,可能会引发并发修改异常ConcurrentModificationException
。为避免此类异常,应该使用迭代器自身的remove()
方法来删除元素。 -
一次性遍历:迭代器通常是单向的,即不支持回溯或跳跃遍历集合。所以,在开始遍历之前,应确认不会在遍历过程中修改集合的结构,以防止出现元素丢失、重复遍历等问题。
-
删除元素时机:当需要在迭代器遍历过程中删除元素时,应先调用
next()
方法获取当前元素,然后再调用迭代器的remove()
方法删除元素。否则,会导致删除错误或引发异常。 -
遍历性能:迭代器遍历是一种逐个访问集合元素的方式,对于大型集合或需要频繁遍历的场景,可能会对性能有一定影响。如果对性能有更高要求,可以考虑其他遍历方式,如增强型 for 循环、Stream API 等。
-
不同类型的集合可能具有不同的迭代器实现方式,因此在使用迭代器进行遍历时,需要根据具体集合类型来获取相应的迭代器。
1.1.6 源码分析
迭代器遍历相关的三个方法:
-
Iterator iterator() :获取一个迭代器对象
-
boolean hasNext() :判断当前指向的位置是否有元素
-
E next() :获取当前指向的元素并移动指针
1.2 增强for遍历
1.2.1 概述
增强for循环(Enhanced for Loop),也被称为for-each循环,是Java中一种用于遍历集合或数组元素的简化循环结构。它提供了一种更简洁、易读的方式来遍历集合中的元素,无需手动使用索引或迭代器。
增强for循环的主要优点是简化了遍历集合或数组的代码,使代码更加简洁易读。
此外,增强for循环具有以下特点:
-
不需要手动控制索引或迭代器,遍历过程更加简便。
-
仅适用于遍历访问集合中的元素,无法获取当前元素的索引或修改集合结构。
-
不需要额外考虑越界或空元素的问题,减少了容易出错的可能性。
需要注意的是,增强for循环在遍历过程中是只读的,无法修改集合中的元素。如果需要对元素进行修改,可以使用普通for循环或迭代器进行遍历。
增强for循环:
-
它是JDK5之后出现的,其内部原理是一个Iterator迭代器
-
实现Iterable接口的类才可以使用迭代器和增强for
-
简化数组和Collection集合的遍历
1.2.2 方法
格式:
for(数据类类型 变量名 : 集合/数组){
}
细节:
-
变量名其实是一个第三方变量,在循环的过程中一次表示集合中的每一个数据
-
修改增强for中的变量值,不会改变集合中的原有数据
1.2.3 代码示例
package text.text02;
import java.util.ArrayList;
import java.util.Collection;
/*
增强for循环:
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
格式:for(数据类类型 变量名 : 集合/数组){
}
细节:
1,变量名其实是一个第三方变量,在循环的过程中一次表示集合中的每一个数据
2. 修改增强for中的变量值,不会改变集合中的原有数据
*/
public class text25A {
public static void main(String[] args) {
//创建集合并添加数据
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//利用增强for循环遍历集合
//String为数据类型
//s:为变量名
//coll:为集合的名字
for (String s : coll) {
System.out.print(s + "\t"); //aaa bbb ccc ddd
}
System.out.println();
System.out.println(coll); //[aaa, bbb, ccc, ddd]
}
}
1.2.4 输出结果
- 利用增强for循环遍历集合
1.2.5 注意事项
-
只读访问:增强for循环是只读访问集合或数组元素的,无法在循环体中修改集合或数组的结构。如果尝试在循环体中修改集合或数组,比如使用
collection.remove(element)
,将会引发ConcurrentModificationException
异常。 -
局部变量:增强for循环中声明的元素变量是局部变量,其作用域只在当前循环中有效。在循环外部无法访问到该变量。
-
不支持索引操作:增强for循环无法获取当前元素的索引值。如果需要索引值进行操作,需要使用传统的for循环或者配合其他变量来实现。
-
遍历顺序:增强for循环按照集合或数组中元素的顺序进行遍历,无法保证按照特定的顺序(比如插入顺序或排序顺序)进行遍历。
-
判空处理:在使用增强for循环遍历集合或数组之前,需要确保集合或数组不为null且不为空。否则,如果对一个空集合或数组进行遍历,将会导致
NullPointerException
异常。 -
增强for循环适用性:增强for循环适用于遍历实现了
Iterable
接口的集合类(如List、Set等)或数组。对于其他类型的集合或自定义类,可能无法使用增强for循环,需要使用传统的for循环或迭代器来进行遍历。
1.3 Lambda表达式遍历
1.3.1 概述
Lambda表达式是Java 8引入的一种函数式编程的特性,它提供了一种更简洁、灵活的方式来表示匿名函数。在遍历集合或数组时,Lambda表达式可以与函数式接口结合使用,实现更简洁的遍历代码。
Lambda表达式在遍历集合或数组中的元素时,通常结合Stream API来使用。Stream API提供了一种函数式的流式操作方式,可以通过链式调用一系列方法来处理集合或数组中的元素。
在Lambda表达式中,箭头符号->
将参数列表与函数体分隔开来。参数列表可以省略参数类型,如果只有一个参数,还可以省略小括号。函数体可以是单个表达式或代码块。
1.3.2 方法
Lambda表达式遍历:
default void forEach(Consumer<? super T> action)
1.3.3 代码示例
package text.text02;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
/*
Lambda表达式遍历:
default void forEach(Consumer<? super T> action):
*/
public class text26A {
public static void main(String[] args) {
//创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//先利用匿名内部类遍历
coll.forEach(new Consumer<String>() {
//底层原理:其实也会自己遍历集合,一次得到每一个元素,把得到的每一个元素,传递给下面的accep方法
//s依次表示集合中的每一个元素
@Override
public void accept(String s) {
System.out.print(s + "\t"); //aaa bbb ccc ddd
}
});
System.out.println();
//利用Lambda表达式遍历
coll.forEach(s -> System.out.print(s + "\t")); //aaa bbb ccc ddd
}
}
1.3.4 输出结果
-
先利用匿名内部类遍历
-
利用Lambda表达式遍历
1.3.5 注意事项
-
函数式接口:Lambda表达式需要与函数式接口(Functional Interface)结合使用。函数式接口是只有一个抽象方法的接口,可以使用Lambda表达式作为该接口的实现。确保目标上下文期望的是函数式接口,否则无法使用Lambda表达式。
-
Lambda表达式语法:Lambda表达式由
->
箭头符号分成两个部分:左侧是参数列表,右侧是函数体。参数列表可以省略参数类型,如果只有一个参数,还可以省略小括号。函数体可以是单个表达式或代码块。 -
变量捕获:Lambda表达式可以访问外部的局部变量或成员变量,但是这些变量必须是隐式具有
final
或effectively final
特性的(即只能赋值一次,不可再修改)。这是因为Lambda表达式内部会创建一个对变量的拷贝。 -
异常处理:Lambda表达式中的异常必须显式处理或向上抛出,不能直接在函数体中捕获或抛出异常。如果Lambda表达式中抛出了异常,并且函数式接口的抽象方法没有声明抛出该异常,将会导致编译错误。
-
对象方法引用:如果Lambda表达式的函数体只是调用一个已存在的方法,可以使用对象方法引用(Object Method Reference)来代替Lambda表达式,提高代码的可读性。
-
基本类型:Lambda表达式的参数和返回值可以是任意类型,包括基本类型和引用类型。但是在某些情况下,由于自动装箱和拆箱的性能开销,使用基本类型可能更高效。
-
嵌套Lambda表达式:Lambda表达式可以嵌套使用,即在Lambda表达式中再使用Lambda表达式。但是要注意避免过度嵌套,以保持代码的可读性和维护性。
2. Map
双列集合
双列集合(Map)的特点:
-
键值对:双列集合以键(key)和值(value)的方式存储数据,每个元素是一个键值对对象。
-
键的唯一性:每个键是唯一的,不允许重复的键。但值可以重复。
-
无序性:Map的实现类(如HashMap)通常不保证元素的顺序,每次遍历的顺序可能不同。
-
可通过键快速访问值:通过给定一个键的值,可以快速找到对应的值。
-
动态大小:Map的大小是动态可变的,可以根据需要增加或减少键值对的个数。
方法介绍:
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
2.1 键找值遍历
2.1.1 概述
键找值的遍历是指在双列集合(如Map)中,通过遍历键来获取对应的值的过程。
-
获取键集合:首先,通过调用Map的keySet()方法获取所有的键集合。这个方法将返回一个Set集合,其中包含了Map中所有的键。
-
遍历键集合:使用迭代器(Iterator)或增强型for循环,依次遍历键集合中的每个键。
-
通过键获取值:在遍历过程中,通过调用Map的get(key)方法,传入当前遍历到的键,来获取对应的值。
-
处理获取到的值:根据具体的需求,对获取到的值进行相应的处理操作。
需要注意的是,遍历过程中的键的顺序通常是不确定的,特别是在使用的是HashMap这样的实现类时。如果需要按特定的顺序遍历键集合,可以考虑使用TreeMap这样有序的Map实现类。
2.1.2 代码示例
package text.text02;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
/*
Map集合的第一种遍历方式:键找值
1.获取所有的键,把这些键放到一个单列集合中 keySet()方法
2.遍历单列集合,获取每一个键
3.利用Map集合里面的get方法获取对应的值
*/
public class text46 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
//添加元素
map.put("刘备", "孙尚香");
map.put("李白", "王昭君");
map.put("孙策", "小乔");
map.put("周瑜", "小乔");
map.put("孙悟空", "露娜");
map.put("亚瑟", "安琪拉");
//遍历集合
//调用KeySet方法,将map集合中的所有的键放到单列集合中
Set<String> ks = map.keySet();
System.out.println("===========利用单列集合中的增强for的方式遍历===========");
//1.利用单列集合中的增强for的方式遍历
for (String key : ks) {
//调用Map集合里面的get方法获取对应的值
String value = map.get(key);
System.out.println(key + " = " + value);
}
System.out.println("===========利用单列集合中的迭代器的方式遍历===========");
//2.利用单列集合中的迭代器的方式遍历
//调用单列集合的遍历方式遍历单列集合,获取单列集合里面的每一个数据(键)
Iterator<String> it = ks.iterator();
while (it.hasNext()) {
String key = it.next();
//调用Map集合里面的get方法,获取到对应的值
String value = map.get(key);
System.out.println(key + " = " + value);
}
System.out.println("===========利用单列集合中的Lambda表达式的方式遍历===========");
//3.利用单列集合中的Lambda表达式的方式遍历
ks.forEach(new Consumer<String>() {
@Override
public void accept(String key) {
//调用Map集合里面的get方法,获取到对应的值
String value = map.get(key);
System.out.println(key + " = " + value);
}
});
}
}
2.1.3 输出结果
- 利用单列集合中的增强for的方式遍历
- 利用单列集合中的迭代器的方式遍历
- 利用单列集合中的Lambda表达式的方式遍历
2.1.4 注意事项
-
空指针判断:在获取键对应的值之前,最好先判断键是否存在。可以使用containsKey()方法来判断指定键是否存在于Map中。
-
并发修改:在进行键找值的遍历时,如果在遍历过程中对Map进行了修改(如增加、删除键值对),可能会导致ConcurrentModificationException异常。为了避免这种情况,可以通过使用迭代器的remove()方法进行删除操作,或者使用并发安全的Map实现类(如ConcurrentHashMap)来避免此问题。
-
顺序性:一般情况下,HashMap等实现类的遍历是无序的,即遍历键集合得到的键的顺序可能是不确定的。如果需要按特定的顺序遍历键集合,可以选择使用TreeMap等有序的Map实现类。
-
性能考虑:当需要频繁进行键找值的操作时,可以考虑使用HashMap这样的哈希表实现类,因为哈希表的查找时间复杂度为O(1)。而TreeMap等有序实现类的查找时间复杂度为O(logN),可能在大规模数据操作时性能较低。
-
存储的键对象要正确实现equals()和hashCode()方法:在进行键找值的遍历和查找操作时,Map会依赖键对象的equals()和hashCode()方法来判断键是否相等和生成哈希码。所以要确保存储的键对象正确实现了这两个方法,以保证键的唯一性和查找的准确性。
2.2 键值对遍历
2.2.1 概述
键值对遍历是指在双列集合(如Map)中,同时遍历键和对应的值的过程。
-
获取键值对集合:首先,通过调用Map的entrySet()方法获取所有的键值对集合。这个方法将返回一个Set集合,其中包含了Map中所有的键值对。
-
遍历键值对集合:使用迭代器(Iterator)或增强型for循环,依次遍历键值对集合中的每个键值对。
-
获取键和值:在遍历过程中,每次迭代返回一个键值对对象。通过调用键值对对象的getKey()方法可以获取当前迭代的键,通过调用getValue()方法可以获取对应的值。
-
处理获取到的键和值:根据具体的需求,对获取到的键和值进行相应的处理操作。例如,输出键和值、进行计算、修改等。
需要注意的是,遍历过程中的键值对的顺序通常是不确定的,特别是在使用的是HashMap这样的实现类时。如果需要按特定的顺序遍历键值对集合,可以考虑使用TreeMap这样有序的Map实现类。
2.2.2 代码示例
package text.text02;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
/*
Map集合的第二种遍历方式:键值对
1.通过Map集合里面的entrySet方法,获取所有的键值对对象并放入一个Set集合中
Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合 Map是外接接口,Entry是内接接口,Map.Entry<K,V>是Set集合的泛型,<K,V>是Map集合的泛型。
2.遍历Set集合,得到里面的每一个键值对对象
3.通过Map集合里的getKey方法获取键;通过Map集合里的getValue方法获取值
*/
public class text47 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
//添加元素
map.put("刘备", "孙尚香");
map.put("李白", "王昭君");
map.put("孙策", "小乔");
map.put("周瑜", "小乔");
map.put("孙悟空", "露娜");
map.put("亚瑟", "安琪拉");
//遍历集合
//通过调用Map集合中的entrySet方法获取所有的键值对对象
Set<Map.Entry<String, String>> entries = map.entrySet();
//遍历Set集合获取每一个对应的键和值
System.out.println("================利用迭代器遍历================");
//1.利用迭代器遍历
Iterator<Map.Entry<String, String>> it = entries.iterator();
while (it.hasNext()) {
//获取单列集合里的键值对对象
Map.Entry<String, String> set = it.next();
String key = set.getKey();//获取键
String value = set.getValue();//获取值
System.out.println(key + " = " + value);
}
System.out.println("================利用增强for遍历================");
//2.利用增强for遍历
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();//获取键
String value = entry.getValue();//获取值
System.out.println(key + " = " + value);
}
System.out.println("================利用lambda表达式================");
//3.利用lambda表达式
entries.forEach(new Consumer<Map.Entry<String, String>>() {
@Override
public void accept(Map.Entry<String, String> entry) {
String key = entry.getKey();//获取键
String value = entry.getValue();//获取值
System.out.println(key + " = " + value);
}
});
}
}
2.2.3 输出结果
- 利用迭代器遍历
- 利用增强for遍历
- 利用lambda表达式
2.2.4 注意事项
-
空指针判断:在遍历过程中,最好先判断键值对对象是否为空,以避免空指针异常。可以使用entrySet()方法获取的键值对集合进行判断。
-
并发修改:在进行键值对遍历时,如果在遍历过程中对Map进行了修改(如增加、删除键值对),可能会导致ConcurrentModificationException异常。为了避免这种情况,可以通过使用迭代器的remove()方法进行删除操作,或者使用并发安全的Map实现类(如ConcurrentHashMap)来避免此问题。
-
顺序性:一般情况下,HashMap等实现类的遍历是无序的,即遍历键值对集合得到的键值对的顺序可能是不确定的。如果需要按特定的顺序遍历键值对集合,可以选择使用TreeMap等有序的Map实现类。
-
存储的键对象要正确实现equals()和hashCode()方法:在进行键值对遍历和查找操作时,Map会依赖键对象的equals()和hashCode()方法来判断键是否相等和生成哈希码。所以要确保存储的键对象正确实现了这两个方法,以保证键的唯一性和查找的准确性。
-
性能考虑:当需要频繁进行键值对的遍历和操作时,可以考虑使用HashMap这样的哈希表实现类,因为哈希表的查找时间复杂度为O(1)。而TreeMap等有序实现类的查找时间复杂度为O(logN),可能在大规模数据操作时性能较低。
2.3 结合lambda表达式遍历
2.3.1 概述
结合lambda遍历Map集合:
default void forEach(BiConsumer< ? super k , ? super v > action)
BiConsumer是一个函数式接口
-
获取键值对集合:首先,通过调用Map的entrySet()方法获取所有的键值对集合。这个方法将返回一个Set集合,其中包含了Map中所有的键值对。
-
使用Lambda表达式遍历键值对集合:可以使用forEach()方法结合Lambda表达式对键值对集合进行遍历。这样可以直接在Lambda表达式中定义遍历过程的具体操作。
-
处理键和值:在Lambda表达式中可以对键和值进行相应的处理操作。通过参数列表中的两个参数可以分别获取键和值。可以根据具体的需求对键和值进行输出、计算、修改等操作。
-
lambda表达式的语法:Lambda表达式的一般语法为
(参数列表) -> { 方法体 }
。在遍历键值对时,可以将遍历的逻辑放在方法体内。例如,(key, value) -> System.out.println(key + ": " + value)
表示遍历过程中将键和值打印输出。
使用Lambda表达式可以简化键值对的遍历代码,使得代码更加简洁和可读。此外,由于Lambda表达式可以通过并行流和串行流进行并行化处理,还可以提高遍历和处理的效率。
需要注意的是,Lambda表达式在Java 8及之后的版本中引入,所以需要确保项目的Java版本支持Lambda表达式的使用。
2.3.2 代码示例
package text.text02;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/*
Map集合的第三种遍历方式:结合lambda表达式遍历Map集合
default void forEach(BiConsumer< ? super k , ? super v > action) 结合lambda遍历Map集合 BiConsumer是一个函数式接口
方法底层:
forEach其实就是利用第二种方式进行遍历,依次得到每一个键和值,再调用accept方法
entrySet()等同于Set<Map.Entry<String, String>> entries = map.entrySet()中的entries,只是省略了返回值
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
*/
public class text48 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<>();
//添加元素
map.put("刘备", "孙尚香");
map.put("李白", "王昭君");
map.put("孙策", "小乔");
map.put("周瑜", "小乔");
map.put("孙悟空", "露娜");
map.put("亚瑟", "安琪拉");
//结合lambda表达式遍历Map集合
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + " = " + value);
}
});
}
}
2.3.3 输出结果
- 结合lambda表达式遍历Map集合
2.3.4 注意事项
-
Lambda表达式的参数类型:在Lambda表达式中,可以使用自动类型推断省略参数的类型声明。但是,在遍历键值对时,建议明确指定键值对的参数类型,以提高代码的可读性和可维护性。
-
Lambda表达式的参数变量可能为null:在遍历中,Map的键或值可能为null。在使用Lambda表达式处理键或值时,需要注意空指针异常。建议在Lambda表达式中对参数进行null判断,或使用Optional类来确保安全访问。
-
并发修改:在使用Lambda表达式进行键值对遍历时,如果在遍历过程中对Map进行了修改(如增加、删除键值对),可能会导致ConcurrentModificationException异常。为了避免这种情况,可以使用并发安全的Map实现类,例如ConcurrentHashMap。
-
Lambda表达式的参数命名:为了代码的可读性,建议给Lambda表达式的参数起有意义的名称,以便在Lambda表达式中使用参数时有清晰的语义。例如,
(key, value) -> {...}
,其中key
和value
是有意义的参数名。 -
性能考虑:尽管使用Lambda表达式可以简化代码,但在处理大规模数据时,可能需要考虑性能问题。Lambda表达式的执行效率与具体的操作有关,对于复杂的操作,可能需要权衡代码简洁性和性能效率。