java基础之集合概览

有时候需要存储一组数据,之前使用数组,但是数组具有固定的容量,但是在写程序时并不知道需要多少对象,在java.util包下提供了一套完整的集合类,包含List、Set、Queue、Map。java集合类都可以自动地调整自己的大小。

在创建集合时,经常使用泛型,可以在编译期防止将错误的类型放入到集合中。

集合概念

集合分为两个基本接口

  • 集合(Collection):一个独立元素的序列,List必须已插入顺序保存元素,Set不能包含重复元素,Queue按照排队规则来确定对象产生的顺序(一般是插入顺序)

  • 映射(Map):一组成对的"键值对"对象,允许使用键来查找值。map允许我们使用一个对象来查找另一个对象

    Arrays.asList()的输出是一个List,但是底层实现是数组,没法调整大小。

    List<String> list = Arrays.asList("123","234");
    list.add("345");//java.lang.UnsupportedOperationException

List

存储有序,可以重复的元素,相当于动态数组 集合中元素所在类要重写equals方法

  • ArrayList
  • LinkedList
  • Vector

两种类型的list

  • ArrayList:擅长随机访问元素,但在List中间插入和删除元素时速度较慢

  • LinkedList:擅长在List中间进行插入和删除操作,提供了优化的顺序访问,对于随机访问相对较慢

List特性

  • 允许插入重复元素
  • 允许插入多个null元素
  • List提供了ListIterator迭代器,可以提供双向访问

ArrayList和Vector的异同点

相同点

  • 两者都是基于索引的,内部使用数组

  • 两者维护插入顺序,可以根据插入顺序来获取元素

  • ArrayList和Vector的迭代器实现都是fail-fast的

  • ArrayList和Vector两者都允许null值,也可以使用索引值对元素进行随机访问

不同点

  • Vector是同步的,ArrayList不是,但是已过时,使用CopyOnWriteArrayList
  • ArrayList比Vector快

LinkedList链表

LinkedList添加了一些方法,使其可以被用作栈,队列和双向队列,方法差异

  • getFirst()和element()是相同的,都是返回列表的头部,而并不删除它,如果list为空,则抛出NoSuchElementException异常。peek()方法在列表为空时返回null

  • removeFirst()和remove()方法相同,删除并返回列表头部元素,在列表为空时返回NoSuchElementException异常,poll()在列表为空时返回null

  • addFirst()在列表头部插入一个元素

  • offer()和add()和addLast()相同,在列表尾部添加一个元素

  • removeLast()删除并返回列表的最后一个元素

ArrayList和LinkedList的区别

  • ArrayList是由数组支持的基于索引的数据结构,支持对元素的随机访问,复杂度为O(1),但是LinkedList是基于链表的,存储一系列的节点数据,每个节点都与前一个节点和下一个节点相连。虽然存在使用索引获取元素的方法,但是内部实现是从起始点开始遍历的,时间复杂度是O(n)
  • 与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快
  • LinkedList比ArrayList消耗更多内存,因为需要存储前后节点的引用

迭代器Iterators

Iterator

Iterator接口提供了遍历任何Collection的接口,取代了java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除数据

​ iterator只能单向移动

  • 使用iterator()方法使集合返回一个Iterator。Iterator将准备好返回序列中的第一个元素。

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

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

  • 使用remove()方法将迭代器最近返回的那个元素删除。

Enumeration和iterator的区别

  • Enumeration的速度是Iterator的两倍,使用内存也少,但是iterator更加安全,使得一个集合在遍历时,会阻止其他线程去修改集合,Iterator允许移除元素
  • Iterator支持fail-fast机制,而Enumeration不支持,Iterator遍历时,当其他线程修改集合内容时,迭代器会立马感知到,引起快速失败,抛出ConcurrentModificationException异常
  • Enumeration本身不支持同步,只是在Vector和hashtable实现Enumeration时,添加了同步
ListIterator
  • ListIterator是Iterator的子类型,只能由各种List类生成,
  • Iterator只能向前移动,ListIterator可以双向移动,可以生成迭代器在列表中指向位置的后一个和前一个元素的索引。

堆栈stack

堆栈是后进先出(LIFO),最后压入(push)栈的元素,第一个被弹出(pop)栈。

java1.0中有一个stack类,但是设计的不好,Java6添加了ArrayDeque,其中包含了直接实现堆栈功能的方法

  • push()添加元素到栈底
  • peek()和pop()返回对象,peek()返回栈顶元素,但不从栈顶删除,而pop()删除并返回栈顶元素

Set

Set不保存重复的元素。查找是Set最重要的操作,选择HashSet实现,针对快速查找进行了优化。

存储无序,不可重复 添加Set集合中的元素所在类要重写equals和hashCode方法

无序性:指的是元素在底层存储的位置是无序的

  • HashSet没有顺序,使用散列函数,HashSet维护顺序与TreeSet或LinkedHashSet不同,因为它们实现具有不同的元素存储方式

  • LinkedHashSet 也使用了散列,使用了链表来维护元素的插入顺序,结果将按元素的插入顺序显示。元素必须定义hashCode()和equals()方法,遍历元素时,会按照添加的进去的顺序

  • TreeSet将元素存储在红黑树数据结构,可以从Set中获取有序序列,其中元素必须实现Comparable接口

    要求添加进TreeSet的必须是同一个类的 两种排序方式 1)自然排序:添加的类要实现Comparable接口,重写compareTo方法 2)定制排序: 使用TreeSet(Comparator<? super E> comparator) 构造器 重写compare(T o1, T o2);方法

Map

键值 key不可重复,一个key-value组成一个entry

map的分类

HashMap专为快速访问而设计,TreeMap保持键始终处于排序状态,没有HashMap快。LinkedHashMap按插入顺序保存其元素,但使用散列提供快速访问的能力。

  • HashMap 基于哈希表的实现。为插入和定位键值对提供了常数时间性能。可以通过构造方法调整性能,这些构造方法允许设置哈希表的容量和装填因子。可以添加key为null,value为null
  • LinkedHashMap 与HashMap类似,但是当遍历时,可以按照插入顺序或最近最少使用(LRU)顺序获取键值对。只比HashMap略慢,一个例外是在迭代时,由于其使用链表维护内部顺序,所以会更快些,按照添加进Map的顺序遍历
  • TreeMap 基于红黑树实现,当查看键或键值对时,按排序顺序(由Comparable或Comparator确定)。TreeMap的侧重点在于按排序顺序获得结果。TreeMap是唯一使用subMap()方法的Map,返回红黑树的一部分,按照key所在类的指定属性进行排序,要求key是同一个类的对象(同TreeSet)
  • WeakHashMap 一个具有弱键的Map,为了解决某些类型的问题,它允许释放Map所引用的对象。如果Map外没有对特定键的引用,则可以对该键进行垃圾回收
  • ConcurrentHashMap 不使用同步锁定的线程安全Map
  • IdentityHashMap 使用==来比较键,仅用于解决特殊问题
  • HashTable 不可添加key为null,value为null的 子类Properties 处理属性文件

HashMap工作情况

HashMap在Map.Entry静态内部类实现存储键值对,HashMap使用哈希算法,在put和get方法中,使用hashCode和equals方法,使用put方法时,使用key的hashcode和哈希算法来找出存储键值对的索引,Entry存储在LinkedList中,如果存在entry,使用equals检查传递的key是否存在,如果存在,会覆盖掉value,如果不存在,会创建一个新的entry然后保存。get的时候也是先通过hashcode找到数组中的索引,然后使用equals找到正确的Entry,在进行取值

HashMap默认初始容量是32,负载因子是0.75,阈值是容量乘以负载因子,当map的大小比阈值大时,HashMap会对map的内容进行重新哈希。

HashMap和HashTable的区别

  • HashMap允许key和value为null,HashTable不允许
  • HashTable是同步的,HashMap不是
  • HashMap可以转为LinkedHashMap,使得遍历有序,HashTable的顺序无法预知
  • HashMap提供对key的set进行遍历,所以是fail-fast的,HashTable提供对key的Enumeration进行遍历,不支持fail-fast
  • HashTable应该被CocurrentHashMap替代

队列

队列操作

队列是一个先进先出(FIFO)集合,LinkedList实现了Queue接口,并且提供了一些方法支持队列行为

  • offer()在队列尾部插入一个元素

  • peek()和element()返回队列头而不删除它,如果队列为空,element()抛出NoSuchElementException,而peek()返回null

  • poll()和remove()都删除并返回队头元素,如果队列为空,poll()返回null,remove()抛出NoSuchElementException

PriorityQueue优先级队列

优先级队列声明下一个弹出的元素是最需要的元素。

BlockingQueue队列

是concurrent包下的类,在进行检索或移除一个元素的时候,会等待队列变成非空;当添加一个元素的时候,会等待队列中的可用空间。主要用于实现生产者-消费者模式

Collections工具类

unmodifiableCollection方法

Collections.unmodifiableCollection(list);Collections.unmodifiableList(list);使用该方法会创建一个只读集合,所有改变集合的操作都会抛出UnsupportedOperationException

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
        return new UnmodifiableCollection<>(c);
}

synchronizedCollection方法

Collections.synchronizedCollection(list)方法可以创建一个线程安全的集合

public static <T> Collection<T> synchronizedCollection(Collection<T> c) {
    return new SynchronizedCollection<>(c);
}

问题

1、遍历时移除List中的元素

使用forEach和Iterator

在使用forEach遍历时,实际上是使用的Iterator,使用的核心方法是hasNext()和next(),但是使用的是list.remove,来看个例子

//源码
public class TestList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("J");
        list.add("A");
        list.add("V");
        list.add("A");
        for (String s: list) {
            list.remove(s);
        }
    }
}

//编译之后
public class TestList {
    public TestList() {
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("J");
        list.add("A");
        list.add("V");
        list.add("A");
        Iterator var2 = list.iterator();
        while(var2.hasNext()) {
            String s = (String)var2.next();
            list.remove(s);
        }
    }
}  

之前说过,Iterator在遍历时,不允许其他线程对该集合进行操作,看一下ArrayList的iterator是怎么实现的

public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

在每次获取下一个元素时,都会比较modCount 和 expectedModCount

然后在调用的list的remove方法会导致modCount增加(modCount表示被修改次数)

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null// clear to let GC do its work

        return oldValue;
    }

此时iterator的next方法中两个变量就不一致了,就会抛出ConcurrentModificationException异常

再看一下如果使用iterator的remove方法

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

iterator在remove之后会将modCount的值赋给expectedModCount,就不会出现两个变量不等的情况了

不使用forEach遍历

使用普通for循环,有两种方式,第一种是使用正序遍历,但是进行remove操作之后要把遍历的索引进行修正减一,否则在移除下一个的时候就会出错,第二种就是使用倒序遍历

// 正序遍历
for (int i = 0; i < list.size(); i++) {
    String s = list.remove(i);
    i = i - 1;
    System.out.println(s);
}

//倒序遍历
for (int i = list.size() - 1; i >= 0; i--) {
    String s = list.remove(i);
    System.out.println(s);
}

2、fail-fast和fail-safe

java.util包中集合类被设计为fail-fast的,而java.util.concurrent中集合为fail-safe的。fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException,Iterator的安全失败是基于对底层集合做拷贝,不受源集合上修改的影响

fail-fast

fail-fast迭代器抛出ConcurrentModificationException,通过modCount来进行实现,在进行迭代时,每次对于元素的修改都会修改该值,一旦该值被修改了,就会抛出异常

// 当Itr被实例化的时候,记录一下迭代器被实例化时ArrayList的修改次数(在用ArrayList进行add/remove操作时modCount每次都加一)
int expectedModCount = modCount;

// 检查是否被修改了
    final void checkForComodification() {
          // 当修改次数与Itr被实例化时的修改次数不一致时,说明在进行迭代操作的时候其他线程进行了ArrrayList的add/remove操作,此时抛出ConcurrentModificationException,即为fast-fail快速失败机制
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

3、Arrays.asList

这个方法返回的是一个ArrayList,不过这个ArrayList是Arrays类的内部类,在调用add方法的时候会直接报错

UnsupportedOperationException这是运行时异常

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

https://zhhll.icu/2020/java基础/集合/1.java基础之集合/

本文由 mdnice 多平台发布

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

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

相关文章

〖Python网络爬虫实战㊴〗- 极验滑块介绍(一)

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000+ python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,订阅本专栏前必读关于专栏〖Python网络爬虫实战〗转为付费专栏的订阅说明 作者:爱吃饼干的小白鼠。Python领域优质创作者,2022年…

企业网络中的身份安全

随着近年来数字化转型的快速发展&#xff0c;企业使用的数字身份数量急剧增长。身份不再仅仅局限于用户。它们现在扩展到设备、应用程序、机器人、第三方供应商和组织中员工以外的其他实体。即使在用户之间&#xff0c;也存在不同类型的身份&#xff0c;例如属于IT管理员、远程…

python读取PDF文件中的指定页码的范围并存储到指定的文件名

读取PDF文件中的指定页码的范围并存储到指定的文件名 # -*- coding: utf-8 -*- """ Created on Mon Nov 27 21:36:12 2023author: cnliu pip install pypdf2 #安装pypdf2 --3.o版 """ from PyPDF2 import PdfWriter, PdfReader import os#pat…

IDEA编译器技巧-提示词忽略大小写

IDEA编译器技巧-提示词忽略大小写 写代码时,每次创建对象都要按住 Shift 字母 做大写开头, 废手, 下面通过编译器配置解放Shift 键 setting -> Editor -> General -> Code Completion -> Match case 把这个√去掉, 创建对象就不需要再按住 Shift 键 示例: 1.…

LeetCode Hot100 124.二叉树中的最大路径和

题目&#xff1a; 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点…

【密码学】【多方安全计算】不经意传输(Oblivious Transfer,OT)

文章目录 不经意传输&#xff08;oblivious transfer&#xff09;定义不经意传输的实例&#xff08;1 out 2&#xff0c;二选一不经意传输&#xff09;基于RSA的1 out 2 不经意传输疑问 不经意传输&#xff08;oblivious transfer&#xff09;定义 不经意传输&#xff08;obli…

从零开始学Go web——第一天

文章目录 从零开始学Go web——第一天一、Go与web应用简介1.1 Go的可扩展性1.2 Go的模块化1.3 Go的可维护1.4 Go的高性能 二、web应用2.1 工作原理2.2 各个组成部分2.2.1 处理器2.2.2 模板引擎 三、HTTP简介四、HTTP请求4.1 请求的文本数据4.2 请求方法4.2.1 请求方法类型4.2.2…

达索系统3DEXPERIENCE WORKS 2024电磁仿真功能

在设计工作中&#xff0c;将复杂的模型进行网格分割是必不可少的一步&#xff0c;这样可以化繁而简&#xff0c;也可以让后续的工作更容易开展。 电磁仿真可帮助您在复杂、嘈杂的电磁环境中提高效率&#xff0c;在确保兼容性的同时&#xff0c;保障出众性能。 一系列专用求解器…

链表的有序构建和查找/构建链表【数据结构】

链表的有序构建和查找 题目描述 单链表结点的存储结构包含两部分&#xff1a;数据、下一结点指针&#xff08;默认为空&#xff09;。 单链表包含头结点&#xff0c;存储实际数据的结点位置从1开始。 现输入一批无序的整数队列&#xff0c;编写程序完成以下要求 1&#xff09;…

windows中打开psql命令行

一、第一种方式 1.点击下方的psql&#xff0c;打开命令行窗口 2.中括号中的是默认值&#xff0c;直接回车就行 3.成功 二、第二种方式 双击安装目录中的执行文件 “D:\soft\postgresql\catalogue\scripts\runpsql.bat” 三、第三种方式 1.加到环境变量 把“D:\soft\postg…

婴儿专用洗衣机哪个牌子比较好?好用迷你洗衣机品牌推荐

当婴儿的到来&#xff0c;确实会给家庭带来许多变化&#xff0c;就好比如对于宝宝相关衣物的清洗需求。对于新生儿及婴幼儿的衣服&#xff0c;一般都要给予特殊的照顾与清洗&#xff0c;以保证不含细菌及过敏原。尤其是刚刚出生的婴儿&#xff0c;这时候宝宝们的皮肤很是幼嫩。…

百度网盘PC端程序二维码刷新不出来

问题 百度网盘PC端程序二维码刷新不出来。 原因 下载的百度网盘PC端程序版本有问题。 解决办法 删除百度网盘PC端程序&#xff0c;从官网下载&#xff0c;选择“从microsoft获取”&#xff0c;安装后解决。

快速开发表单好用吗?优势在哪?

如果应用快速开发表单&#xff0c;对提升企业的办公效率帮助巨大。在快节奏的现代社会生活中&#xff0c;职场办公也需要采用更专业的办公软件实现高效率提升。低代码技术平台就是如今常用于职场办公中的优质平台&#xff0c;其可视化操作、简单灵活、组件丰富等优势特点&#…

【精选】SpringDI依赖注入及注解实现SpringIoC

SpringDI 什么是依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;&#xff0c;它是Spring控制反转思想的具体实现。 控制反转将对象的创建交给了Spring&#xff0c;但是对象中可能会依赖其他对象。比如service类中要有dao类的属性&#xff0…

【Leetcode Sheet】Weekly Practice 17

Leetcode Test 2216 美化数组的最少删除数(11.21) 给你一个下标从 0 开始的整数数组 nums &#xff0c;如果满足下述条件&#xff0c;则认为数组 nums 是一个 美丽数组 &#xff1a; nums.length 为偶数对所有满足 i % 2 0 的下标 i &#xff0c;nums[i] ! nums[i 1] 均成…

VBA高级应用30例:Ribbon(功能区)的介绍

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

Nginx性能调优策略

Nginx是一个高性能的Web服务器和反向代理服务器&#xff0c;常用于处理高并发的请求。以下是一些常见的Nginx性能调优策略&#xff1a; 一、调整worker_processes和worker_connections 在Nginx配置文件中&#xff0c;可以通过worker_processes和worker_connections参数来调整w…

JAVA将PDF转图片

前言 当今时代&#xff0c;PDF 文件已经成为了常用的文档格式。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将 PDF 文件转换为图片格式&#xff0c;以便更方便地分享和使用。这时&#xff0c;我们可以使用 Java 编程语言来实现这个功能。Java 提供了许多库和工具&a…

Android 通过demo调试节点权限问题

Android 通过demo调试节点权限问题 近来收到客户反馈提到在应用层无法控制节点&#xff0c;于是写了一个简单的demo来验证节点的IO权限&#xff0c;具体调试步骤就是写一个按钮点击事件&#xff0c;当点击按钮时将需要验证的节点写为1&#xff08;节点默认为1则写为0&#xff…

城市安全守护者:分析无人机在交通领域的应用

随着科技的进步&#xff0c;无人机在交通领域的应用不断增加&#xff0c;为智慧交通管理提供了新便利。无人机凭借其灵活性&#xff0c;在违章取证、交通事故侦查、交通疏导等方面展现出巨大的应用潜力。无人机在交通领域的应用有哪些&#xff1f;跟着我们一探究竟。 1、违章取…