Created
May 19, 2017 10:47
-
-
Save SubOptimal/cfa46324881ded1efb82a81f3ef61aed to your computer and use it in GitHub Desktop.
related to this post on Twitter https://twitter.com/JosePaumard/status/864938907183480832
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static java.lang.System.out; | |
import java.lang.reflect.Field; | |
import java.util.AbstractList; | |
import java.util.ArrayList; | |
/** | |
* Related to an answer to this Twitter post | |
* https://twitter.com/JosePaumard/status/864938907183480832 | |
* | |
* @author Frank Dietrich <Frank.Dietrich@gmx.li> | |
*/ | |
class ArrayListDemo { | |
public static void main(String[] args) throws Exception { | |
makeFieldsAccessible(); | |
/** | |
* When we add elements to the ArrayList the backing array needs to be | |
* increased in size at some point. When we add elements to the end and | |
* the next element would be added on <code>array[array.size()]</code> | |
* the backing array is copied into a new array with size | |
* <code>array.size() + array.size() / 2</code>. | |
* | |
* For each change of the arrayList size an internal modification | |
* counter is increased. | |
* | |
* In the output array elements which are <code>null</code>, are | |
* displayed as a '#' character. | |
*/ | |
out.println("--- fill the list with latin characters"); | |
ArrayList<Object> list = new ArrayList<>(); | |
for (int loopCount = 1; loopCount <= 26; loopCount++) { | |
list.add((char) ('a' + loopCount - 1)); | |
Character[] backingArray = getBackingArray(list); | |
int modCount = getSizeModificationCounter(list); | |
print(loopCount, list, backingArray, modCount); | |
} | |
/** | |
* If we remove one element from the arrayList, all following elements | |
* in the backing array are shifted to the left. And the previous last | |
* element is set to <code>null</code>, so this object could be GCed. | |
* The size of the backing array is not reduced. | |
*/ | |
out.printf("%n--- remove every third character from the list%n"); | |
int iteration = 0; | |
for (int loopCount = 26 / 3; loopCount > 0; loopCount--) { | |
iteration++; | |
list.remove(loopCount * 3 - 1); | |
Object[] backingArray = getBackingArray(list); | |
int modCountValue = getSizeModificationCounter(list); | |
print(iteration, list, backingArray, modCountValue); | |
} | |
/** | |
* If we remove element from the list matching a predicate the removal | |
* process is done in three steps: | |
* | |
* - create a BitSet which contains the index position of elements in | |
* the backing array matching the prediacte, if one element in the | |
* arrayList is <code>null</code> a NPE is thrown and the backing array | |
* stays unaltered | |
* | |
* - shift the elements right from the removed ones to the left to fill | |
* the gaps | |
* | |
* - set the elements which are now behind the right end of the new | |
* arraylist size to <code>null</code> | |
*/ | |
out.printf("%n--- remove every character which comes before 'k' in the alphabet%n"); | |
list.removeIf((Object t) -> ((char) t) < 'k'); | |
Object[] backingArray = getBackingArray(list); | |
int modCountValue = getSizeModificationCounter(list); | |
print(1, list, backingArray, modCountValue); | |
} | |
static void makeFieldsAccessible() throws SecurityException, NoSuchFieldException { | |
fieldElementData = ArrayList.class.getDeclaredField("elementData"); | |
fieldElementData.setAccessible(true); | |
fieldModCount = AbstractList.class.getDeclaredField("modCount"); | |
fieldModCount.setAccessible(true); | |
} | |
static Character[] getBackingArray(ArrayList<Object> list) throws Exception { | |
Object[] objects = (Object[]) fieldElementData.get(list); | |
Character[] chars = new Character[objects.length]; | |
for (int i = 0; i < objects.length; i++) { | |
chars[i] = (Character) objects[i]; | |
} | |
return chars; | |
} | |
static int getSizeModificationCounter(ArrayList<Object> list) throws Exception { | |
return (int) fieldModCount.get(list); | |
} | |
static void print(int loopCount, ArrayList<Object> list, Object[] backingArray, int modCountValue) { | |
System.out.printf( | |
"iteration: %2d list.size(): %2d backingArray.length: %s size modification count: %2d %s%n", | |
loopCount, list.size(), backingArray.length, | |
modCountValue, prettyArray(backingArray)); | |
} | |
static String prettyArray(Object[] arr) { | |
StringBuilder sb = new StringBuilder(arr.length); | |
for (Object c : arr) { | |
sb.append(c == null ? '#' : c); | |
} | |
return sb.toString(); | |
} | |
static Field fieldModCount; | |
static Field fieldElementData; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
output