常见集合类
Lambda表达式
具体信息请查看 API 帮助文档
迭代器遍历是一种在编程中常用的集合遍历方式。迭代器是一种对象,用于按顺序访问集合中的元素,并且可以进行元素的增删操作。迭代器提供了一种统一的访问集合元素的方式,不依赖于集合的具体实现。
使用迭代器进行集合遍历的基本步骤如下:
获取迭代器:通过调用集合的 iterator()
方法来获取迭代器对象。不同的集合类具有不同的迭代器实现,因此需要根据具体的集合类型来获取相应的迭代器。
遍历元素:使用迭代器对象的 hasNext()
方法判断是否还有下一个元素,使用 next()
方法获取当前元素,并将迭代器移动到下一个位置。
执行操作:对每个元素执行相应的操作,例如读取元素的值、修改元素、删除元素等。
可选地移除元素:如果需要从集合中移除元素,可以使用迭代器的 remove()
方法。该方法删除的是上一次调用 next()
方法返回的元素。
循环迭代:重复以上步骤,直到遍历完所有元素,或达到自定义的终止条件。
迭代器遍历的优点是:
可以适用于各种类型的集合,不依赖于集合的具体实现。
在遍历过程中可以进行元素的增删操作,不会引发并发修改异常。
通过迭代器可以实现不同遍历方式,如正向遍历、反向遍历、部分遍历等。
Iterator中的常用方法 :
方法 | 描述 |
---|---|
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方法进行删除。
package text.text02;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
迭代器遍历:
Iterator 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 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 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]
}
}
利用循环遍历集合,获取集合中的每一个元素
迭代器遍历时,不能用集合的方法进行增加或者删除
并发修改异常:在迭代器遍历过程中,如果直接使用集合的方法(如 add()
、remove()
)修改集合的结构,可能会引发并发修改异常 ConcurrentModificationException
。为避免此类异常,应该使用迭代器自身的 remove()
方法来删除元素。
一次性遍历:迭代器通常是单向的,即不支持回溯或跳跃遍历集合。所以,在开始遍历之前,应确认不会在遍历过程中修改集合的结构,以防止出现元素丢失、重复遍历等问题。
删除元素时机:当需要在迭代器遍历过程中删除元素时,应先调用 next()
方法获取当前元素,然后再调用迭代器的 remove()
方法删除元素。否则,会导致删除错误或引发异常。
遍历性能:迭代器遍历是一种逐个访问集合元素的方式,对于大型集合或需要频繁遍历的场景,可能会对性能有一定影响。如果对性能有更高要求,可以考虑其他遍历方式,如增强型 for 循环、Stream API 等。
不同类型的集合可能具有不同的迭代器实现方式,因此在使用迭代器进行遍历时,需要根据具体集合类型来获取相应的迭代器。
增强for循环(Enhanced for Loop),也被称为for-each循环,是Java中一种用于遍历集合或数组元素的简化循环结构。它提供了一种更简洁、易读的方式来遍历集合中的元素,无需手动使用索引或迭代器。
增强for循环的主要优点是简化了遍历集合或数组的代码,使代码更加简洁易读。
此外,增强for循环具有以下特点:
不需要手动控制索引或迭代器,遍历过程更加简便。
仅适用于遍历访问集合中的元素,无法获取当前元素的索引或修改集合结构。
不需要额外考虑越界或空元素的问题,减少了容易出错的可能性。
需要注意的是,增强for循环在遍历过程中是只读的,无法修改集合中的元素。如果需要对元素进行修改,可以使用普通for循环或迭代器进行遍历。
增强for循环:
它是JDK5之后出现的,其内部原理是一个Iterator迭代器
实现Iterable接口的类才可以使用迭代器和增强for
简化数组和Collection集合的遍历
格式:
for(数据类类型 变量名 : 集合/数组){
}
细节:
变量名其实是一个第三方变量,在循环的过程中一次表示集合中的每一个数据
修改增强for中的变量值,不会改变集合中的原有数据
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]
}
}
只读访问:增强for循环是只读访问集合或数组元素的,无法在循环体中修改集合或数组的结构。如果尝试在循环体中修改集合或数组,比如使用 collection.remove(element)
,将会引发 ConcurrentModificationException
异常。
局部变量:增强for循环中声明的元素变量是局部变量,其作用域只在当前循环中有效。在循环外部无法访问到该变量。
不支持索引操作:增强for循环无法获取当前元素的索引值。如果需要索引值进行操作,需要使用传统的for循环或者配合其他变量来实现。
遍历顺序:增强for循环按照集合或数组中元素的顺序进行遍历,无法保证按照特定的顺序(比如插入顺序或排序顺序)进行遍历。
判空处理:在使用增强for循环遍历集合或数组之前,需要确保集合或数组不为null且不为空。否则,如果对一个空集合或数组进行遍历,将会导致 NullPointerException
异常。
增强for循环适用性:增强for循环适用于遍历实现了 Iterable
接口的集合类(如List、Set等)或数组。对于其他类型的集合或自定义类,可能无法使用增强for循环,需要使用传统的for循环或迭代器来进行遍历。
Lambda表达式是Java 8引入的一种函数式编程的特性,它提供了一种更简洁、灵活的方式来表示匿名函数。在遍历集合或数组时,Lambda表达式可以与函数式接口结合使用,实现更简洁的遍历代码。
Lambda表达式在遍历集合或数组中的元素时,通常结合Stream API来使用。Stream API提供了一种函数式的流式操作方式,可以通过链式调用一系列方法来处理集合或数组中的元素。
在Lambda表达式中,箭头符号->
将参数列表与函数体分隔开来。参数列表可以省略参数类型,如果只有一个参数,还可以省略小括号。函数体可以是单个表达式或代码块。
Lambda表达式遍历:
default void forEach(Consumer<? super T> action)
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
}
}
函数式接口:Lambda表达式需要与函数式接口(Functional Interface)结合使用。函数式接口是只有一个抽象方法的接口,可以使用Lambda表达式作为该接口的实现。确保目标上下文期望的是函数式接口,否则无法使用Lambda表达式。
Lambda表达式语法:Lambda表达式由->
箭头符号分成两个部分:左侧是参数列表,右侧是函数体。参数列表可以省略参数类型,如果只有一个参数,还可以省略小括号。函数体可以是单个表达式或代码块。
变量捕获:Lambda表达式可以访问外部的局部变量或成员变量,但是这些变量必须是隐式具有final
或effectively final
特性的(即只能赋值一次,不可再修改)。这是因为Lambda表达式内部会创建一个对变量的拷贝。
异常处理:Lambda表达式中的异常必须显式处理或向上抛出,不能直接在函数体中捕获或抛出异常。如果Lambda表达式中抛出了异常,并且函数式接口的抽象方法没有声明抛出该异常,将会导致编译错误。
对象方法引用:如果Lambda表达式的函数体只是调用一个已存在的方法,可以使用对象方法引用(Object Method Reference)来代替Lambda表达式,提高代码的可读性。
基本类型:Lambda表达式的参数和返回值可以是任意类型,包括基本类型和引用类型。但是在某些情况下,由于自动装箱和拆箱的性能开销,使用基本类型可能更高效。
嵌套Lambda表达式:Lambda表达式可以嵌套使用,即在Lambda表达式中再使用Lambda表达式。但是要注意避免过度嵌套,以保持代码的可读性和维护性。