Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How Java traverses the set and deletes some of its elements

2025-03-12 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the relevant knowledge of "how Java traverses the collection and deletes some of its elements". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

This paper analyzes the source code of ArrayList based on jdk1.8.

The first is the main member variables.

/ * Default initial capacity. * / private static final int DEFAULT_CAPACITY = 10; / * * Shared empty array instance used for empty instances. * / private static final Object [] EMPTY_ELEMENTDATA = {}; / * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. * / private static final Object [] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; / * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData = = DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. * / transient Object [] elementData; / / non-private to simplify nested class access / * The size of the ArrayList (the number of elements it contains). * * @ serial * * / private int size

Where the initial size of 10 is the number of elements in the collection. In addition, there are two empty arrays EMPTY_ELEMENTDATA, and DEFAULTCAPACITY_EMPTY_ELEMENTDATA. Through the comments of DEFAULTCAPACITY_EMPTY_ELEMENTDATA, we can see that this variable is different from EMPTY_ELEMENTDATA in order to determine how much to expand when the first element is inserted. As you can see from the description here, after the ArrayList is created, the array space is not really allocated, but the space that is allocated when the first element is inserted. This is different from jdk1.6. In jdk1.6, once the ArrayList is created, the data space is allocated by default, 10 or specified spaces. By doing so, jdk1.8 can delay space allocation and improve program performance.

Next, take a look at the constructor.

/ * Constructs an empty list with an initial capacity of ten. * / public ArrayList () {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;} / * * Constructs an empty list with the specified initial capacity. * * @ param initialCapacity the initial capacity of the list * @ throws IllegalArgumentException if the specified initial capacity * is negative * * / public ArrayList (int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object [initialCapacity];} else if (initialCapacity = = 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException ("Illegal Capacity:" + initialCapacity);}}

A no-parameter constructor that creates an empty array of length 0.

With a parameter constructor, an array is created normally when the parameter is greater than 0, and an array with a length of 0 is also created when the parameter is 0. But it can be distinguished from empty arrays created by no-parameter constructors, using different objects.

Next, insert the element add.

/ * Appends the specified element to the end of this list. * * @ param e element to be appended to this list * @ return true (as specified by {@ link Collection#add}) * * / public boolean add (E) {ensureCapacityInternal (size + 1); / / Increments modCounting! ElementData [size++] = e; return true;} private void ensureCapacityInternal (int minCapacity) {ensureExplicitCapacity (calculateCapacity (elementData, minCapacity));} private static int calculateCapacity (Object [] elementData, int minCapacity) {if (elementData = = DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max (DEFAULT_CAPACITY, minCapacity);} return minCapacity;} private void ensureExplicitCapacity (int minCapacity) {modCount++; / overflow-conscious code if (minCapacity-elementData.length > 0) grow (minCapacity) }

Through the calculateCapacity function, we can know that if the list is created with new ArrayList (), the first time the add element is calculated, minCapacity = 1. If the list is created by new ArrayList (0), the calculated minCapacity is 10. 0. Then go to grow according to minCapacity.

The get method is relatively simple and will not be analyzed here.

A common problem with ArrayList is ConcurrentModificationException, a synchronous modification exception, also known as a quick failure, fast-fail. When we iterate through ArrayList in foreach, this exception is thrown if the element of ArrayList is deleted during the traversal, or if another thread adds an element to the ArrayList. It should be noted here that for (int I = 0; I)

< list.size(); i++)的方式遍历ArrayList时,是不会抛出同步修改异常的,但用这种方式遍历,需要处理好i的前进速度。 那么,用foreach方式遍历ArrayList为什么会抛出同步修改异常呢? foreach代码的底层实现,是用iterator对ArrayList进行遍历,在遍历过程中,会持续调用next获取下一个元素。next方法中,会首先checkForComodification(),它的作用是检查modCount和expectedModCount是否相等。不相等时,则抛出同步修改异常。那么什么情况下修改次数和期望修改次数不相等呢?这里需要首先弄明白,modCount和expectedModCount是什么东西?modCount是ArrayList从它的父类继承来的属性,记录了集合的修改次数,add,remove时都会给modCount加1. expectedModCount是迭代器的成员变量,它是在创建迭代器时,取的modCount的值,并且,在遍历过程中不再改变。那么就清楚了,expectedModCount其实是开始遍历时modCount的值,如果在遍历过程中,ArrayList进行了add或remove操作,那么必然导致expectedModCount和modCount不相等,于是就抛出了同步修改异常。 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 ();}

So, how can synchronous modification exceptions be avoided? In other words, how do we traverse the collection and delete some of the elements?

The answer is to delete the element using the iterator's remove method. In the iterator's remove method, modCount is re-assigned to expectedModCount after the element is deleted, so it does not throw a synchronous modification exception.

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 () That's all for "how Java traverses the collection and deletes some of its elements". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report