Li Ll

Instead of arrays, LSL uses lists. The list type is exactly what it sounds like: a heterogeneous list of the other data types. Lists are created via comma-separated values (CSV) of the other data types. enclosed by square brackets: “[” and “]”. See below for examples of the syntax.

Lists not only store the value of the list item, but also its type (see llGetListEntryType and llCSV2List). To directly enter a float value into a list, a decimal point (.) must be used.

Because they store multiple value types, lists are not accessed with square brackets like arrays are. They are read by accessor functions that specify the type of value attempting to be retrieved (e.g. llList2Integer) and written with accessor functions that insert or replace values (or ranges of values) based on new list variables (e.g. llListReplaceList).

Note: while the LSL compiler will only accept code with a maximum of 72 items in a list, lists can actually support as many items as a script’s memory will allow. If a list of more than 72 predefined items is needed, just concatenate (combine) two predefined lists into a new one: longlist = firstpart + secondpart;
As of 1.20.4, this no longer seems to be true. A list with at least 100 is possible, though I haven’t tested to see just how many elements I can initialize a list to. – BlueJeansAndRain
June 5, 2008 – Server | Client 1.19.1 (4): I was able to create a global list containing 1,081 null integers and a local list containing 2,318 null integers before receiving “(0, 0) : Byte code assembly failed — out of memory”. – CoryFimicoloud

Lists cannot contain other lists, so using them like a multidimensional array is not possible natively (although several ways around this are available, notably this script). Also, strided lists can be used.

It’s also worth noting that with functions, lists are not referenced; they are duplicated locally. This means that, as an example:

iUseLists(list input){...} 

handed the list:

list oh_hi = [data here]; 

results in “input” being a duplicate of the data held in “oh_hi”, rather than oh_hi being operated on. This is important, as many higher level languages (the most obvious in my mind being java) operate on the original list (contextually, array). In practice, this means that a scripter attempting to use this functionality erroneously could easily end up flooding their script memory with a large list, and not having their expected results in the original list as well.


//a list with a string, integer, float, vector and rotation
list l = ["somestring",12,3.0,<3.3,4,0>,<0,0,0,1>];

Lists can be concatenated by using +.


newlist = oldlist + ["oneitem","anotheritem"]; 

Thus, to add an element to the (end) of an existing list:


myList = myList + [new_item];


myList += [new_item]; 

This voodoo magic will allow appending new elements in a memory efficient fashion (thanks to BlindWanderer):

myList = (myList=[]) + myList + ["new_item"]; 

Don’t ask me why but I can save 3-4KB of memory with adding 90 elements. – PsykePhaeton

Is this broken as of Aug-21-2007? It doesn’t appear to work – VagnerSousaBR

“This voodoo magic seems to work fine. Just pay attention to the “new_item” square brackets when it’s already returned as a list type in a function like llParseString2List(). I got a stack-heap collision error when I was composing an huge list by the usual way. This way has worked fine.”

This works because LSL makes a temporary copy of all variables in an expression before it evaluates it. Say you’re concatenating two 2kb lists: You originally use a total of 4kb of memory. The normal method of concatenation would copy them (then requiring a total of 8kb), then concatenate them (total of 12kb), then copy them into the original variable (14kb), then destroy all the temporary copies. The “voodoo magic” here is that after the temporary copies are made for evaluating the expression, the original variable is cleared, requiring extra memory only during the copy. This gives the rest of the operation a significant amount of breathing room. -DunselHodgson

This trick is no longer needed with Mono. See the Q&A section for evidence. – MimikaOh

It seems that this trick can also be used to great effect when passing lists to functions that change the list and then overwrite the same list with the new copy, such as llDeleteSubList. From my testing on a large sample list using calls to llGetFreeMemory(), this appears to use no memory at all, where the normal way of doing it would use over 2000 bytes. I’m not sure if these figures are really correct, but it certainly made it a lot less likely to cause a stack/heap collision when the list was very big, so it must be saving at least some memory, and the resulting list was as expected. The syntax for this is as follows:

myList = llDeleteSubList((myList=[])+myList, 0, 0); 

Instead of the normal way, which would be:

myList = llDeleteSubList(myList, 0, 0); 

Note: This temporarily clears the original list variable before saving the result back into it, so if you save the result in a different variable instead, the original one will end up empty after this. –EddyOfarrel

EddyOfarrel results appear correct that the ‘voodoo’ trick works in the list modification functions. My memory usage results can be found on my user page. – CoryFimicoloud

Q: Does anyone know whether any of this applies to scripts compiled with Mono? (From my initial testing, it seems like using this kind of trick makes no difference to the amount of memory used by the operation in a Mono script, and may actually cause the script to use more memory to start with. I suspect this probably makes sense, as Mono probably doesn’t do things in such an inefficient way to start with, but it would be nice if someone who knows more about how exactly this trick and Mono work could confirm this.) -EddyOfarrel

I believe it does not apply to Mono. Here is my test.

default {
    state_entry() {
        list l;
        integer i = 0;
        while (TRUE) {
            l = l + [i];  // or add the (l=[]), see below
            if (i % 100 == 0) llOwnerSay((string)i);


Before Mono this reaches 1000 with the voodoo trick, 400 without. With Mono it reaches 3100 with or without the voodoo. — MimikaOh

To clear a list, set it equal to nothing:

myList = []; 

To search a list for a value, use llListFindList

list myList = ["A","B","C","D","E","F"];
string searchFor = "C";

integer index = llListFindList( myList, [searchFor] );

if ( index != -1 )
    llOwnerSay( searchFor + " was found in myList at position " + (string)index );
    llOwnerSay( searchFor + " was not found in myList" );


Refer to a list’s elements by their index number. Lists have 0-based indexing, where the first element has index 0.

list myList = ["A","B","C"];
string element;

element = llList2String(myList,0);
// element now has the value "A".

element = llList2String(myList,1);
// element now has the value "B".

element = llList2String(myList,2);
// element now has the value "C".


The value llGetListLength() returns the number of elements in the list. Therefore, the last element has index = (llGetListLength(myList) - 1).

list myList = ["A","B","C"];
integer listLength = llGetListLength(myList);
// listLength equals 3.

string element = llList2String(myList,listLength);
// myList doesnt have an element at index 3 - this will return an empty string.

string element = llList2String(myList,listLength - 1);
// element now has the value "C".


Negative numbers can also be used to count backwards in a list. Therefore, the last element has index -1. The first element would also have an index of (-llGetListLength(myList))

list myList = ["A","B","C"];
integer listLength = llGetListLength(myList);
// listLength equals 3.

string element = llList2String(myList,-listLength);
// element now has the value "A".

string element = llList2String(myList,-1);
// element now has the value "C".



Function Name Purpose
llGetListLength Gets the number of elements in a list
llCSV2List Converts comma-separated values (CSV) to a list
llList2CSV Converts a list to comma-separated values (CSV)
llDeleteSubList Removes a portion of a list
llList2List Returns a portion of a list
llListFindList Returns the position (index) of the first instance of a sublist in a list
llListInsertList Inserts a list into a list
llListReplaceList Replaces a part of a list with another list
llDumpList2String Dumps a list to a string with a specified separating string
llParseString2List Parses a string to a list, using two lists of separators
llParseStringKeepNulls Similar to llParseString2List, but adds null values between spaces
llList2ListStrided Extracts a subset of a strided list
llListSort Sorts a list ascending or descending, as appropriate to type.
llListStatistics Performs statistical operations on a list composed of integers and floats.
llListRandomize Randomizes a list
llGetListEntryType Gets the type of entry of an element in list
llList2Float Returns the float element
llList2Integer Returns the integer element
llList2Key Returns the key element
llList2Rot Returns the rotation element
llList2String Returns the string element
llList2Vector Returns the vector element

To move a list entry from src to dest:

list ssMoveListEntry(list myList, integer src, integer dest) {
    string entry = llList2String(myList, src);  // Warning: will lose type information and may cause precision loss for float values
    myList = (myList=[]) + llDeleteSubList(myList, src, src);
    myList = (myList=[]) + llListInsertList(myList, (list)entry, dest + (dest > src));
    return myList;

If losing type information or precision is an issue use:

list ssMoveListEntry(list myList, integer src, integer dest) {
    list entry = llList2List(myList, src, src);
    myList = (myList=[]) + llDeleteSubList(myList, src, src);
    myList = (myList=[]) + llListInsertList(myList, entry, dest + (dest > src));
    return myList;

Strided Lists

Since lists cannot contain items of the list type, the only way to get something similar to a multidimensional array is using strides. A strided list is a regular list with items grouped together in a fixed layout.

Say you wanted to make a visitor tracker and keep track of visitor’s names, time spent on your land, and the date they visited last. You could use a strided list, where each stride consists of three items:

integer STRIDELENGTH = 3; // this is very helpful when dealing with strided lists to keep your code flexible
list visitors = ["Ama Omega", 5, "2004-06-12", "Catherine Omega", 12, "2004-06-28", "Ezhar Fairlight", 25, "2004-06-30"];

Whenever you manipulate this list, you do it in complete strides, so to add another visitor to the list:

visitors += ["Mistress Midnight", 1, "2004-06-30"];

To sort the list by names in ascending order:

visitors = llListSort(visitors, STRIDELENGTH, TRUE);

To remove one or more strides from the list:

list DeleteSubListStrided(list src, integer start_stride_number, integer end_stride_number, integer stride_len) {
    return llDeleteSubList(src, start_stride_number * stride_len, (end_stride_number + 1) * stride_len - 1);

visitors = DeleteSubListStrided(visitors, 0, 0, STRIDELENGTH);  // delete the first stride in the visitors list

To replace a stride in the list:

list UpdateSubListStrided(list src, list new_stride, integer stride_number, integer stride_len) {
    return llListReplaceList(src, new_stride, stride_number * stride_len, (stride_number + 1) * stride_len - 1);

visitors = UpdateSubListSTrided(visitors, ["Cynthia Russell", 1, "2008-10-02"], 2, STRIDELENGTH);  // replace third entry with Cynthia's visit


The following functions have direct support for strided lists:

  • llList2ListStrided
  • llListRandomize
  • llListSort

More examples of functions dealing with strided lists can be found on this page. LibraryStridedLists

Q & A

Q: Does this function have O(1) time? I want to use it a lot on some big lists.
A: No, it takes much longer with growing list size. Better get used to LSL being painfully slow when manipulating large data sets (strings by far being the worst).
Re-Question: So, does that mean llListInsertList and llDeleteSubList actually make copies, i.e., reallocate, the entire list they’re operating on?
Re-Answer: Yup. LSL is a pass-by-value language. Any information passed to a function is copied to that function’s stack-frame before the function is run. This means that in practical terms, you need twice as much memory to insert or delete a sublist. (See the notes on the llListInsertList and llDeleteSubList pages to eliminate some confusion about the “manipulation” they do.)


Q: Why are there so many llList2* functions?
A: LSL2 doesn’t support run-time typing, meaning you have to actually specify the type of each variable. The Lindens could have implemented a single function, llList2String, then required explicit typecasting, as in (integer)llList2String(foo, 0). However, that isn’t as memory- or CPU-efficient as llList2Integer(foo,0) (yes, really) and the amount of work required for the Lindens to implement seperate functions for each type was negligible.
You can still typecast values stored in a list, of course, but it’s advised to use the llList2* functions, unless it’s a vector or rotation stored as a string, in which case you should cast to a vector or rotation after using llList2String. llList2Vector and llList2Rotation do not cast from strings automatically. See the next question for more.


Q: Have the old problems concerning the odd behavior of llList2Vector and llList2Rot when requesting a TYPE_STRING element from the list been fixed yet?
A: No. If a list element is a string containing text that would otherwise be directly typecast to a vector or rotation, It will not be converted. The resulting vector or rotation will be ZERO_VECTOR or ZERO_ROTATION respectively. This is a bug. To get around it, use llList2String to retrieve elements from your list, then cast them to vectors or rotations as needed: (vector)llList2String(positions,2);

Lists can be conveniently passed as a single string using the CSV functions. For example, this is useful for sending complex data between objects via listen or within linked objects with llMessageLinked. Or not, if you would be passing data with vectors, rotations, or strings with commas. llDumpList2String and llParseString2List are recommended for safe passing of lists. For an example of this method see ExampleListConversion.

I’ve tried running tests on memory usage, comparing mono and LSL. They both seem to use the same memory usage for storing lists. And trying to get some kind of formula about the memory usage of a list of a given length that’s populated purely with integers, I’m getting very schizophrenic results. A list consisting of just one integer uses 112 bytes of memory. 10 integers also used 112 bytes of memory. 100 integers used 1988 bytes. A list of 1000 integers used 20028 bytes. What does it seem that there’s an upwards curve in the memory usage as lists grow longer? And when the heck are we going to get access to proper arrays? – LuccaKitty
LuccaKitty, typically list allocates memory for certain number of elements (usually 10) immediately to avoid memory fragmentation and the cost of resizing, and that’s why you don’t see increase in memory usage until the 11th element is inserted in the list. – DomchiUnderwood