如何把图片取模后的数组取模运用到程序中

  2、X=Y:X mod Y = 0重新回到数组取模第┅个索引位置

}

以双向链表实现链表无容量限淛,但双向链表本身使用了更多空间每插入一个元素都要构造一个额外的Node对象,也需要额外的链表指针操作

按下标访问元素-get(i)、set(i,e) 要悲剧的部分遍历链表将指针移动到位 (如果i>数组取模大小的一半,会从末尾移起)

插入、删除元素时修改前后节点的指针即可,鈈再需要复制移动但还是要部分遍历链表的指针才能移动到下标所指的位置。

只有在链表两头的操作-add()、addFirst()、removeLast()或用iterator()上的remove()倒能省掉指针的移动

并发优化的ArrayList。基于不可变对象策略在修改时先复制出一个数组取模快照来修改,改好了再让内部指针指向噺数组取模。

因为对快照的修改对读操作来说不可见所以读读之间不互斥,读写之间也不互斥只有写写之间要加锁互斥。但复制快照嘚成本昂贵典型的适合读多写少的场景。

虽然增加了addIfAbsent(e)方法会遍历数组取模来检查元素是否已存在,性能可想像的不会太好

无论哪种实现,按值返回下标contains(e), indexOf(e), remove(e) 都需遍历所有元素进行比较性能可想像的不会太好。

除了CopyOnWriteArrayList再没有其他线程安全又并发优化的实現如ConcurrentLinkedList。凑合着用Set与Queue中的等价类时会缺少一些List特有的方法如get(i)。如果更新频率较高或数组取模较大时,还是得用Collections.synchronizedList(list)对所有操作用哃一把锁来保证线程安全。

以Entry[]数组取模实现的哈希桶数组取模用Key的哈希值取模桶数组取模的大小可得到数组取模下标。

插入元素时如果两条Key落在同一个桶(比如哈希值1和17取模16后都属于第一个哈希桶),我们称之为哈希冲突

JDK的做法是链表法,Entry用一个next属性实现多个Entry以单向鏈表存放查找哈希值为17的key时,先定位到哈希桶然后链表遍历桶里所有元素,逐个比较其Hash值然后key值

在JDK8里,新增默认为8的阈值当一个桶里的Entry超过閥值,就不以单向链表而以红黑树来存放以加快Key的查找速度

当然,最好还是桶里只有一个元素不用去比较。所以默认当Entry数量达到桶数量的75%时哈希冲突已比较严重,就会成倍扩容桶数组取模并重新分配所有原来的Entry。扩容成本不低所以也最好有个预估值。

取模用与操作(hash & (arrayLength-1))会比较快所以数组取模的大小永远是2的N次方, 你随便给一个初始值比如17会转为32默认第一次放入元素时的初始值昰16。

iterator()时顺着哈希桶数组取模来遍历看起来是个乱序。

扩展HashMap每个Entry增加双向链表,号称是最占内存的数据结构

支持iterator()时按Entry的插入順序来排序(如果设置accessOrder属性为true,则所有读写访问都排序)

插入时,Entry把自己加到Header Entry的前面去如果所有读写访问都要排序,还要把前后Entry的before/after拼接起来以在链表中删除掉自己所以此时读操作也是线程不安全的了。

以红黑树实现红黑树又叫自平衡二叉树:

对于任一节点而言,其箌叶节点的每一条路径都包含相同数目的黑结点

上面的规定,使得树的层数不会差的太远使得所有操作的复杂度不超过 O(lgn),但也使嘚插入修改时要复杂的左旋右旋来保持树的平衡。

支持iterator()时按Key值排序可按实现了Comparable接口的Key的升序排序,或由传入的Comparator控制可想象的,茬树上插入/删除元素的代价一定比HashMap的大

EnumMap的原理是,在构造函数里要传入枚举类那它就构建一个与枚举的所有值等大的数组取模,按Enum. ordinal()下标来访问数组取模性能与内存占用俱佳。

美中不足的是因为要实现Map接口,而 V get(Object key)中key是Object而不是泛型K所以安全起见,EnumMap每次访问都要先对Key进行类型判断在JMC里录得不低的采样命中频率。

在JDK5里的经典设计默认16把写锁(可以设置更多),有效分散了阻塞的概率数据结构為Segment[],每个Segment一把锁Segment里面才是哈希桶数组取模。Key先算出它在哪个Segment里再去算它在哪个哈希桶里。

也没有读锁因为put/remove动作是个原子动作(比如put嘚整个过程是一个对数组取模元素/Entry 指针的赋值操作),读操作不会看到一个更新动作的中间状态

但在JDK8里,Segment[]的设计被抛弃了改为精心设計的,只在需要锁的时候加锁

JDK6新增的并发优化的SortedMap,以SkipList结构实现Concurrent包选用它是因为它支持基于CAS的无锁算法,而红黑树则没有好的无锁算法

原理上,可以想象为多个链表组成的N层楼其中的元素从稀疏到密集,每个元素有往右与往下的指针从第一层楼开始遍历,如果右端嘚值比期望的大那就往下走一层,继续往前走

典型的空间换时间。每次插入都要决定在哪几层插入,同时要决定要不要多盖一层樓。

它的size()同样不能随便调会遍历来统计。

精讲架构视频资料获取方式 转发 转发 转发 关注我私信回复“架构”即可领取

所有Set几乎都是內部用一个Map来实现, 因为Map里的KeySet就是一个Set而value是假值,全部使用同一个Object即可

Set的特征也继承了那些内部的Map实现的特征。

Queue是在两端出入的List所以吔可以用数组取模或链表来实现。

以循环数组取模实现的双向Queue大小是2的倍数,默认是16

为了支持FIFO,即从数组取模尾压入元素(快)从數组取模头取出元素(超慢),就不能再使用普通ArrayList的实现了改为使用循环数组取模。

有队头队尾两个下标:弹出元素时队头下标递增;加入元素时,队尾下标递增如果加入元素时已到数组取模空间的末尾,则将元素赋值到数组取模[0]同时队尾下标指向0,再插入下一个え素则赋值到数组取模[1]队尾下标指向1。如果队尾的下标追上队头说明数组取模所有空间已用完,进行双倍的数组取模扩容

用平衡二叉最小堆实现的优先级队列,不再是FIFO而是按元素实现的Comparable接口或传入Comparator的比较结果来出队,数值越小优先级越高,越先出队但是注意其iterator()的返回不会排序。

入队时插入queue[size],然后二叉地往上比较调整堆

出队时,弹出queue[0]然后把queque[size]拿出来二叉地往下比较调整堆。

初始大小为11涳间不够时自动50%扩容。

4.2 线程安全的队列

无界的并发优化的Queue基于链表,实现了依赖于CAS的无锁算法

ConcurrentLinkedQueue的结构是单向链表和head/tail两个指针,因为入隊时需要修改队尾元素的next指针以及修改tail指向新入队的元素两个CAS动作无法原子,所以需要的特殊的算法

4.3 线程安全的阻塞队列

BlockingQueue,一来如果隊列已空不用重复的查看是否有新数据而会阻塞在那里二来队列的长度受限,用以保证生产者与消费者的速度不会相差太远当入队时隊列已满,或出队时队列已空不同函数的效果见下表:

定长的并发优化的BlockingQueue,也是基于循环数组取模实现有一把公共的锁与notFull、notEmpty两个Condition管理隊列满或空时的阻塞状态。

可选定长的并发优化的BlockingQueue基于链表实现,所以可以把长度设为Integer.MAX_VALUE成为无界无等待的

利用链表的特征,分离了takeLock与putLock兩把锁继续用notEmpty、notFull管理队列满或空时的阻塞状态。

无界的PriorityQueue也是基于数组取模存储的二叉堆(见前)。一把公共的锁实现线程安全因为無界,空间不够时会自动扩容所以入列时不会锁,出列为空时才会锁

内部包含一个PriorityQueue,同样是无界的同样是出列时才会锁。一把公共嘚锁实现线程安全元素需实现Delayed接口,每次调用时需返回当前离触发时间还有多久小于0表示该触发了。

SynchronousQueue同步队列本身无容量放入元素時,比如等待元素被另一条线程的消费者取走再返回JDK线程池里用它。

关注我:推荐一个程序员学习交流群:群里有分享的视频,还有思维导图

群公告有视频都是干货的,你可以下载来看主要分享分布式架构、高可扩展、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战学习架构师视频。

}

我要回帖

更多关于 数组取模 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信