Java 集合框架详谈及代码分析(Iterable->Collection->List、Set->各接口实现类、Map->各接口实现类)

news2025/2/28 6:43:57

目录

  • Java 集合框架详谈及代码分析(Iterable->Collection->List、Set->各接口实现类、Map->各接口实现类)
    • 1、集合概述
      • 1-1:Java 集合概述
      • 1-2:List、Set、Map 三者的区别?
      • 1-3:集合框架底层数据结构分析
        • 1-3-1:List 接口
          • ArrayList:底层的数据结构是 Object[] 数组
          • Vector:底层的数据结构是 Object[] 数组
          • LinkedList:底层的数据结构是 双向链表
        • 1-3-2:Set 接口
          • HashSet(元素是 无序、唯一):
          • LinkedHashSet:(元素是 有序、唯一)
          • TreeSet(元素是 有序、唯一):
        • 1-3-3:Map 接口
          • HashMap :元素是 无序存储的
          • LinkedHashMap:元素是 有序存储的
          • HashTable:元素是 无序存储的
          • TreeMap:元素是 有序存储的
      • 1-4:如何选用集合?
      • 1-5:Iterator 迭代器
        • 迭代器 Iterator 是什么?
        • 简单使用迭代器
          • 遍历 List 集合
            • 代码:
          • 遍历 Map 集合
            • 代码:
          • 小总结:
      • 1-6:线程安全和不安全的集合介绍
    • 2、List 接口集合分析
      • 2-1:ArrayList 和 Vector 的区别?
      • 2-2:ArrayList 和 LinkedList 的区别?
      • 2-3:双向链表和双向循环链表 --> 图解
    • 3、Set 接口集合分析
      • 3-1:comparable 和 Comparator 的区别
        • 3-1-1:Comparator 定制排序
          • 代码:
      • 3-2:无序性和不可重复性的含义
        • 无序性:
        • 不可重复性:
      • 3-3:HashSet、LinkedHashSet 和 TreeSet 三者的区别
    • 4、Map 接口集合分析
      • 4-1:HashMap 和 Hashtable 的区别
      • 4-2:HashMap 和 HashSet 区别
      • 4-3:HashMap 和 TreeMap 区别
        • 代码:
      • 4-4:HashSet 如何检查重复
        • hashCode() 与 equals() 的相关规定:
        • == 与 equals 的区别
      • 4-5:ConcurrentHashMap 和 Hashtable 的区别
    • 5、注意事项:
      • 不要在 foreach 循环里进行元素的 remove / add 操作
        • 代码示例:

Java 集合框架详谈及代码分析(Iterable->Collection->List、Set->各接口实现类、Map->各接口实现类)


1、集合概述

1-1:Java 集合概述

   Java 集合框架包括一组接口和类,核心接口包括 Collection、List、Set 和 Map,它们定义了不同类型的集合操作和行为。

   如图:List 和 Set 这两个集合接口都继承了 Collection 接口,而 Map 独立于 Collection 接口,用于存储键值对。

简单在IDEA工具上展示常用集合的继承关系:

List 接口的主要实现类:ArrayList、LinkedList、Vector;
Set 接口的主要实现类:HashSet、LinkedHashSet、TreeSet;
Map 接口的主要实现类:HashMap、ConcurrentHashMap、TreeMap、HashTable;

在这里插入图片描述


1-2:List、Set、Map 三者的区别?

   List:存储的元素是有序的、可重复的。
   Set:存储的元素是无序的、不可重复的。
   Map:使用键值对(key-value)进行存储,key 是唯一的、无序的、不可重复的,value 是无序的、可重复的,每个键(key)最多映射到一个值(value)。


1-3:集合框架底层数据结构分析

散列表,又叫哈希表,里面的元素是无序存储的,而链表和红黑树则是有序的。

Collection 接口下的集合:

1-3-1:List 接口

List 接口下的实现类的底层数据结构

ArrayList:底层的数据结构是 Object[] 数组
Vector:底层的数据结构是 Object[] 数组
LinkedList:底层的数据结构是 双向链表

1-3-2:Set 接口
HashSet(元素是 无序、唯一):

   基于 HashMap 实现的,底层采用 HashMap 来保存元素。

LinkedHashSet:(元素是 有序、唯一)

   LinkedHashSet 是 HashSet 的子类,其内部是通过 LinkedHashMap 来实现的。

一些源代码:

在这里插入图片描述

TreeSet(元素是 有序、唯一):

   底层是基于红黑树实现的(自平衡的排序二叉树)


1-3-3:Map 接口

key 是唯一的,value是 可重复的。

HashMap :元素是 无序存储的

   JDK1.8 之前,HashMap 是由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(通过“拉链法”解决冲突);

   JDK1.8 之后,在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认是 8 )时,会将链表转化为红黑树,以减少搜索时间。

   注意:在将链表转成红黑树之前会进行判断,如果当前数组的长度小于 64 ,那么会先进行数组扩容,而不是直接转换成红黑树,只有数组长度达到或超过 64 时,才会转成红黑树。
HashMap 数组扩容时,会将数组的容量扩充为原先的一倍,并重新分配现有的键值对到新的数组中。

LinkedHashMap:元素是 有序存储的

   LinkedHashMap 继承自 HashMap ,所以他的底层仍然是基于拉链式散列结构,即由数组+链表/红黑树组成。
   并且在这个数据结构的基础上,增加了一条 双向链表 ,作用是可以维护键值对(元素)的插入顺序和访问顺序。

   访问顺序:每次通过 get、put 等方法访问该集合里面的元素时,该元素会被移动到链表的末尾,同理,较少访问到的元素则会位于链表的头部。

在这里插入图片描述

HashTable:元素是 无序存储的

   数组 + 链表组成的,数组是 HashMap 的主体,链表则是为了解决哈希冲突而存在的。

   HashTable 基本不怎么用了,都是由 HashMap 来代替。

TreeMap:元素是 有序存储的

   基于红黑树(自平衡的排序二叉树)实现的。


1-4:如何选用集合?

需要根据键(key)获取到元素值时,就选用 Map 接口下的集合:
   存储元素需要排序时,选择 TreeMap ;
   存储元素不需要排序时,选择 HashMap ;
   元素需要保证线程安全,选择 ConcurrentHashMap ;

当我们只需要存放元素值时,就选择实现 Collection 接口的集合(List、Set):
   需要保证元素唯一时,选择实现了 Set 接口的集合:TreeSet 或 HashSet ;
   不需要保证元素唯一,选择实现 List 接口的集合:ArrayList 或 LinkedList ;


1-5:Iterator 迭代器

迭代器 Iterator 是什么?

   迭代器也是设计模式的一种,叫做 迭代器模式

源码是这样的:

在这里插入图片描述


简单使用迭代器
遍历 List 集合

   优点:如果在迭代器遍历该list集合期间,有其他线程修改了集合中的元素值,那么迭代器就会报错:ConcurrentModificationException

   注意点:如果通过迭代器遍历完了,因为业务需求,需要再遍历一次,就只能再通过 list.iterator(); 再一次获取迭代器来遍历。

在这里插入图片描述

代码:
//Java集合演示类
public class JavaCollections {
    public static void main(String[] args) {
        List<String> list = List.of("aa", "bb", "cc", "dd");
        
        //创建迭代器:从list集合中获取一个可以用来遍历元素的迭代器对象
        Iterator<String> iterator = list.iterator();

        //使用迭代器遍历list集合中的元素
        String next = iterator.next();
        System.out.println(next);
        //用hasNext方法,根据迭代器里面的游标位置,来判断集合中是否还有元素,有的话返回true
        while (iterator.hasNext()){
            System.err.println(iterator.next());
        }
        //上面遍历后,游标已经移动到最后一个元素的位置了,所以再用 next() 方法判断集合中还有没有下一个元素时,发现已经没有元素了,直接抛异常。
        //元素不存在抛出的异常--NoSuchElementException
        String n = iterator.next();
        System.out.println(n);
        
    }
}

遍历 Map 集合

简单演示下用 Iterator 迭代器遍历 Map 集合。

   如图,源码中,因为 Iterator 迭代器是由 Iterable 接口提供的,而 Collection 接口继承了 Iterable 接口,而 List 接口 和 Set 接口又继承了 Collection 接口,所以 List 和 Set 集合可以使用迭代器,但是 Map 就不行了,因为 Map 并不是继承 Iterable 接口。

   所以当我们要用迭代器去遍历 HashMap 集合时,需要先把 HashMap 集合通过 entrySet() 方法转换成映射 Set 集合。

在这里插入图片描述
在这里插入图片描述

如图:迭代器遍历 HashMap 代码实现

在这里插入图片描述

代码:
//Java集合演示类
public class JavaCollections {
    public static void main(String[] args) {

        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("小明","10");
        hashMap.put("小白","20");
        hashMap.put("小黑","30");
        hashMap.put("小黄","40");
        hashMap.put("小蓝","50");

        //{小明=10, 小白=20, 小蓝=50, 小黑=30, 小黄=40}
        System.out.println("hashMap ==> " + hashMap);

        //HashMap 并不能直接进行for循环,只能这样
        hashMap.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String s, String s2) {
                System.out.println(s + "--->" +s2);
            }
        });

        //entrySet() -- 将 HashMap 中所有的键值对映射成一个 set 集合
        Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();

        //打印结果:[小明=10, 小白=20, 小蓝=50, 小黑=30, 小黄=40]
        System.out.println("entrySet ===> "+entrySet);
        
        //将 HashMap 映射成set集合后,才能用 Iterator 迭代器进行遍历
        Iterator<Map.Entry<String, String>> iteratorMap = entrySet.iterator();

        //迭代器遍历
        while(iteratorMap.hasNext()){
            System.err.println(iteratorMap.next());
        }
    }
}

小总结:

   Iterator 对象称为迭代器,是设计模式中的一种,迭代器可以对集合进行遍历,它的特点是更加安全,因为它可以确保,在遍历当前集合元素期间,如果元素被其他线程更改到,会抛出 ConcurrentModificationException 异常。


1-6:线程安全和不安全的集合介绍

ArrayList、LinkedList、HashMap、TreeSet、TreeMap 都是线程不安全的。

如果要使用线程安全的集合,下面是一些替代关系:

   1、ConcurrentHashMap: 可以看作是线程安全的 HashMap;

   2、CopyOnWriteArrayList:可以看作是线程安全的 ArrayList ;
     在读多写少的场合性能⾮常好,远远好于 Vector .

   3、ConcurrentLinkedQueue: 可以看做⼀个线程安全的 LinkedList;

     ConcurrentLinkedQueue 是高效的并发队列,使用链表实现,是一个非阻塞队列。

   4、ConcurrentSkipListMap:跳表的实现,这是一个 Map,使用 跳表 的数据结构进行快速查找。


2、List 接口集合分析

2-1:ArrayList 和 Vector 的区别?

   1、ArrayList 是 List 的主要实现类,底层使用 Object[] 数组进行元素存储,适用于频繁的查找工作,线程不安全;

   2、Vector 是 List 的古老实现类,底层使用 Object[] 数组进行元素存储,线程是安全的。


2-2:ArrayList 和 LinkedList 的区别?

1、线程安全方面
   ArrayList 和 LinkedList 都是不同步的,也就是 线程都是不安全的
(同步是指多个线程之间协调执行的机制。
比如多个线程同时修改这个集合,因为不是同步的,可能就导致数据操作不是原子性的,会出现数据不一致的问题)

2、底层数据结构
   ArrayList 底层使用的是 Object 数组
   LinkedList 底层使用的是 双向链表 的数据结构;

3、是否支持快速随机访问:
   快速随机访问就是通过元素的序号快速获取元素的对象,也就是 get(int index) 方法。

   ArrayList 支持;LinkedList 不支持。

4、内存空间占用问题:
   ArrayList 主要是在 List 列表的结尾会预留一定的容量空间;
   LinkedList 主要是每个元素都需要消耗比 ArrayList 更多的空间
  (LinkedList 的每个元素的前后都需要占据4个字节的空间,然后本身元素也要占据 4个字节的空间)


2-3:双向链表和双向循环链表 --> 图解

在这里插入图片描述


3、Set 接口集合分析

3-1:comparable 和 Comparator 的区别

   comparable 接口有一个 compareTo 方法来对集合进行排序;
   Comparator 接口有一个 compare 方法来对集合进行排序。

在这里插入图片描述


3-1-1:Comparator 定制排序

   Comparator 的 compare 方法实现了逆序排序(从大到小)

在这里插入图片描述

代码:
//Java集合演示类
public class JavaCollections02 {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(-1);
        arrayList.add(0);
        arrayList.add(-3);
        arrayList.add(2);
        arrayList.add(5);
        arrayList.add(5);
        arrayList.add(30);
        arrayList.add(8);

        System.err.println("原始数组:" + arrayList);
        //Collections是一个工具类,提供一系列方法来操作集合 ,  .reverse() 方法可以反转集合中的元素的位置
//        Collections.reverse(arrayList);
//        System.err.println("Collections.reverse(arrayList) 反转集合中元素的位置 -->" + arrayList);

//        //.sort() 升序排序集合中的元素
//        Collections.sort(arrayList);
//        System.err.println("Collections.sort(arrayList) 升序排序集合中的元素 -->" + arrayList);

        //定制排序的用法:元素位置逆序排序
        Collections.sort(arrayList, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.err.println("o1 = "+o1 +" <==> o2 = "+ o2);

                //o2.compareTo(o1) 会返回正整数、零或负整数,分别表示 o2 大于、等于或小于 o1。
                int num = o2.compareTo(o1);
                System.err.println("o1和o2大小对比结果: -->  num = " + num +" 表示 : "+ ((num>0) ? "o2 > o1":((num==0 ? "o2 == o1":"o2 < o1"))));

                return num;
            }
        });
        System.err.println("定制排序后,元素位置反转:-->" + arrayList);
    }
}

3-2:无序性和不可重复性的含义

无序性:

   无序性不等于随机性,无序性是指存储的数据元素在底层数组中,并非按照数组索引的顺序添加的,而是根据数据的哈希值决定的。

不可重复性:

   不可重复性是指添加的元素按照 equals() 判断时,返回 false,需要同时重写 equals() 方法和 HashCode() 方法。


3-3:HashSet、LinkedHashSet 和 TreeSet 三者的区别

   HashSet:是 Set 接口的主要实现类,HashSet 的底层是 HashMap,线程是不安全的,可以存储 null 值。

   LinkedHashSet:是 HashSet 的子类,能够按照添加的顺序遍历。

   TreeSet:底层是使用红黑树,能够按照添加元素的顺序进行遍历,排序的方式有自然排序和定制排序(通过 Comparator 的 compare 方法来定制排序)。


4、Map 接口集合分析

4-1:HashMap 和 Hashtable 的区别

1、线程是否安全:
   HashMap 是线程不安全的;HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过 synchronized 关键字修饰过。
   如果要保证线程安全的话,就直接使用 ConcurrentHashMap

2、效率:
   因为线程安全问题,所以 HashMap 效率要比 HashTable 高一点。
   另外,HashTable 基本被淘汰了,尽量不适用它。

3、对 Null Key 和 Null Value 的支持:
   HashMap 可以存储 null 的 key 和 value,但是 null 作为 key 只能有一个,null 作为值可以有多个;
   HashTable 不允许有null key 和 null 值,否则会报错。

4、初始容量大小和每次扩充容量大小的不同:
   创建时如果不指定初始容量值,HashMap 默认的初始化大小为 16 ,之后每次扩充,容量变为原来的 2 倍。而 HashTable 默认的初始化大小为 11,之后每次扩充,容量变为原来的 2n+1。

   创建时如果设置了初始容量值,那么 HashMap 会将其扩充为 2 的幂次方大小,也就是说 HashMap 总是使用 2 的幂作为哈希表的大小。而 HashTable 会直接使用我们给定的初始容量值。

   比如我给 HashMap 的初始容量值时 8,HashMap 在内部会将这个值调整为比 8 大且最接近 8 的一个 2 的幂次方,那么扩充为 2 的幂次方大小就是:16,也就是扩充为原来容量的两倍。

   示例:8 是 2 ^ 3 ,那么比8大且最接近,就是 2 ^ 4 ,就是16
      如果我设置为 20 ,那么比 20 大又最接近的就是 2 ^ 5 = 32

问题:初始容量设置为 20 ,根据 2 的幂次方来扩充,选择的是 32 ,也不是原来的两倍呀。

解答:对于 HashMap 来说,在进行扩容时,并不是严格按照原来容量的两倍进行扩容的,当 HashMap 中的元素数量达到当前容量与负载因子的乘积时(默认负载因子为 0.75),HashMap 就会进行扩容操作。
   在设置初始容量为 20 的情况下,HashMap 首先会找到大于等于 20 的最小的 2 的幂,即 32,作为初始容量。
   当 HashMap 中元素数量达到 32 * 0.75 = 24 时,HashMap 将进行扩容操作,将容量扩大为 32 的两倍,即 64。

注意:初始容量的设置只会在一开始时,通过 2 的幂次方进行调整,调整为不小于指定初始容量的最接近的 2 的幂次方大小。
而在后续扩容过程中,HashMap 会将当前容量直接扩容为两倍,从而保持容量始终为 2 的幂次方。

5、底层数据结构:
   HashMap 的底层数据结构是:数组+链表+红黑树
   JDK1.8 之后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认是 8 )时,会将链表转化为红黑树,以减少搜索时间。

   注意:在将链表转成红黑树之前会进行判断,如果当前数组的长度小于 64 ,那么会先进行数组扩容,而不是直接转换成红黑树,只有数组长度达到或超过 64 时,才会转成红黑树。

   HashMap 数组扩容时,会将数组的容量扩充为原先的一倍,并重新分配现有的键值对到新的数组中。

   HashTable 的底层数据结构是:数组+链表,但是没有转成红黑树的机制

在这里插入图片描述


4-2:HashMap 和 HashSet 区别

   HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone() 、 writeObject() 、 readObject() 是 HashSet ⾃⼰不得不实现之外,其他⽅法都是直接调⽤ HashMap 中的⽅法。

在这里插入图片描述


4-3:HashMap 和 TreeMap 区别

   TreeMap 和 HashMap 都继承自 AbstractMap ,但是需要注意的是 TreeMap 它还实现了NavigableMap 接口和 SortedMap 接口。

   实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。
   实现 SortMap 接口让 TreeMap 有了对集合中的元素根据键排序的能力,默认是按 key 的升序排序,不过我们也可以指定排序的比较器(使用 Comparator 接口的 compare 方法)。

   示例代码如下:

如图可以看出,TreeMap 中的元素已经是按照 People 的 age 字段的升序来排列了。

相比于 HashMap 来说, TreeMap 主要多了对集合中的元素根据键(key)排序的能力以及对集合内元素
的搜索的能力。

在这里插入图片描述

上面是通过传⼊匿名内部类的方式实现的,可以将代码替换成 Lambda 表达式实现的⽅式:

在这里插入图片描述

在这里插入图片描述


TreeMap 的继承关系图例:

在这里插入图片描述

在这里插入图片描述


代码:
package cn.ljh.mytest;

import java.util.Comparator;
import java.util.TreeMap;
import java.util.function.BiConsumer;

/**
 * @author JH  2024-06
 */
//演示通过实现了 SortMap 接口的 TreeMap ,来自定义排序的比较器
public class People {
    //字段-年龄
    private Integer age;
    //构造器
    public People(Integer age) { this.age = age; }
    //get方法
    public Integer getAge() { return age; }

    public static void main(String[] args) {

        //创建 TreeMap 集合对象,使用匿名内部类实现自定义排序的比较器
        //Comparator 接口要求实现一个 compare 方法,用来比较两个 People 对象的 age
        TreeMap<People, String> treeMap = new TreeMap<>(new Comparator<People>() {
            @Override
            public int compare(People p1, People p2) {

                //计算 p1 对象和 p2 对象的 年龄差
                int ageNum = p1.getAge() - p2.getAge();
                /*
                 * return Integer.compare(ageNum, 0); 使用 Integer.compare 方法比较 ageNum 和 0。
                 *     如果 ageNum 小于 0,则返回负数(表示 p1 的年龄 < p2 的年龄)。
                 *     如果 ageNum 等于 0,则返回 0(表示 p1 和 p2 的年龄相同)。
                 *     如果 ageNum 大于 0,则返回正数(表示 p1 的年龄 > p2 的年龄)。
                 *
                 * 在 TreeMap 中插入和检索元素时,键(People 对象)将按照其年龄进行 --> 升序排列
                 */
                 //按 key 进行升序排序
                return Integer.compare(ageNum, 0);
            }
        });

        treeMap.put(new People(10), "小明 10 岁");
        treeMap.put(new People(50), "小红 50 岁");
        treeMap.put(new People(30), "小白 30 岁");
        treeMap.put(new People(80), "小黑 80 岁");

        //直接打印看顺序
        System.err.println(treeMap);

        //遍历方式1:
        treeMap.forEach(new BiConsumer<People, String>() {
            @Override
            public void accept(People people, String s) {
                System.err.println(people.age+" ===> " + s);
            }
        });
        //遍历方式2:
        //此时打印出来的数据,就是根据 age 升序排序出来的
        treeMap.entrySet().stream().forEach(peopleStringEntry -> System.out.println(peopleStringEntry.getValue()));

    }
}


4-4:HashSet 如何检查重复

   当我们通过 .add() 方法把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加⼊的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。
   但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加⼊操作成功。


hashCode() 与 equals() 的相关规定:

1、如果两个对象相等,则 hashcode ⼀定也是相同的;
2、两个对象相等,通过 equals 方法对比会返回 true,如:a.equals(b) ==> true
3、两个对象有相同的 hashcode 值,它们也不⼀定是相等的。
4、综上,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
5、hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)


== 与 equals 的区别

对于基本数据类型来说,== 比较的是两个值是否相等;

对于引用数据类型来说,== 比较的是两个对象的内存地址。

equals() :判断两个对象是否相等。
没重写前,功能等价于 == ,比较的是地址是否相等;
重写后,就是判断两个对象的内容(地址里的内容)是否相等。

在这里插入图片描述

String 的 equals 是重写过的,Object 的 equals 方法比较的是对象的内存地址。


4-5:ConcurrentHashMap 和 Hashtable 的区别

   ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。

1、底层数据结构:
ConcurrentHashMap:数组+链表/红⿊⼆叉树

Hashtable:数组+链表

2、实现线程安全的方式:
ConcurrentHashMap:
JDK1.7–> 采用分段锁 技术;
JDK1.8 之后,采用 Node 数组+链表+红黑树 的数据结构,并发控制 采用 synchronized 和 CAS 来实现

Hashtable :使用 synchronized 实现线程安全。


5、注意事项:

不要在 foreach 循环里进行元素的 remove / add 操作

如果要在循环里面删除和添加元素,要使用 Iterator 方式,如果是并发操作,需要对Iterator 对象加锁。

代码示例:

没使用 Iterator 的 remove 方法来在循环里面删除元素,则会报 ConcurrentModificationException 修改异常。

在这里插入图片描述


public class JavaCollections04 {
    public static void main(String[] args) {

        ArrayList<String> arrayList = new ArrayList<>();

        arrayList.add("1");
        arrayList.add("2");
        arrayList.add("3");
        arrayList.add("4");

        System.err.println("原始集合数据 ==> "+arrayList);

        //正确删除元素
        Iterator<String> iterator = arrayList.iterator();
        while (iterator.hasNext()){
            String item = iterator.next();
            if ("1".equals(item)){
                iterator.remove();
            }
        }
        System.err.println("iterator.remove() 删除 1 ==> "+arrayList);

        //错误删除元素
        for (String s : arrayList) {
            if ("2".equals(s)){
                arrayList.remove(s);
            }
        }
        System.err.println("arrayList.remove(s) 删除 2 ==> "+arrayList);
    }
}






本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1841358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python电灯开关 青少年编程电子学会python编程等级考试三级真题解析2021年12月

python电灯开关 2021年12月 python编程等级考试级编程题 一、题目要求 1、编程实现 n个灯排成一排&#xff0c;开始时都是关着的。现进行如下操作: 所有电灯的按钮按动一次;所有编号为2的倍数的电灯按钮按动一次;所有编号为3的倍数的电灯的按钮按动一次: …所有编号为n-1的…

LeetCode 算法:删除链表的倒数第 N 个结点 c++

原题链接&#x1f517;&#xff1a;删除链表的倒数第 N 个结点 难度&#xff1a;中等⭐️⭐️ 题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a…

详细分析Element Plus的el-pagination基本知识(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 需求&#xff1a;从无到有做一个分页并且附带分页的导入导出增删改查等功能 前提一定是要先有分页&#xff0c;作为全栈玩家&#xff0c;先在前端部署一个分页的列表 相关后续的功能&#xff0c;是Java&#xff0c;推荐阅读&#x…

rockchip linux sdk指定编译配置文件

SDK&#xff1a;rk3568_linux4.19_V1.4.0 硬件平台&#xff1a;RK3566 一、指定板级配置文件 板级配置文件在<SDK>/device/rockchip/rk3566_rk3568目录下。 1、方法1 ./build.sh后⾯加上板级配置⽂件&#xff0c;例如&#xff1a; ./build.sh /device/rockchip/rk3…

STM32微控制器库指南:函数特性、应用范围与实践

在嵌入式系统的设计和开发中&#xff0c;STM32系列微控制器因其卓越的处理能力和多样的外设选项而广受推崇。STM32库函数作为开发流程中不可或缺的工具&#xff0c;扮演着至关重要的角色。本文将详细阐述STM32库函数的主要特性、应用场景及其在实际开发中的应用实例。 什么是ST…

Golang | Leetcode Golang题解之第168题Excel表列名称

题目&#xff1a; 题解&#xff1a; func convertToTitle(columnNumber int) string {ans : []byte{}for columnNumber > 0 {columnNumber--ans append(ans, Abyte(columnNumber%26))columnNumber / 26}for i, n : 0, len(ans); i < n/2; i {ans[i], ans[n-1-i] ans[n…

JavaScript基础部分知识点总结(Part3)

函数的概念 1. 函数的概念 在JS 里面&#xff0c;可能会定义非常多的相同代码或者功能相似的代码&#xff0c;这些代码可能需要大量重复使用。虽然for循环语句也能实现一些简单的重复操作&#xff0c;但是比较具有局限性&#xff0c;此时我们就可以使用JS 中的函数。函数&…

RabbitMQ实践——交换器(Exchange)和绑定(Banding)

大纲 direct型交换器默认交换器命名交换器 fanout型交换器topic型交换器headers型交换器 RabbitMQ在概念上由三部分组成&#xff1a; 交换器&#xff08;Exchange&#xff09;&#xff1a;负责接收消息发布者发布消息的结构&#xff0c;同时它会根据“绑定关系”&#xff08;Ba…

PySide(PyQt)的特殊按钮(互锁、自锁、独占模式)

界面图&#xff1a; Qt Designer中创建窗口&#xff0c;放置一个QGroupBox&#xff0c;命名为btnStation&#xff0c;这就是自定义的按钮站&#xff0c;按钮站里放置6个按钮。自锁按钮相当于电器中的自锁功能的按钮&#xff0c;每按一次状态反转并保持不变。独占按钮也是自锁功…

使用asyncua模块的call_method方法调用OPC UA的Server端方法报错:asyncio.exceptions.TimeoutError

使用asyncua模块的call_method方法调用OPC UA的Server端方法报错&#xff1a;asyncio.exceptions.TimeoutError 报错信息如下&#xff1a; Traceback (most recent call last): asyncio.run(main()) File “D:\miniconda3\envs\py31013\lib\asyncio\runners.py”, line 44, in…

Python酷库之旅-比翼双飞情侣库(17)

目录 一、xlwt库的由来 1、背景和需求 2、项目启动 3、功能特点 4、版本兼容性 5、与其他库的关系 6、示例和应用 7、发展历史 二、xlwt库优缺点 1、优点 1-1、简单易用 1-2、功能丰富 1-3、兼容旧版Excel 1-4、社区支持 1-5、稳定性 2、缺点 2-1、不支持.xls…

什么是无限铸币攻击?它是如何运作的?

一、无限铸币攻击解释 无限铸币攻击是指攻击者操纵合约代码不断铸造超出授权供应限制的新代币。 这种黑客行为在去中心化金融 (DeFi) 协议中最为常见。这种攻击通过创建无限数量的代币来损害加密货币或代币的完整性和价值。 例如&#xff0c;一名黑客利用了 Paid 网络的智能…

第三十三章 添加和使用自定义标题元素

文章目录 第三十三章 添加和使用自定义标题元素SOAP 标头元素简介如何表示 SOAP 标头 第三十三章 添加和使用自定义标题元素 本主题介绍如何添加和使用自定义 SOAP 标头元素。 有关发生故障时添加标头元素的信息&#xff0c;请参阅 SOAP 故障处理。 WS-Addressing 标头元素在…

Java swing JTable 示例

代码&#xff0c; import java.awt.Container; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable;public class Mylmlk {public static void main(String[] agrs){JFrame framenew JFrame("学生成绩表");frame.setSize(500,2…

it职业生涯规划系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;职业介绍管理&#xff0c;答题管理&#xff0c;试题管理&#xff0c;基础数据管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;在线答题&#xff0…

QT基础 - QMainWindow主窗口

目录 零. 简介 一. 菜单栏 二. 工具栏 三. 状态栏 四. 可停靠区域 五. 总结 零. 简介 QMainWindow 是 Qt 中用于构建主窗口的类。 它通常包含以下几个主要部分&#xff1a; 菜单栏&#xff1a;用于提供各种操作选项。工具栏&#xff1a;放置常用的操作按钮。中心区域&…

DAMA学习笔记(二)-数据治理

1.引言 数据治理&#xff08;Data Governance&#xff0c;DG&#xff09;的定义是在管理数据资产过程中行使权力和管控&#xff0c;包括计划、监控和实施。在所有组织中&#xff0c;无论是否有正式的数据治理职能&#xff0c;都需要对数据进行决策。建立了正式的数据治理规程及…

【STM32-ST-Link】

STM32-ST-Link ■ ST-Link简介■ ST-Link驱动的安装。■ ST-Link编程软件(MDK)配置。■ ST-Link固件升级方法 ■ ST-Link简介 由于德产 J-LINK 价格非常昂贵&#xff0c; 而国产 J-LINK 因为版权问题将在万能的淘宝销声匿迹。 所以我们有必要给大家介绍 JTAG/SWD 调试工具中另…

排序算法及源代码

堆排序&#xff1a; 在学习堆之后我们知道了大堆和小堆&#xff0c;对于大堆而言第一个节点就是对大值&#xff0c;对于小堆而言&#xff0c;第一个值就是最小的值。如果我们把第一个值与最后一个值交换再对最后一个值前面的数据重新建堆&#xff0c;如此下去就可以实现建堆排…

【Java】已解决java.util.concurrent.TimeoutException异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.util.concurrent.TimeoutException异常 一、问题背景 java.util.concurrent.TimeoutException是Java并发编程中常见的一个异常&#xff0c;它通常发生在使用Future或Future…