Skip to content

Instantly share code, notes, and snippets.

@SubOptimal
Created May 19, 2017 10:47
Show Gist options
  • Save SubOptimal/cfa46324881ded1efb82a81f3ef61aed to your computer and use it in GitHub Desktop.
Save SubOptimal/cfa46324881ded1efb82a81f3ef61aed to your computer and use it in GitHub Desktop.
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;
}
@SubOptimal
Copy link
Author

output

--- fill the list with latin characters
iteration:  1  list.size():  1  backingArray.length: 10  size modification count:  1  a#########
iteration:  2  list.size():  2  backingArray.length: 10  size modification count:  2  ab########
iteration:  3  list.size():  3  backingArray.length: 10  size modification count:  3  abc#######
iteration:  4  list.size():  4  backingArray.length: 10  size modification count:  4  abcd######
iteration:  5  list.size():  5  backingArray.length: 10  size modification count:  5  abcde#####
iteration:  6  list.size():  6  backingArray.length: 10  size modification count:  6  abcdef####
iteration:  7  list.size():  7  backingArray.length: 10  size modification count:  7  abcdefg###
iteration:  8  list.size():  8  backingArray.length: 10  size modification count:  8  abcdefgh##
iteration:  9  list.size():  9  backingArray.length: 10  size modification count:  9  abcdefghi#
iteration: 10  list.size(): 10  backingArray.length: 10  size modification count: 10  abcdefghij
iteration: 11  list.size(): 11  backingArray.length: 15  size modification count: 11  abcdefghijk####
iteration: 12  list.size(): 12  backingArray.length: 15  size modification count: 12  abcdefghijkl###
iteration: 13  list.size(): 13  backingArray.length: 15  size modification count: 13  abcdefghijklm##
iteration: 14  list.size(): 14  backingArray.length: 15  size modification count: 14  abcdefghijklmn#
iteration: 15  list.size(): 15  backingArray.length: 15  size modification count: 15  abcdefghijklmno
iteration: 16  list.size(): 16  backingArray.length: 22  size modification count: 16  abcdefghijklmnop######
iteration: 17  list.size(): 17  backingArray.length: 22  size modification count: 17  abcdefghijklmnopq#####
iteration: 18  list.size(): 18  backingArray.length: 22  size modification count: 18  abcdefghijklmnopqr####
iteration: 19  list.size(): 19  backingArray.length: 22  size modification count: 19  abcdefghijklmnopqrs###
iteration: 20  list.size(): 20  backingArray.length: 22  size modification count: 20  abcdefghijklmnopqrst##
iteration: 21  list.size(): 21  backingArray.length: 22  size modification count: 21  abcdefghijklmnopqrstu#
iteration: 22  list.size(): 22  backingArray.length: 22  size modification count: 22  abcdefghijklmnopqrstuv
iteration: 23  list.size(): 23  backingArray.length: 33  size modification count: 23  abcdefghijklmnopqrstuvw##########
iteration: 24  list.size(): 24  backingArray.length: 33  size modification count: 24  abcdefghijklmnopqrstuvwx#########
iteration: 25  list.size(): 25  backingArray.length: 33  size modification count: 25  abcdefghijklmnopqrstuvwxy########
iteration: 26  list.size(): 26  backingArray.length: 33  size modification count: 26  abcdefghijklmnopqrstuvwxyz#######

--- remove every third character from the list
iteration:  1  list.size(): 25  backingArray.length: 33  size modification count: 27  abcdefghijklmnopqrstuvwyz########
iteration:  2  list.size(): 24  backingArray.length: 33  size modification count: 28  abcdefghijklmnopqrstvwyz#########
iteration:  3  list.size(): 23  backingArray.length: 33  size modification count: 29  abcdefghijklmnopqstvwyz##########
iteration:  4  list.size(): 22  backingArray.length: 33  size modification count: 30  abcdefghijklmnpqstvwyz###########
iteration:  5  list.size(): 21  backingArray.length: 33  size modification count: 31  abcdefghijkmnpqstvwyz############
iteration:  6  list.size(): 20  backingArray.length: 33  size modification count: 32  abcdefghjkmnpqstvwyz#############
iteration:  7  list.size(): 19  backingArray.length: 33  size modification count: 33  abcdeghjkmnpqstvwyz##############
iteration:  8  list.size(): 18  backingArray.length: 33  size modification count: 34  abdeghjkmnpqstvwyz###############

--- remove every character which comes before 'k' in the alphabet
iteration:  1  list.size(): 11  backingArray.length: 33  size modification count: 35  kmnpqstvwyz######################

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment