1.集合体系补充(1)

1.接口式引用

        集合的构造,我们需要采用接口类型引用的的方式,这样做的好处就是方便根据业务或者设计上的变化,快速更换具体的实现。

        事实上,Java集合设计体系者也是支持我们这样做的,并且集合体系的设计也是如此的。

        创建一个ArrayList实例化变量,赋值给List引用。

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.forEach(System.out::println);

        但是当我们思考业务之后,感觉使用栈比列表更好:

        直接无缝衔接到Stack上,切换之后调用的API也不用转变,代码可维护性极高。

List<Integer> list = new Stack<>();
list.add(1);
list.add(2);
list.add(3);
list.forEach(System.out::println);

        之所以可以这样做的原因是:接口不会决定实现的具体效果,表现出来的效果最终由创建的实际对象来决定,又由于这样对象都实现了List接口,调用List接口的方法时会触发多态机制。

2.集合设计体系

2.1利于扩展的体系

        Java集合设计者不仅仅去实现了种类繁多的集合,还为开发者方便扩展集合体系做足了准备。

        从Collection入手:

        Java设计者设计Collection接口的原因不只是为了设计Java的集合体系结构,更是为用户拓展提供了一些标准的实用方法声明。

        Java类库使用者在使用Java集合标准实现自己的集合时,可以实现Collection接口,并实现其中的方法。

        但是如果每次定义自己的集合时,都需要实现这些实用方法,就太麻烦了,于是Java集合设计者声明了新的抽象方法AbstractCollection。

        AbstractCollection提供了实用的抽象方法的默认实现,除了基础方法(如size,iterator这种不同集合的实现差异性极大的以外),均提供了相应默认实现,意味着集合设计者在设计集合体系时,直接去继承AbstractCollection即可。

        虽然AbstractCollection提供了默认实现,但是如果设计的集合中开发者认为这些实用的方法有更好的实现,完全可以自定义重写。

        设计体系:

        但是现在这种方案已经逐渐在被淘汰了,在接口默认方法(defalult关键字)出现之前,使用接口设计抽象行为,抽象类实现默认行为是被认可的,但是现在更为主流的方案是接口中实现default默认方法。

2.2集合接口体系

        Java集合框架为不同类型的集合定义了大量接口。

        Java集合体系被划分为:1.Collection(单数据存储)2.Map(键值对映射存储)

        单数据存储的Collection又被分为了三大体系:1.List(列表)2.Set(集)3.Queue(队列)

        还有两个附加的体系,迭代器机制体系Iterator接口,快速随机访问机制RandomAccess接口。

2.3集合实现类体系

        具体集合体系是比较庞大的:

        1.Collection体系

                Collection体系主要是通过AbstractCollection默认实现了Collection常用的方法是,下面三大分支,List体系(AbstractList在AbstractCollection的基础上扩展出List常用的一些方法),Set体系(借助AbstractSet拓展),Queue体系(一部分借助AbstractQueue扩展,剩下一部分直接继承的AbstractCollection)

                List体系主要是有:1.ArrayList(借助数组结构实现的列表),2.LinkedList(借助链表结构实现的列表)

                Set体系主要是有:1.HashSet(无序存储,借助Hash值和Hash算法实现的,底层数据结构是HashMao)2.EnumSet(存储枚举类型数据)3.TreeSet(在HashSet的基础上增加了有序性的特点,底层数据结构是TreeMap)

                Queue体系主要是有:1.PriorityQueu((优先级队列,借助数组实现的堆结构,默认是小根堆,可以传入比较器或者自定义自然排序规则来改变排序规则)2.ArrayQueue(数组实现的对立)

        2.Map体系

                Map体系主要借助Map接口声明了Map中应该存在的被设计好的方法,规范Map结构是如何运行的,AbstractMap被设计为实现了Map接口中的通用方法,方便子类继承。

                Map体系比较庞大:所有的实现类都依托于一个抽象类AbstractMap展开,被划分为六大Map实现类。

                1.HashMap:底层数据结构是Hash表,借助的是Hash算法,数组,链表,平衡树等数据结构综合应用实现。具有无序性,可以通过key快速定为value的特性。

                2.LinkedHashMap:底层数据结构是Hash表和链表实现,主要是在Hash表的基础上使用链表维护了数据的顺序,方便记录元素加入的顺序。

                3.TreeMap:底层数据结构是红黑树,实现了键值对存储和有序存储的功能。

                4.EnumMap:存储Enum对象。

                5.WeakHashMap:弱引用的HashMap。

                6.IdentityHashMap:判断两个key是否一致使用的是==指针判断,而不是equals判断。

3.迭代器机制

        在Java集合体系中,有一个十分重要的机制就是迭代器机制。

        作者认为需要掌握的迭代器机制的内容:

3.1集合中的迭代器体系

3.1.1Iterator接口

        在集合Collection接口中声明了一个抽象方法iterator,返回Iterator

Iterator<E> iterator();

        Iterator接口中定义了几个方法:

public interface Iterator<E> {boolean hasNext();E next();default void remove() {throw new UnsupportedOperationException("remove");}default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}
}

        1.hasNext => 判断下一位置是否还有数据,有数据返回true,无数据返回false。

        2.next() => 迭代器向后移动,并将越过去的数据返回(要非常注意这个越过去,这是Java实现迭代器的重要机制)、

        3.remove() => 删除上一次返回的数据(即上一次越过去的数据)

        为什么要多次强调越过去这个机制呢?是Java设计Iterator迭代器时设计的机制问题。

3.1.2编程届的迭代器机制

        编程中有几种迭代器机制,一种是位置驱动迭代类(例如CPP的迭代器就是如此),另一种是位置变更驱动类(例如Java就是如此)

        以图解的形式介绍两种迭代器机制:

3.1.2.1位置驱动迭代模型

        位置驱动迭代更新模型机制如下:

        其中最关键的是迭代器的游标指针,迭代器的游标指针指向元素当前位置index,需要返回数据的时候直接调用iterator(index)方法,将元素返回出去,当不想返回当前数据的时候,就将游标指针向后移动到下一个元素,这就是:位置驱动迭代更新机制,迭代的元素与位置紧密相关。

3.1.2.2位置变化驱动迭代模型

        位置变化驱动迭代模型机制是Java设计者设计Iterator迭代器时规定的机制,迭代器的游标指针指向两个元素之间地带,定义出两个关键方法:next和hasNext方法。

        hasNext方法即判断是否有下一个元素,如果有下一个元素就返回true,如果没有就返回false。

        next方法即返回下一个元素,事先判断是否有下一个元素,如果没有就会抛出异常,如果有就返回元素,需要格外注意的是,返回的元素是越过去的元素,为什么被称为越过去的元素呢?游标指针的变化是从上一个元素和下一个元素之间的夹缝中,当调用next方法时,会越过去下一个元素,指向下一个元素和下下个元素之间夹缝中,所以给人一种越过去的感觉。

        迭代遍历一般就是循环判断hasNext,查看是否有数据,如果有数据就使用next遍历下一个元素。

        提供的remove方法是删除上一次返回的元素,这样设计也是符合逻辑的,先使用hasNext方法查看是否有下一个元素,然后使用next返回下一个元素,查看一下这个元素到底要不要删除,符合条件就使用remove删除元素。

        需要注意的是remove是不能连续调用的,调用remove必须得先next一次,不可以连续调用remove删除,否则会抛出IllepgalStateException异常,告诉我们这次抛出的异常时违法的。

3.1.3更强大的遍历器 => ListIterator接口

3.1.3.1ListIterator接口的定义

        ListIterator是更为强大的迭代器,他扩展了Iterator接口,提供了更加强大的功能。

        ListIterator的定义如下:

        继承了Iterator并在接口内部定义了一些方法

public interface ListIterator<E> extends Iterator<E> {boolean hasNext();E next();boolean hasPrevious();E previous();int nextIndex();int previousIndex();void remove();void set(E e);void add(E e);
}
3.1.3.2ListIterator接口的原理

        其中添加了一些新功能:add(为集合新增数据),set(为集合中上一次被迭代器越过的元素设置数据值),previous/hasPrevious(反向遍历集合),nextIndex(查看正向遍历下一个数据时的索引),previousIndex(查看反向遍历下一个数据时的索引)

3.1.3.3ListIterator接口的出现价值

        ListIterator接口被广泛用于各种有序集合中,其实这也体现出迭代器的设计理念。

        ListIterator接口相对于Iterator接口最重要的就是增加了添加元素机制。

        Iterator迭代器是通用的,所有Collection中都可以使用,但是ListIterator只能在实现了List接口的集合中使用:

                List接口中定义了listIterator方法,可以返回ListIterator类型的对象。

public interface List<E> extends Collection<E> {ListIterator<E> listIterator();
}

        Set接口中没有定义这个方法,只能获取Set集合的Iterator迭代器对象。

        Queue接口中也没有定义这个方法,Queue的实现类中,只能获取Queue集合的Iterator迭代器对象。

        为什么只有List接口中的迭代器可以添加元素呢?

                这个问题需要从Collection的三大分支的特性去理解,Collection三大分支:List,Set,Queue。

                1.List和Set相比,List拥有有序性的特点,Set存储元素是无序的,前面已经多次提到Java中依赖的是位置变化驱动迭代机制,List是有序集合,即位置是确定,有规律的,所以是可以让Java的迭代器添加元素的,但是Set是无序的,存储元素的顺序没有规律,没有顺序,迭代器无法借助位置变化驱动机制去添加数据。

                2.Queue根本不能在中间插入元素,没有这个机制。

3.1.3.4ListIterator的高级特性

        ListIterator支持从集合中的指定位置开始遍历。

        在AbstractList抽象集合中有如下定义:

        抽象集合中定义了一个类ListItr实现了ListIterator接口,且实现了List接口中的listIterator方法返回一个实例化的ListItr对象。

        可以发现ListItr中提供了有参构造器,接收一个int类型的索引,决定了迭代器从哪个位置开始迭代遍历。

public ListIterator<E> listIterator() {return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {rangeCheckForAdd(index);return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator<E> {ListItr(int index) {cursor = index;}
}    

        要点:ListIterator提供的高级特性可以决定迭代器游标指针的初始位置。

3.1.4迭代器遍历集合的顺序性

        迭代器遍历集合时是具有顺序性的,List有序集合遍历时就是按照有序集合的顺序遍历,Set无序集合遍历时是用一种感觉随机的感觉遍历的。

        一般需要依赖有序性时,建议使用List有序集合。

        如果不需要依赖于有序性时,建议使用Set集合,没有顺序,但是依然可以完成很多事情:最小值,最大值,总和等。

3.2迭代器的基本使用

3.2.1基本迭代

// 测试集合遍历
private static void traverse(ArrayList<Integer> list) {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}
}ArrayList<Integer> list = new ArrayList<Integer>() {{add(1);add(2);add(3);add(4);add(5);}
};
traverse(list);

3.2.2删除集合中指定位置的元素

// 测试清空集合
private static void clearList(ArrayList<Integer> list) {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {if (iterator.next() == 2) {iterator.remove();}}
}clearList(list);
System.out.println(list);

3.2.3Iterator循环消费

        循环消费机制是集合迭代器体系提供的一个默认方法。

// 测试迭代消费机制
public static void testConsumer() {List<Integer> list = new ArrayList<>();Collections.addAll(list, 1, 2, 3, 4, 5);Iterator<Integer> iterator = list.iterator();iterator.forEachRemaining((item) -> {System.out.println("蛋蛋" + item);});
}

        forEachRemaining的源码:

default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());
}

3.2.4有序集合添加元素

        有序集合添加元素需要使用到有序集合增强迭代器ListIterator。

List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5);
ListIterator<Integer> iterator = list.listIterator();
iterator.next();
iterator.next();
iterator.add(999);
System.out.println(list);

3.2.5指定位置初始化加强迭代器

        ListIterator是支持将游标初始化到指定位置的:

// 测试指定位置初始化ListIterator
public static void testIndexInit() {List<Integer> list = new ArrayList<>();Collections.addAll(list, 1, 2, 3, 4, 5);ListIterator<Integer> iterator = list.listIterator(3);System.out.println(iterator.next());
}

        执行结果:

3.3加强for循环(for-each)

3.3.1基本使用

        使用加强for循环可以实现集合的遍历:

List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5);
for (Integer item : list) {System.out.println(item);
}

        遍历结果:

3.3.2底层原理

        加强for循环的底层原理是迭代器机制,可以通过反编译字节码文件后表现出:

List<Integer> list = new ArrayList();
Collections.addAll(list, new Integer[]{1, 2, 3, 4, 5});
Iterator var2 = list.iterator();while(var2.hasNext()) {Integer item = (Integer)var2.next();System.out.println(item);
}

3.4快速失败机制

3.4.1什么是快速失败机制

        java.util包下的集合体系是不支持多线程操作的,仅仅支持单线程使用,在多线程下没有同步机制,很有可能因为集合体系结构的改变,导致原本应该正常执行的代码在运行中出现异常,Java类库设计者为了避免这种情况的发生,推出了Fail-Fast快速失败机制,尽可能的抛出错误,避免更大的问题出现。

        要点:提前抛出错误,避免多线程下,集合体系结构的改变,导致出现更大的问题。

        集合中的快速失败机制是为了在迭代过程中,如果有其他线程调用了remove/add等修改结构的方法,就会抛出comodification异常,而不是将问题拖到后面,再去使用比较复杂的检测算法判断是否出错。

        注意:快速失败机制(fail-fast)只能在迭代时起作用,其他时刻多线程并发执行多个操作是不会触发fail-fast的。

        可以看到在ArrayList的父类中定义了一个modCount变量。

protected transient int modCount = 0;

        modCount记录的是集合的结构修改次数,每当使用影响结构的操作的时(例如add/remove),会增加modCount,在迭代中,迭代器会记录住生成迭代器的modCount,在迭代过程会判断集合的modCount是否发生改变,如果发生改变就会抛出comodification异常。

3.4.2快速失败机制的执行流程

        从ArrayList的角度看待快速失败机制fail-fast。

        remove方法:

                remove方法会对集合的结构体系发生一次改变,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 workreturn oldValue;
}

        迭代器Iterator:

        1.创建迭代器时会初始化expectedModCount为modCount。

int expectedModCount = modCount;

        2.使用迭代器的next元素时,调用checkForComodification方法检查Iterator迭代器中的modCount和集合中的modCount是否一致

@SuppressWarnings("unchecked")
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];
}

        3.checkForComodification方法的定义:

                使用这个方法可以检查集合的modCount是否在迭代过程中发生变化,如果发生变化就会抛出ConcurrentModificationExcption异常。

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

3.4.3总结快速失败机制

        快速失败机制的原理其实就是提前发现迭代器迭代过程中集合发生变化引起的错误,提前抛出错误防止出现更大的错误。

        要点:快速失败机制是用来提前发现迭代器遍历过程中,集合结构改变会引发的问题的,当集合一改变就会立刻抛出错误,防止迭代器迭代过程中引发更大的错误。

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

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

相关文章

枚举及优化(一)

第1题 百钱买百鸡 查看测评数据信息 百钱买百鸡问题&#xff1a;公鸡五文钱一只&#xff0c;母鸡三文钱一只&#xff0c;小鸡三只一文钱&#xff0c;用 100 文钱买 100只鸡&#xff0c;公鸡、母鸡、小鸡各买多少只&#xff1f;本程序要求解的问题是&#xff1a;给定一个正整…

自注意力机制

当输入一系列向量&#xff0c;想要考虑其中一个向量与其他向量之间的关系&#xff0c;决定这个向量最后的输出 任意两个向量之间的关系计算 计算其他向量对a1的关联性 多头注意力机制 图像也可以看成一系列的向量&#xff0c;交给自注意力机制处理&#xff0c;CNN是特殊的自注意…

RabbitMQ的死信队列

1.死信的概念 死信简单理解就是因为种种原因&#xff0c;无法被消费的消息. 有死信自然就有死信队列&#xff0c;消息再一个队列中编程死信之后&#xff0c;它能被重新发送到另一个交换器中&#xff0c;这个交换器就是DLX&#xff0c;绑定DLX的队列&#xff0c;就被称为死信队…

十六 MyBatis使用PageHelper

十六、MyBatis使用PageHelper 16.1 limit分页 mysql的limit后面两个数字&#xff1a; 第一个数字&#xff1a;startIndex&#xff08;起始下标。下标从0开始。&#xff09;第二个数字&#xff1a;pageSize&#xff08;每页显示的记录条数&#xff09; 假设已知页码pageNum&…

SpringBoot框架在共享汽车管理中的应用

3系统分析 3.1可行性分析 通过对本共享汽车管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本共享汽车管理系统采用SSM框架&#xff0c;JAVA作为开发语…

数字化转型助手 快鲸SCRM系统为企业营销赋能

内容概要 在当今这个快速变化的商业环境中&#xff0c;数字化转型已经成为企业生存与发展的关键要素。无论是零售、制造还是服务行业&#xff0c;企业都深刻意识到传统工作模式的局限性&#xff0c;必须借助先进的技术来优化运营和提升客户体验。快鲸SCRM系统就是这样一款数字…

ZooKeeper在kafka集群中有何作用

Zookeeper 存储的 Kafka 信息 &#xff08;1&#xff09;启动 Zookeeper 客户端。 bin/zkCli.sh &#xff08;2&#xff09;通过 ls 命令可以查看 kafka 相关信息。 [zk: localhost:2181(CONNECTED) 2] ls /kafkazk中有一个节点 consumers 这个里面&#xff0c;老版本0.9版…

Linux操作系统:学习进程_对进程概念的深入了解

目录 前言 开篇 一、进程概念 二、进程的描述与管理 1、如何描述与管理 2、Linux中的PCB-task_struct 3、对进程组织的理解 三、进程的属性 1、系统创建进程 2、查看进程 3、进程的标识符 4、退出进程 1>ctrlc 2>kill命令杀死进程 5、用户进程的创建方式…

Embedding 技术在推荐系统中的应用

参考自《深度学习推荐系统》——王喆&#xff0c;用于学习和记录。 介绍 Embedding&#xff0c;中文直译为“嵌入”&#xff0c;常被翻译为“向量化”或者“向量映射”。它的主要作用是将稀疏向量转换成稠密向量&#xff0c;便于上层深度神经网络处理。事实上&#xff0c;Emb…

Kafka面试题

1、kafka消息发送的流程&#xff1f; 在消息发送时涉及到了两个线程&#xff0c;main 线程 和 sender 线程 &#xff0c;在main线程里面创建了一个双端队列&#xff08;RecordAccumulator&#xff09; ,当双端队列里面的信息满足 一定的条件后&#xff0c; sender线程会拉取双端…

RabbitMQ延迟队列(重要)

RabbitMQ延迟队列 1、延迟队列1.1、延迟队列使用场景1.2、延迟队列实现原理 2、使用rabbitmq-delayed-message-exchange 延迟插件2.1、下载2.2、安装2.2.1、解压2.2.2、启用插件2.2.3、查询安装情况 2.4、示例2.4.1、RabbitConfig配置类&#xff08;关键代码&#xff09;2.4.2、…

机器学习—神经网络如何高效实现

深度学习研究人员能够扩展神经网络的原因之一&#xff0c;在过去的十年里建立了非常大的神经网络&#xff0c;是因为神经网络可以向量化&#xff0c;它们可以使用矩阵乘法非常有效的实现&#xff0c;事实证明&#xff0c;并行计算硬件&#xff0c;包括gpus&#xff0c;但也有一…

【数据集】【YOLO】【目标检测】水面船只识别数据集 9798 张,YOLO船只识别算法实战训练教程!

一、数据集介绍 【数据集】水面船只识别数据集 9798 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。 数据集中包含1种分类&#xff1a;{0: ship}&#xff0c;代表水面船只。 数据集来自国内外图片网站和视频截图&#xff1b; 可用于无人机船只检测、监控灯塔船…

斜坡函数功能块(支持正常停机和紧急停机切换)

1、CODESYS斜坡函数功能块 CODESYS斜坡函数功能块(ST源代码)_用plc难能写一个斜坡加减速度吗-CSDN博客文章浏览阅读1k次。本文介绍了如何在CODESYS平台上创建斜坡函数功能块(FC),用于PID闭环控制中的给定值平滑处理。通过ST源代码实现,详细步骤包括仿真测试、变量修改、FC…

渗透测试--web基础之windows(二):常用命令详解及病毒编写

声明&#xff1a;学习素材来自b站up【泷羽Sec】&#xff0c;侵删&#xff0c;若阅读过程中有相关方面的不足&#xff0c;还请指正&#xff0c;本文只做相关技术分享,切莫从事违法等相关行为&#xff0c;本人一律不承担一切后果 目录 一、常见端口对应的服务 二、 常见的cmd命…

【含文档】基于ssm+jsp的客户管理系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: apache tomcat 主要技术: Java,Spring,SpringMvc,mybatis,mysql,vue 2.视频演示地址 3.功能 管理员登录进入…

腾讯混元宣布大语言模型和3D模型正式开源

腾讯混元大模型正在加快开源步伐。 11月5日&#xff0c;腾讯混元宣布最新的MoE模型“混元Large“以及混元3D生成大模型“ Hunyuan3D-1.0”正式开源&#xff0c;支持企业及开发者精调、部署等不同场景的使用需求&#xff0c;可在HuggingFace、Github等技术社区直接下载&#xff…

《常用深度学习神经网络及其原理与应用场景》

一、总体介绍 一、引言 随着科技的不断发展&#xff0c;深度学习已经成为人工智能领域中最具影响力的技术之一。深度学习神经网络通过模拟人类大脑的神经元结构和工作方式&#xff0c;能够自动学习数据中的特征和模式&#xff0c;从而实现各种复杂的任务&#xff0c;如图像识…

操作系统三级调度相关习题

填空题 微机是以(总线)为组带构成的计算机系统在批处理兼分时系统中&#xff0c;往往把由分时系统控制的作业称为(前台)作业&#xff0c;把由批处理系统处理的作业称为(后台)作业在分时系统中&#xff0c;若时间片长度一定&#xff0c;则(用户数越多)&#xff0c;系统响应时间…

STL 迭代器iteratior 详解

C的标准模板库&#xff08;STL&#xff09;中&#xff0c;迭代器是一种重要的工具&#xff0c;用于访问容器中的元素。 迭代器是一个变量&#xff0c;相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素&#xff0c;通过迭代器就可以读写它指向的元素。从…