ArrayList
To use the ArrayList
type, you need to import the collection
package:
import std.collection.*
ArrayList
is a generic type, so Cangjie uses ArrayList<T>
to denote it, where T
is the element type. T
can be any type or a type variable.
ArrayList
has excellent capacity expansion capability and is applicable to scenarios where elements must be stored sequentially and be directly accessible by their position in the sequence, as in an Array
, but in addition need to be frequently added to, and removed from, the sequence. Compared with Array
, ArrayList
can add and remove elements in place, without copying the elements left intact to a newly created instance.
ArrayList
is a reference type: all references to the same ArrayList
instance share the same elements and are modified in a unified manner. See Modifying an ArrayList
for details.
var a: ArrayList<Int64> = ... // ArrayList whose element type is Int64
var b: ArrayList<String> = ... // ArrayList whose element type is String
As generic types in Cangjie are invariant, two instantiations of ArrayList
with different element types are themselves different, incompatible types, even if their element types are in a subtype relationship. Therefore, values of those two types cannot be "cross-assigned" to variables.
Therefore, the following continuation of the last example is invalid:
b = a // Error: Type mismatch
In Cangjie, you use a constructor to create an ArrayList
with a particular element type. The different constructors enable you to pre-allocate storage and/or initialize the elements.
// Create an empty ArrayList of strings:
let a = ArrayList<String>()
// Create an ArrayList of strings, pre-allocating space for 100 elements:
let b = ArrayList<String>(100)
// Create an ArrayList of three Int64 integers, containing elements 0, 1, and 2:
let c = ArrayList<Int64>([0, 1, 2])
// Populate a freshly created ArrayList with elements of another collection "c":
let d = ArrayList<Int64>(c)
// Create an ArrayList of two strings, initialized by the specified rule function:
let e = ArrayList<String>(2, {x: Int64 => x.toString()})
Accessing ArrayList Members
When you need to access each element of an ArrayList
, you can use a for-in
loop expression to traverse it.
import std.collection.*
main() {
let list = ArrayList<Int64>([0, 1, 2])
for (i in list) {
println("The element is ${i}")
}
}
Compiling and executing the above code outputs the following information:
The element is 0
The element is 1
The element is 2
When you need to know the number of elements in an ArrayList
, you can use the size
property to obtain that information.
import std.collection.*
main() {
let list = ArrayList<Int64>([0, 1, 2])
if (list.size == 0) {
println("This is an empty arraylist")
} else {
println("The size of arraylist is ${list.size}")
}
}
Compiling and executing the above code outputs the following information:
The size of arraylist is 3
When you want to access a single element at a specific position, you can use the index operator [ ]
. The type of the index expression must be Int64
. The index of the first element of a non-empty ArrayList
is zero. You can access any element of an ArrayList
from 0 to the position of the last element, which is one less than the value of the size
property of the ArrayList
. Using a negative number or a number greater than or equal to size
as an index triggers a run-time exception.
let a = list[0] // a == 0
let b = list[1] // b == 1
let c = list[-1] // Runtime exception
ArrayList
also supports the syntax of Range
in indices. For details, see section Array.
Modifying an ArrayList
You can use the index operator [ ]
to modify an ArrayList
element at a certain position:
let list = ArrayList<Int64>([0, 1, 2])
list[0] = 3
ArrayList
is a reference type. When an ArrayList
is used as an expression, no copy is created. All references to the same ArrayList
instance share the same data. Therefore, modifications of an ArrayList
instance element affect all references to that instance:
let list1 = ArrayList<Int64>([0, 1, 2])
let list2 = list1
list2[0] = 3
// list1 contains elements 3, 1, 2
// list2 contains elements 3, 1, 2
To add a single element to the end of an ArrayList
, use the append
member function. If you want to add multiple elements to the end at the same time, you can use the appendAll
member function. That function can accept a value of another collection type (for example, Array
), provided its element type is the same.
import std.collection.*
main() {
let list = ArrayList<Int64>()
list.append(0) // list contains element 0
list.append(1) // list contains elements 0, 1
let li = [2, 3]
list.appendAll(li) // list contains elements 0, 1, 2, 3
}
You can use the insert
and insertAll
member functions in a similar manner to insert a specified element, or all elements of a collection with the same element type, at a specific position. The size of the ArrayList
first increases accordingly and the elements at and to the right of the specified index are moved towards the end of the resized ArrayList
to make space for the element(s) being inserted.
let list = ArrayList<Int64>([0, 1, 2]) // list contains elements 0, 1, 2
list.insert(1, 4) // list contains elements 0, 4, 1, 2
To delete an element from an ArrayList
, you use its remove
member function, specifying the index of the element to be deleted. The subsequent elements, if any, are then moved to fill the space, and the size
property is decremented.
let list = ArrayList<String>(["a", "b", "c", "d"]) // list contains the elements "a", "b", "c", "d"
list.remove(1) // Delete the element at index 1, now the list contains elements "a", "c", "d"
Increasing the Size of an ArrayList
Each ArrayList
reserves a certain amount of memory to hold its contents. When we add elements to an ArrayList
so that its reserved capacity would be exceeded, the ArrayList
allocates a larger memory area and copies all its elements to that new memory. This growth strategy means that operations adding elements have higher performance costs when they trigger such reallocation, but as the reserved memory of the ArrayList
increases, such operations occur less frequently.
If you know how many elements will be added to an ArrayList
, you can have it pre-allocate a sufficient amount of memory before adding elements to avoid intermediate reallocation, which improves performance.
import std.collection.*
main() {
let list = ArrayList<Int64>(100) // Allocate space at once
for (i in 0..100) {
list.append(i) // Does not trigger reallocation of space
}
list.reserve(100) // Prepare more space
for (i in 0..100) {
list.append(i) // Does not trigger reallocation of space
}
}