泛型&数据结构

1. 泛型Generics

1.1 泛型的概述

泛型是JDK5.0以后增加的,他可以帮助我们建立类型安全的集合。在使用了泛型的集合中,不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,使代码可读性和安全性更高。

  • 不使用泛型时,会在程序运行时期检查元素的类型是否为String,强转不当会出现类型转换异常

  • 使用泛型时,在编译时期就会进行类型检查,如果不符合集合泛型,那么就添加不进去,编译报错

说明: 集合添加了泛型后,可以在编译时期约束元素的类型,对编程有指导作用。

1.1.1 泛型擦除补偿

java分两个部分,编译和运行,泛型的出现提高了编译的安全性,正因为编译时检查完了,运行时才没事。因此泛型本质上是编译时期的技术,是给编译器用的。

程序在运行时,会将泛型擦除掉(擦除之后的类型就是Object类型),这个称为泛型擦除。

为什么要擦除呢?为了兼容运行的类加载器!运行的时候,要靠虚拟机启动类装载器,专门读取类文件解析类文件,并把类文件加载进内存的程序。JDK1.4版本,JDK1.5版本都用的这个加载器。程序只在编译的时候,将类型进行检查。如果检查完是安全的,没有错误的,运行就可以把这个泛型去掉了,因为类型就统一了。我们还想要以前的类加载器进行加载解析,并进内存。

有一个问题,取出来的时候不加强转了,你还知道取出来的是什么吗?编译器只做检查,检查完,最终这个类型被擦掉,所以这个里面和以前一样还是object,(这里应该是指,add方法中加入的元素变成了object类型了),那就意味着它们提升了。同时,下面还不能做强转,取出来就能用,这是为什么呢?

这里又做了另外一种动作:在运行时,通过获取元素的实际类型进行强制,叫做补偿(不必手动实现强制转换)。编译结束后,要是完全不告诉类装载器不合适,用了泛型补偿机制告诉类加载器,都检测完了,就编写一个补偿程序。在类装载器已有的基础上,来了以后,我根据你指定的好的元素类型,真正运行的时候就要碰到这个元素了,根据你的元素获取其类型,再对你进行一次转换就行了。

1.2 泛型的使用

泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中,将数据类型作为参数进行传递。

1.2.1 含有泛型的类

定义格式:修饰符 class 类名<代表泛型的变量> { }

使用格式:创建对象时,确定泛型的类型

注意:静态方法不能使用类上定义的泛型。

1.2.2 含有泛型的方法

定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(形参列表){}

使用格式:调用方法时,确定泛型的类型。

注意:泛型对象只能使用Object类中的方法,不能使用其特有的方法。

1.2.3 含有泛型的接口

定义格式:修饰符 interface 接口名<代表泛型的变量> { }

使用格式:

1、 定义类时确定泛型的类型。

2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型。

1.2.4 泛型通配符

泛型是在限定数据类型,当在集合或者其他地方使用到泛型后,那么这时一旦明确泛型的数据类型,那么在使用的时候只能给其传递和数据类型匹配的类型,否则就会报错。

有的时候,我们在定义方法时,根本无法确定具体集合中的元素类型是什么。为了解决这个“无法确定具体集合中的元素类型”问题,java 中,为我们提供了泛型的通配符。

通配符的几种形式:

1、无限定通配符,<?>。

2、上限通配符,<? extends Number>。表示参数类型只能是Number及其子类。

3、下限通配符,<? supper Number>。表示参数类型只能是Number及其父类。

2 队列(Queue)

1.1 队列的简介

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,队列是一种操作受限制的线性表。进行插入操作(入口)的端称为队尾,进行删除操作(出口)的端称为队头。

队列的插入操作只能在队尾操作,队列的删除操作只能在队头操作,因此队列是一种先进先出(First In First Out)的线性表,简称FIFO表。

LinkedList类实现了Queue接口,在Queue接口中提供的方法可以实现队列的相关操作。

Queue接口扩展了Collection接口,常见的方法如下所示。

方法名描述
boolean add(E e);这两个方法都是在尾部添加元素add()会在长度不够时抛出异常; offer()则不会,只返回false
boolean offer(E e);
E element();这两个方法都是查看头部元素 ,返回头部元素,但不改变队列element()会在没元素时抛出异常; peek()则返回null;
E peek();
E remove();这两个方法都是删除头部元素,返回头部元素,并且从队列中删除remove()会在没元素时抛出异常; poll()返回null;
E poll();
boolean isEmpty();判断队列是否为空,如果为空则返回true;否则返回false。

【示例】使用LinkedList实现队列操作

import java.util.LinkedList;
import java.util.Queue;
/*
    @author mashushu
    @date 2023/7/17 
    @description 队列的介绍
         队列在JDK中的形式是:Queue接口,也就是说实现了Queue接口的类都具有队列的功能
​
        队列的体系结构:
            LinkedList -实现--> Deque  -继承--> Queue -继承--> Collection
        Queue接口的常用方法:
        boolean add(E e); 在队列尾部添加元素, add()会在长度不够时抛出异常
        boolean offer(E e); 在队列尾部添加元素, offer()则不会,只返回false
        E element();   查看头部元素 ,返回头部元素,但不改变队列 , element()会在没元素时抛出异常;
        E peek();      查看头部元素 ,返回头部元素,但不改变队列 ,  peek()则返回null;
        E remove();  删除头部元素,返回头部元素,并且从队列中删除,remove()会在没元素时抛出异常;
        E poll();    删除头部元素,返回头部元素,并且从队列中删除,poll()返回null;
        boolean isEmpty(); 判断队列是否为空,如果为空则返回true;否则返回false。
*/
public class QueueDemo1 {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(2);
        queue.add(5);
        queue.add(8);
        queue.add(1);
        queue.add(4);
        System.out.println("queue = " + queue); // list = [2, 5, 8, 1, 4]
​
        // 遍历队列并删除队列元素
        while(!queue.isEmpty()){
            Integer remove = queue.remove();
            System.out.println("remove = " + remove);
        }
        System.out.println("queue = " + queue);
    }
}

1.2 队列的实现

1.2.1 数组模拟队列实现

【示例】顺序队列的实现

public class ArrayQueue<T> {
    /**
     * 模拟队列的数组
     */
    private Object[] elementData;
    /**
     * 保存队首的指针
     */
    private int front;
    /**
     * 保存队尾的指针
     */
    private int rear;
    /**
     * 无参构造方法
     */
    public ArrayQueue() {
        // 设置数组的默认空间长度为10
        this(10);
    }
​
    /**
     * 有参构造方法
     * @param capcaity 设置数组的空间长度
     */
    public ArrayQueue(int capcaity) {
        // 处理capcaity参数不合法的情况
        if (capcaity < 0) {
            throw new IllegalArgumentException("参数不合法异常,capcaity:" + capcaity);
        }
        // 创建指定空间长度的数组
        this.elementData = new Object[capcaity];
    }
​
    /**
     * 获取队列中元素的个数
     * @return 返回队列中元素的个数
     */
    public int getSize() {
        return rear - front;
    }
​
    /**
     * 入队列操作
     * @param element
     */
    public void add(T element) {
        // 判断队列是否已满
        if (isFull()) {
            throw new RuntimeException("队列已满,无法执行入队列操作");
        }
        // 执行入队操作
        elementData[rear++] = element;
    }
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列首元素
        T element = (T)elementData[front];
        // 把front索引位置元素设置为默认值,并更新front索引值
        elementData[front++] = null;
        // 返回被删除的队首元素
        return element;
    }
​
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 获取并返回队列的首元素
        return (T)elementData[front];
    }
​
    /**
     * 判断队列是否已满
     * @return 队列已满,则返回true;队列未满,则返回false。
     */
    private boolean isFull() {
        return rear == elementData.length;
    }
​
    /**
     * 判断队列是否为空
     * @return 队列为空,则返回true;队列不为空,则返回false。
     */
    public boolean isEmpty() {
        return front == rear;
    }
}

【示例】循环队列的实现

public class ArrayQueue<T> {
    /**
     * 模拟队列的数组
     */
    private Object[] elementData;
    /**
     * 保存队首的指针
     */
    private int front;
    /**
     * 保存队尾的指针
     */
    private int rear;
    /**
     * 无参构造方法
     */
    public ArrayQueue() {
        // 设置数组的默认空间长度为10
        this(10);
    }
​
    /**
     * 有参构造方法
     * @param capcaity 设置数组的空间长度
     */
    public ArrayQueue(int capcaity) {
        // 处理capcaity参数不合法的情况
        if (capcaity < 0) {
            throw new IllegalArgumentException("参数不合法异常,capcaity:" + capcaity);
        }
        // 创建指定空间长度的数组
        this.elementData = new Object[capcaity];
    }
​
    /**
     * 获取队列中元素的个数
     * @return 返回队列中元素的个数
     */
    public int getSize() {
        if (rear >= front) {
            return rear - front;
        }
        else {
            return (rear + elementData.length) - front;
        }
    }
​
    /**
     * 入栈操作
     * @param element
     */
    public void add(T element) {
        // 判断队列是否已满
        if (isFull()) {
            throw new RuntimeException("队列已满,无法执行入队列操作");
        }
        // 执行入队操作
        elementData[rear] = element;
        // 更新rear的值
        rear = (rear + 1) % elementData.length;
    }
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列首元素
        T element = (T)elementData[front];
        // 把front索引位置元素设置为默认值
        elementData[front] = null;
        // 更新front索引值
        front = (front + 1) % elementData.length;
        // 返回被删除的队首元素
        return element;
    }
​
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 获取并返回队列的首元素
        return (T)elementData[front];
    }
​
    /**
     * 判断队列是否已满
     * @return 队列已满,则返回true;队列未满,则返回false。
     */
    private boolean isFull() {
        return front == (rear + 1) % elementData.length;
    }
​
    /**
     * 判断队列是否为空
     * @return 队列为空,则返回true;队列不为空,则返回false。
     */
    public boolean isEmpty() {
        return front == rear;
    }
}
1.2.2 链表模拟队列实现
public class LinkedListQueue<T> {
    /**
     * 保存队列首节点
     */
    private QueueNode<T> front;
    /**
     * 保存队列尾结点
     */
    private QueueNode<T> rear;
    /**
     * 保存实际存放元素的个数
     */
    private int size;
    /**
     * 获取队列实际存储元素的个数
     */
    private int getSize() {
        return size;
    }
​
    /**
     * 入队列操作
     * @param element
     */
    public void add(T element) {
        // 把需要入队的元素封装成节点对象
        // 处理链表为空的情况
        if (rear == null) {
            // 把元素封装成对象,并且只为队尾
            rear = new QueueNode<>(element, null);
            // 更新front的值
            front = rear;
        }
        // 处理链表非空的情况
        else {
            // 把元素封装成对象,并添加到队尾
            rear.next = new QueueNode<>(element, null);
            // 更新rear的值
            rear = rear.next;
        }
        // 更新size的值
        size++;
    }
​
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列的首元素值
        T element = front.data;
        // 更新front的指向,让front指向其下一个节点
        front = front.next;
        // 更新size的值
        size--;
        // 返回出队的元素
        return element;
    }
    
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 返回队列首元素
        return front.data;
    }
​
    /**
     * 判断队列是否为空
     * @return 如果队列为空,则返回true;如果队列非空,则返回false。
     */
    public boolean isEmpty() {
        return size == 0;
    }
​
    /**
     * 单链表节点类
     */
    private static class QueueNode<T> {
        /**
         * 节点中存放的数据
         */
        private T data;
        /**
         * 指向下一个节点
         */
        private QueueNode<T> next;
        /**
         * 构造方法
         */
        public QueueNode(T data, QueueNode<T> next) {
            this.data = data;
            this.next = next;
        }
    }
}

3. 栈(Stack)

3.1 栈的简介

栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作,栈的重要特点为:后进先出先进后出

栈的一个最重要的特征就是栈的插入和删除只能在栈顶进行,所以每次删除的元素都是最后进栈的元素,故栈也被称为后进先出(Last In First Out)的线性表,简称LIFO表。

Java中有一个类Stack,用于表示栈,但这个类已经过时了。Java中没有单独的栈接口,栈相关方法包括在了表示双端队列的接口Deque中,主要有四个方法:

方法名描述
void push(E e);入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。
E pop();出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。
E peek();查看栈头部元素,不修改栈,如果栈为空,返回null。
boolean isEmpty();判断栈是否为空,如果为空则返回true;否则返回false。

【示例】使用LinkedList实现栈操作

/*
    @author mashushu
    @date 2023/7/19 
    @description 栈结构
        void push(E e);入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。
        E pop();出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。
        E peek();查看栈头部元素,不修改栈,如果栈为空,返回null。
        boolean isEmpty();判断栈是否为空,如果为空则返回true;否则返回false。
*/
public class DequeDemo1 {
    public static void main(String[] args) {
        Deque<Integer> deque = new LinkedList<>();
        deque.push(11);
        deque.push(22);
        deque.push(33);
        deque.push(44);
        deque.push(55);
        System.out.println(deque);//[55, 44, 33, 22, 11]
        while (!deque.isEmpty()){
            System.out.println(deque.pop());
        }
        System.out.println(deque);
    }
}

3.2 栈的实现方式

每个栈都有一个栈顶指针,它初始值为-1,且总是指向最后一个入栈的元素,栈有两种处理方式,即进栈(push)和出栈(pop),因为在进栈只需要移动一个变量存储空间,所以它的时间复杂度为O(1),但是对于出栈分两种情况,栈未满时,时间复杂度也为O(1),但是当栈满时,需要重新分配内存,并移动栈内所有数据,所以此时的时间复杂度为O(n)。以下举例栈结构的两种实现方式,顺序表存储和链表存储。

3.2.1 数组模拟栈实现
要求:需要限制栈的最大容量。
/**
 * 数组模拟栈(限制最大容量)
 */
public class ArrayStack<T> {
    /**
     * 存放数据的容器
     */
    private Object[] elementData;
    /**
     * 指向栈顶的指针
     */
    private int top = -1; // 默认值为-1
    /**
     * 保存栈的最大容量
     */
    private int maxSize;
    /**
     * 构造方法
     * @param maxSize 设置栈的最大容量
     */
    public ArrayStack(int maxSize) {
        this.elementData = new Object[maxSize];
        this.maxSize = maxSize;
    }
    
    /**
     * 入栈操作
     * @param value
     */
    public void push(T element) {
        // 1.判断栈是否已满
        if(isFull()) {
            throw new RuntimeException("栈已满,无法执行入栈操作");
        }
        // 2.添加数据入栈
        elementData[++top] = element;
    }
    /**
     * 出栈操作
     */
    public T pop() {
        // 1.判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈为空,无执行出栈操作");
        }
        // 2.获取需要出栈的元素
        T value = (T)elementData[top];
        // 3.清空栈顶存放的数据
        elementData[top] = null;
        // 4.修改栈顶指针的指向
        top--;
        // 6.返回需要取出的栈顶元素
        return value;
    }
    /**
     * 检查栈是否为空
     * @return
     */
    public boolean isEmpty() {
        return top == -1;
    }
    
    /**
     * 判断栈是否已满
     * @return
     */
    private boolean isFull() {
        return maxSize - 1 == top;
    }
}
3.2.2 链表模拟栈实现
要求:无需限制链表的最大容量。
/**
 * 链表模拟栈(不限制最大容量)
 */
public class LinkedListStack<T> {
    /**
     * 保存单链表首节点
     */
    private StackNode<T> topNode;
    /**
     * 保存实际添加元素的个数
     */
    private int size;
    /**
     * 入栈操作(链表的头部开始插入)
     * @param element 需要入栈的数据
     */
    public void push(T element) {
        // 1.把element元素封装成节点对象
        StackNode<E> node = new StackNode<>(element);
        // 2.把node节点添加进入链表(把node插入到topNode的前面)
        // 把node节点的next指向topNode节点
        node.next = topNode;
        // 更新topNode指向node节点
        topNode = node;
        // 3.更新size的值,也就是执行size++操作
        size++;
    }
​
    /**
     * 出栈操作(删除单链表首节点)
     * @return 返回栈顶的数据
     */
    public T pop() {
        // 判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈为空,无执行出栈操作");
        }
        // 定义tempNode变量,来零时保存headNode节点
        StackNode<T> tempNode = topNode;
        // 更新topNode的值,将topNode.next 改为:topNode
        topNode = topNode.next;
        // 完成出栈操作后,size递减
        size--;
        // 返回栈顶的数据
        return tempNode.data;
    }
​
    /**
     * 获取栈顶元素(不会删除栈顶元素)
     * @return
     */
    public T peek() {
        // 判断栈是否为空
        if (isEmpty()) {
            return null;
        }
        // 返回栈顶数据
        return topNode.data;
    }
​
    /**
     * 判断栈是否为空
     * @return 如果栈为空,则返回true;如果栈非空,则返回false。
     */
    public boolean isEmpty() {
        return size == 0;
    }
​
    /**
     * 栈节点类
     */
    private static class StackNode<T> {
        /**
         * 节点中存放的数据
         */
        private T data;
        /**
         * 指向下一个节点
         */
        private StackNode<T> next;
        /**
         * 构造方法
         * @param data
         */
        public StackNode(T data, StackNode<T> next) {
            this.data = data;
            this.next = next;
        }
    }
}

4. Iterator迭代器

4.1 Iterator类详解

java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。这样使得对容器的遍历操作与其具体的底层实现相隔离,达到解耦的效果。

Collection 集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有元素则把元素取出,然后继续再判断下一个元素,如果还有就再取出,直到把集合中的所有元素全部取出为止。这种取出方式专业术语称为迭代。

Collection 集合中把这种取元素的方式描述在 Iterator 接口中,Iterator 接口的常用方法如下:

方法名描述
boolean hasNext();判断集合中是否有下一个元素可以迭代,如果有,则返回 true。
Object next();返回迭代的下一个元素,并把指针向后移动一位。
void remove();将迭代器当前返回的元素删除(可选操作)。

对于Iterator而言,因为本身是一个接口,所以要想实例化则必须依靠Collection接口完成,也就是使用Collection接口的Iterator<E> iterator()方法。

【示例】使用迭代器遍历ArrayList元素

/*
    @author mashushu
    @date 2023/7/19 
    @description  使用迭代器遍历ArrayList集合
*/
public class IteratorDemo1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");
​
        // 获取迭代器对象
        Iterator<String> iterator = list.iterator();
​
        // 使用while循环迭代
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("--------------------------------");
        for(  Iterator<String> iterator2 = list.iterator();iterator2.hasNext();){
            System.out.println(iterator2.next());
        }
    }
}

注意:在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的 next()方法,将会抛出java.util.NoSuchElementException没有集合元素的异常。

如果在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性,从而会抛出并发修改异常(java.util.ConcurrentModificationException)。

  • Iteratorfor-each的区别

(1) for-each不但能遍历集合,还能遍历数组,但是Iterator只能遍历集合。

(2) 使用for-each遍历集合时不能删除元素,使用Iterator遍历集合时,还可以使用Iterator的remove方法删除元素。

4.2 Iterator的实现原理

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能单向移动(从前往后):

  • 使用iterator()方法要求容器返回一个Iterator,iterator()方法是java.lang.Iterable接口的抽象方法。

  • 使用hasNext()检查序列中是否还有元素。

  • 使用next()获得序列中的下一个元素。

【示例】模拟ArrayList的Iterator实现

// 集合类实现Iterable接口
class MyArrayList implements Iterable {
    // 存放数据的数组
    private Object[] elementData = {"a", "b", "c", "d", "e"};
    // 数组的长度
    private int size = elementData.length;
    // 获取元素方法
    public Object get(int index) {
        // 断索引是否越界,请字节处理,此处是阉割版。。。
        return elementData[index];
    }
    // 获取迭代器
    @Override
    public Iterator iterator() {
        return new MyIterator(0);
    }
    // 匿名内部类,实现容器的迭代器功能
    private class MyIterator implements Iterator {
        // 游标,表示下一个元素的索引位置
        private int cursor;
        // 构造方法
        public MyIterator() {
            this(0);
        }
        public MyIterator(int cursor) {
            this.cursor = cursor;
        }
        // 判断迭代器是否有下一个元素
        @Override
        public boolean hasNext() {
            return cursor != size;
        }
        // 获取下一个元素
        @Override
        public Object next() {  
            // 获取集合中当前游标对应的元素
            Object value = get(cursor);
            // 游标累加,保存下一个元素的索引位置
            cursor++;
            // 返回元素值
            return value;
        }
    }
}

4.3 ListIterator类详解

ListIterator 是 Iterator 的子接口,但它比 Iterator 更加的强大,提供了更多在迭代过程中使用的方法,ListIterator 接口常用方法:

方法名描述
boolean hasNext();以正向遍历列表时,判断迭代器后面是否还有元素。
Object next();返回列表中ListIterator指向位置后面的元素。
boolean hasPrevious();以逆向遍历列表,判断迭代器前面是否还有元素。
Object previous();返回列表中ListIterator指向位置前面的元素。
void remove();从列表中删除next()或previous()返回的最后一个元素。
void set(Object e);从列表中将next()或previous()返回的元素更改为指定元素。
void add(Object e);将指定的元素插入列表,插入位置为迭代器当前位置之前。

ListIterator接口获取方式:

public ListIterator<E> listIterator();返回列表中的列表迭代器,游标位置默认为0。

public ListIterator<E> listIterator(final int index);从列表中的指定位置开始,返回列表中的元素(游标位置为index)的列表迭代器。

【示例】 ListIterator迭代ArrayList元素

import java.util.ArrayList;
import java.util.ListIterator;
​
/*
    @author mashushu
    @date 2023/7/19 
    @description 使用ListIterator 迭代 List集合中的元素
*/
public class IteratorDemo4 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44);
        list.add(55);
​
        // 创建listIterator迭代器对象
        ListIterator<Integer> listIterator1 = list.listIterator();
​
        // 迭代遍历: 从前往后
        while (listIterator1.hasNext()) {
            System.out.println(listIterator1.next());
        }
        System.out.println("----------------------------------------");
        // 创建listIterator迭代器对象      list.size() : 指定迭代器在集合的最大索引处
        // 注意:传入集合的长度,最大取到集合的长度-1
        ListIterator<Integer> listIterator2 = list.listIterator(list.size());
​
        // 迭代遍历: 从后往前
        while (listIterator2.hasPrevious()) {
            System.out.println(listIterator2.previous());
        }
    }
}

通过 ListIterator 迭代器来操作元素,ListIterator接口允许程序员按照任一方向遍历列表,并且允许迭代期间操作集合中的元素。

【示例】迭代过程中对ArrayList元素操作

import java.util.ArrayList;
import java.util.ListIterator;
​
/*
    @author mashushu    
    @date 2023/7/19 
    @description 使用ListIterator 在迭代过程中修改元素
*/
public class IteratorDemo5 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44);
        list.add(55);
​
        // 创建listIterator迭代器对象
        ListIterator<Integer> listIterator1 = list.listIterator();
​
        // 迭代遍历: 从前往后
        while (listIterator1.hasNext()) {
            listIterator1.add(66);// 每次添加的元素会存入迭代器指针的左边
            System.out.println(listIterator1.next());
            listIterator1.set(77); // 将next() 或者 previous() 方法返回的值更改为: 77
        }
        System.out.println("list = " + list);
    }
}

Iterator和ListIterator的区别

  • Iterator应用于Set和List集合及其子类型,但是ListIterator只能用于List及其子类型。

  • Iterator和ListIterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。

  • Iterator和ListIterator都有remove()方法,但是ListIterator还有add()方法和set()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值