Data Structures

Foreword

The array situation in ooc is a bit delicate right now - while I (Amos) am definitely not happy with it, I am still going to document the current state of things, if only as a motivation to make it better.

C arrays

On the heap

C arrays are really just pointers:

tenInts := gc_malloc(10 * Int size) as Int*
for (i in 0..10) {
  tenInts[i] = i
}
printInts(tenInts, 10)

printInts: func (arr: Int*, length: Int) {
  for (i in 0..length) {
    "#{arr[i]}" println()
  }
}

On the stack

Above, we are calling gc_malloc to allocate a block of GC-managed memory on the heap. If for some reason a stack-allocated C array is desirable, this syntax will work:

tenInts: Int[10]
for (i in 0..10) {
  tenInts[i] = i
}
printInts(tenInts[0]&, 10)

printInts: func (arr: Int*, length: Int) {
  for (i in 0..length) {
    "#{arr[i]}" println()
  }
}

ooc arrays

ooc arrays are more convenient / safer than C arrays because they:

  • hold the length (number of elements)
  • do bounds checking when accessing / writing to them

The syntax is as follows:

tenInts := Int[10] new()
for (i in 0..10) {
  tenInts[i] = i
}
printInts(tenInts)

printInts: func (arr: Int[]) {
  for (i in 0..arr length) {
    "#{arr[i]}" println()
  }
}

ArrayList

ArrayList is not technically part of the language - it is usually available in the ooc SDK. Its advantages over ooc arrays are:

  • you can accept an ArrayList<T> for any T
  • you can query the T of an ArrayList (ie. match the type)
  • you can add and remove elements anywhere in the list (whereas arrays are fixed-length)

Array-like usage

They can be used with array-like operators:

import structs/ArrayList

tenInts := ArrayList<Int> new()
for (i in 0..10) {
  tenInts add(i)
}
printInts(tenInts)

printInts: func (list: ArrayList<Int>) {
  for (i in 0..list size) {
    "#{list[i]}" println()
  }
}

Foreach usage

The printInts method above can be rewritten using a foreach to iterate over the list’s elements:

printInts: func (list: ArrayList<Int>) {
  for (i in list) {
    "#{i}" println()
  }
}

Iterator usage

Let’s say we want to remove every odd number from the list.

Since we are modifying it while iterating through it, the best device for that is an iterator:

removeOdds: func (list: ArrayList<Int>) {
  iter := list iterator()
  while (iter hasNext?()) {
    if (iter next() % 2 == 1) {
      // removes the element we just got.
      // NOTE: we are calling it on the iterator,
      // not on the list itself.
      iter remove()
    }
  }
}

Generics usage

Example usage of generics:

import structs/ArrayList

printList(ArrayList<Int> new())
printList(ArrayList<String> new())

printList: func <T> (list: ArrayList<T>) {
  "Got a list of #{list T name}" println()
}

In this case, list T is just a class.

Literals

Simple array literals will give ooc arrays:

tenInts := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
printInts(tenInts)

printInts: func (arr: Int[]) {
  for (i in 0..arr length) {
    "#{arr[i]}" println()
  }
}

Specifying the type the array literal is supposed to be allows C array literals:

tenInts := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as Int*
printInts(tenInts, 10)

printInts: func (arr: Int*, length: Int) {
  for (i in 0..length) {
    "#{arr[i]}" println()
  }
}

In the same fashion, ArrayList literals exist:

import structs/ArrayList

tenInts := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as ArrayList<Int>
printInts(tenInts)

printInts: func (list: ArrayList<Int>) {
  for (elem in list) {
    "#{elem}" println()
  }
}

Others

The ooc sdk is full of other data structures, such as LinkedList, HashMap (an dictionary associating keys and values), etc.

For more information, read up on the structs package.