How do I safely access a List element by index?

← Safe Value Access · Ref: Q162

EK9 Lists use getOrDefault(index, default) for safe element access. If the index is within bounds, returns the element. If out of bounds, returns the default. No exceptions.

GETORDEFAULT BY INDEX

Always returns a usable value:

  first <- numbers.getOrDefault(0, -1)
  missing <- numbers.getOrDefault(99, -1)

No IndexOutOfBoundsException, no null, no crashes.

LENGTH CHECK

Check bounds manually when needed:

  if length numbers > 0
    stdout.println(`Has items`)

EMPTY CHECK

Test whether a list has any elements:

  if numbers is empty
    stdout.println("No items")

SAFE ITERATION

Iterating with for-in never goes out of bounds:

  for item in numbers
    stdout.println($item)

Each item yielded is guaranteed to be set.

STREAM HEAD FOR FIRST ELEMENT

Use head 1 to safely get the first element:

  cat numbers | head 1 | collect as List of Integer

Returns an empty list if the source is empty.

See Q45 for List basics. See Q88 for List operations. See Q125 for head/tail/skip in streams.

Example

defines module qa.safeaccess.listsafe

  defines function
    getList()
      <- rtn <- List() of Integer

  defines program
    ListSafeAccessDemo()
      stdout <- Stdout()

      numbers <- [10, 20, 30, 40, 50]

      // === GETORDEFAULT BY INDEX ===

      // Valid index — returns element
      first <- numbers.getOrDefault(0, -1)
      stdout.println(`First: ${first}`)

      third <- numbers.getOrDefault(2, -1)
      stdout.println(`Third: ${third}`)

      // Out of bounds — returns default
      outOfBounds <- numbers.getOrDefault(99, -1)
      stdout.println(`Out of bounds (default -1): ${outOfBounds}`)

      // === LENGTH CHECK ===

      listLen <- length numbers
      stdout.println(`Length: ${listLen}`)

      if length numbers > 0
        stdout.println("List has items")

      // === EMPTY CHECK ===

      emptyList <- getList()
      if emptyList is empty
        stdout.println("Empty list detected")

      if ~numbers is empty
        stdout.println("Numbers is not empty")

      // === SAFE ITERATION ===

      for item in numbers
        stdout.println(`Item: ${item}`)

      // === STREAM HEAD FOR FIRST ELEMENT ===

      firstItems <- cat numbers | head 1 | collect as List of Integer
      stdout.println(`Head 1: ${firstItems}`)

      // Head on empty list yields empty result
      noItems <- cat emptyList | head 1 | collect as List of Integer
      stdout.println(`Head of empty: ${noItems}`)

Common mistakes

E50060 — List has no get(index) method that could throw on invalid indices. Use getOrDefault(index, default) which always returns a usable value. This prevents IndexOutOfBoundsException-style bugs. See ek9 -h E50060 for details.

Incorrect:

first <- numbers.get(0)

Correct:

first <- numbers.getOrDefault(0, -1)

E50060 — List has no elementAt() method. Use getOrDefault(index, default) which always returns a usable value. See ek9 -h E50060 for details.

Incorrect:

third <- numbers.elementAt(2)

Correct:

third <- numbers.getOrDefault(2, -1)
Other ways to ask this
  • How do I avoid index out of bounds in EK9?
  • What is the safe way to get an element from a List in EK9?
  • How do I access list elements without exceptions in EK9?

Coming from another language?

Java: list.get(index) throws IndexOutOfBoundsException if out of bounds. Python: list[index] throws IndexError, negative indices wrap. Rust: vec[index] panics, vec.get(index) returns Option. Go: slice[index] panics if out of bounds. Kotlin: list[index] throws, list.getOrElse(index) { default } is safe. EK9: getOrDefault(index, default) always returns a value, no exceptions.

Keywords: array, default, element, exception, list, bounds, access, index, getOrDefault, guard, null-safe, safe