Object Arrays

An object array is a fixed-size sequence of objects:

def a= new Object[4] //we must specify the size of the fixed-size array
assert a.size() == 4
assert a.length == 4 //field alternative to size()
a.each{ assert it == null } //default value is null
assert a instanceof Object[]
assert a.class == Object[]

a[0]= 'a'
a[1]= 2 //elements can be any value
a.putAt(2, 'c') //alternative method name syntax
a[3]= false
assert a[0] == 'a' && a[1] == 2 && a.getAt(2) == 'c' && a.getAt(3) == false //either subscript or method name
assert a[-4] == 'a' && a[-3] == 2 && a[-2] == 'c' && a[-1] == false //subscripts can be negative

try{ a[4]; assert 0 }catch(e){ assert e instanceof ArrayIndexOutOfBoundsException }
try{ a[-5]; assert 0 }catch(e){ assert e instanceof ArrayIndexOutOfBoundsException }

assert a[1..2] == [2, 'c'] //we can use the same subscripting as for lists
assert a[2..2] == ['c']
assert a[0, 2..3] == ['a', 'c', false]

assert a.toList() == ['a', 2, 'c', false]
assert a as List == ['a', 2, 'c', false]
assert a.toArrayString() == '[a, 2, c, false]'

The subscript used in constructing object arrays is evaluated as an integer:

assert new Object[ 0x100000003 ].size() == 3 //index coerced to integer, positive or negative
try{ new Object[ 0x80000000 ]; assert 0 }catch(e){ assert e instanceof NegativeArraySizeException }

We can specify the initial collection of contained objects when we construct the array. Those objects can be any other entity in Groovy, eg, numbers, boolean values, characters, strings, regexes, lists, maps, closures, expandos, classes, class instances, or even other object arrays:

assert [
  'Hello, world',
  ['a', false, null, 5] as Object[],
  new Object[200],
  { it*it },
] as Object[]

We can make a shallow copy using clone():

def aq= [1,2]
assert ([ aq, 3 ] as Object[]).clone()[0].is( aq ) //clone() makes a shallow copy only

We have a special syntax for constructing multi-dimensional object arrays with null initial values:

assert [ new Object[3], new Object[2], new Object[1] ] as Object[] //usual syntax
assert [ new Object[3], new Object[3], new Object[3] ] as Object[] //usual syntax when each constituent array of equal size

def m= new Object[3][3] //special syntax when each constituent array of equal size
    assert m[i][j] == null //we can also subscript with special syntax using consecutive indexes

We must specify the size of at least the first, outermost, dimension of an object array when we first create it:

//ar= new Object[] //compile error when uncommented
ar= new Object[10][]
ar= new Object[10][][]
ar= new Object[10][10][]

A multidimensional array need not have arrays of the same length at each level. Thus, a triangular matrix may be created by:

def triangle= new Object[100][]
  triangle[it] = new Object[it+1]

There are strict rules concerning evaluation when subscripting object arrays:
class MyException extends Exception{}
def exception(){ throw new MyException() }
def i, a, b

i= 4
a= new Object[i][i=3] //first subscript evaluated before next one
assert a.size() == 4 && a[0].size() == 3

a= [ 11, 12, 13, 14 ] as Object[]
b= [ 3, 2, 1, 0 ] as Object[]
assert a[(a=b)[2]] == 12 //outside of subscript evaluated before inside, ie, a[b[2]] or a[1] or 12

i= 1 //if what's outside subscript throws exception, subscript isn't evaluated
try{ exception()[i=2] }catch(e){ assert i == 1 }

i= 1
a= new Object[2][2] //if subscript evaluation throws exception, subscripts to right not evaluated
try{ a[ exception() ][i=2] }catch(e){ assert i == 1 }

//index evaluated before indexing occurs (including checking whether what's outside subscript is null)...
a= null
try{ a[exception()]; assert 0 }catch(e){ assert e instanceof MyException } //NullPointerException never occurs here
i= 1
try{ a[i=2]; assert 0 }catch(e){ assert i == 2 && e instanceof NullPointerException }

Implementing an ArrayList with an Object Array

ArrayLists are implemented with object arrays internally. Each ArrayList instance has a capacity, the size of a fixed-size array used to store the elements. This array is always at least as large as the list size, and its capacity grows automatically as elements are added to the list. To see the internal capacity of lists constructed with various values:

class Extras{
  static enq(List l){ l.elementData.size() }
def measure= { list, times->
  def sizes= []
    def size
    use(Extras){ size= list.enq() }
    (size - list.size() + 1).times{ list << 'a' }
    sizes << size

def list1= new ArrayList()
def measure1= measure(list1, 10)
assert measure1 == [10, 16, 25, 38, 58, 88, 133, 200, 301, 452]

def list2= new ArrayList(10)
def measure2= measure(list2, 10)
assert measure2 == measure1

def list3= new ArrayList(5)
def measure3= measure(list3, 10) 
assert measure3 == [5, 8, 13, 20, 31, 47, 71, 107, 161, 242]

def list4= []
def measure4= measure(list4, 10)
assert measure4 == [0, 1, 2, 4, 7, 11, 17, 26, 40, 61]

def list5= new ArrayList(0)
def measure5= measure(list5, 10)
assert measure5 == measure4

For efficiency, we can increase the capacity of a list before adding a large number of elements:

class Extras{ static enq(List l){l.elementData.size()} }
  list= []
  assert list.enq() == 200
  list<< 'a'<< 'b'<< 'c'
  assert list.enq() == 200
  list.trimToSize() //we can also trim the internal fixed-size array to the list size
  assert list.enq() == 3

We can see how many times a list has been modified:

list= []<< 'a' << 'b'; assert list.modCount == 3
list.remove('a'); assert list.modCount == 4

Last edited Jan 28, 2011 at 12:28 PM by gavingrover, version 1


No comments yet.