# Ada Programming/Libraries/Ada.Containers.Vectors

This language feature is only available from Ada 2005 on.
Ada.Containers.Vectors is a unit of the Predefined Language Environment since Ada 2005.
This generic package supplies a container (Vector) which can store any definite subtype in a consecutive list. This makes an Ada.Containers.Vectors.Vector similar to an array — however a Vector can change size after it has been declared, which an array can't do. For that reason, vectors are also known as dynamic arrays or resizable arrays.

## Introduction

One of the major additions to Ada 2005 is the container library. This library enables the Ada developer to manipulate data structures such as doubly linked lists, maps, sets and vectors. This page will show how the Ada.Containers.Vectors package works.
But first: What is a vector? Here's what the reference manual has to say about it:
   The language-defined generic package Containers.Vectors provides private types Vector and Cursor, and a set of operations for each type. A vector container allows insertion and deletion at any position, but it is specifically optimized for insertion and deletion at the high end (the end with the higher index) of the container. A vector container also provides random access to its elements.
   A vector container behaves conceptually as an array that expands as necessary as items are inserted. The length of a vector is the number of elements that the vector contains. The capacity of a vector is the maximum number of elements that can be inserted into the vector prior to it being automatically expanded.
   Elements in a vector container can be referred to by an index value of a generic formal type. The first element of a vector always has its index value equal to the lower bound of the formal type.
   A vector container may contain empty elements. Empty elements do not have a specified value.
Basically it's a one-dimensional array, and in many ways it behaves just like an array, allowing both random and sequential access to the elements of the vector. The main difference, is that a regular array is bound in size, whereas a vector is not, insofar as there are enough resources available to contain the vector. A vector will expand automatically when new elements are added.
The flexibility of vectors is a wonderful thing, but it does come at a cost: A vector is not as fast and lightweight as a regular array. Vectors are by no means slow and/or resource-hungry, but they are both slower and heavier than a regular array. On the other hand, if you can live with the higher resource demands, vectors do give you a great set of very intuitive tools to manage your lists of data.

## Using Ada.Containers.Vectors

Learning how to use the Ada.Containers.Vectors library is, in my humble opinion, best done with actual, working code. I'm not a big fan of pseudo-code, so all examples on this page will compile and work.
The examples all center around a small "Quotes by Linus Torvalds" application, where we have a bunch of quotes in a regular, flat text file. The file (quotes.txt) will be used throughout all the examples, and it looks like this:
In case it isn't obviously clear, each line represents one quote from Linus.
The idea is that quotes are added and removed from this file on a whim, and as such the program doesn't know how many quotes there are before we've read them all, making vectors the perfect data structure to both hold and manipulate all the quotes.
We're not actually going to build the entire application. We'll just focus on the part where the quotes are read from the quotes.txt file, and then added to a vector.
Before we start on the actual code, lets take a peek at the beginning of the Ada.Containers.Vectors specification:
Note the types Index_Type and Element_Type. The former is what defines the index of the vector and the latter is the type of elements the vector contains. With that in mind, lets have a look at the basic Quotes program itself:
If we focus on the vector related parts of the program, we see that on line 4 the Ada.Containers.Vectors library unit is added to the program. This step is vital to being able to use the vectors at all, just as Ada.Text_IO is necessary for basic string IO. If this concept is unfamiliar to you, you should probably read up on the basics of Ada and then return here again.
With the vectors library readily available, we turn our attention to line 9, where we instantiate the Vectors generic with the parameters Natural and Unbounded_String. The result is a vector where the index starts at 0 (remember that Natural is a subtype of Integer with the range 0 .. Integer'Last) and the elements are of type Unbounded_String. On line 11 we declare the Quotes variable as type Vector. We don't need the full declaration, Quote_Container.Vector, because of the use Quote_Container clause at line 10.
We now have a working vector, Quotes, so the next natural step is adding some data to it. This is done on line 18, where each quote is appended to the vector, using the Append procedure. Finally we close the file, and exit the program.
This little Quotes program is the foundation on which this entire article is built. Nearly all of the examples below are simply expanding on this program. But before we move ahead with the various procedures and functions of the vectors library, lets take short look at the vectors package for indefinite types.

## Ada.Containers.Indefinite_Vectors

The sibling library Ada.Containers.Indefinite_Vectors is available for indefinite types. It works in much the same way as Ada.Containers.Vectors, except for a few omitted Insert procedures. Here's an example on how it works:
Compile and execute this code, and you get:
It is important to note, that the indefinite vectors are slower than the constrained vectors, so avoid them if performance is important.
Because the definite and indefinite containers are so alike, the rest of this article will concern itself only with the definite version of the vectors library.

## Procedures and functions

There's a lot of procedures and functions in the Ada.Containers.Vectors package. Luckily they are all very aptly named, so figuring which procedure or function you need for a specific situation is usually pretty simple. To further help in identifying what exactly procedure X or function Y does, I've divided them into several groups, according to their main functionality.
Some procedures/functions might fit in more than one category. In those cases, I've placed them in the category in which I feel they are the most commonly used.

## Setting a vectors size/capacity

In the Ada.Containers.Vectors package there are tools available to create vectors of a specified size/capacity. The primary goal of these methods is to enable the programmer to assign the amount of resources needed, thereby getting rid of the performance overhead of calling Reserve_Capacity internally whenever a new element is added to the vector.

#### Vectors.Reserve_Capacity

Here's the specification for Reserve_Capacity:
The Ada Reference Manual explains Reserve_Capacity best:
There are really only a few cases where using Reserve_Capacity makes any sense, and some of those rare cases are:
* Prepare resources for a vector of a certain size
* Release excessive resources
Of course there might be several other reasons, but those two are probably the most common, with the first one being the absolute winner. So, lets go ahead and add the following code to the body of the Quotes program, after the call to IO.Close (Input); on line 20:
Compile and execute Quotes and you get:
Lets try and expand a bit on the Quotes program, and see how we can manipulate the vector during execution of the program. Place this before the call to IO.Open:
And this after the call to IO.Close:
And the output:
If you're working with big vectors, it might be worth it to check if a few well-placed Reserve_Capacity calls can speed up your code and/or save you some resources, but as with all optimization: Don't do it prematurely and test it rigorously.

#### Vectors.Set_Length

Here's the specification for Length:
One might wonder what the point of a procedure like Set_Length is, since the vector will just grow as new elements are appended/prepended/inserted, so why? In order to answer this question, it's important to understand that a vector's length can never exceed its capacity.
The capacity of a vector is the number of internal data structures set aside for the vector. This might be a lot higher than the vector's actual length, but it can never be less (see Vectors.Capacity). So when a new element is added to a vector, first the capacity of the vector is expanded using Reserve_Capacity (if necessary of course) and then the new element is added. So adding 100 new elements to a vector, effectively calls Reserve_Capacity 100 times. Surely this is a waste of resources, and it's exactly that waste we can avoid by using Set_Length. If we know we'll be adding a bunch of new elements, we can accumulate all those calls to Reserve_Capacity into one call to Set_Length. This is much more efficient.
Another use for Set_Length is to shorten a vector, as we'll see in the following example:
And the output:
To show the benefits of using Set_Length, I've done two small programs. The first adds 10_000_000 new elements using Append. The second program does the same, but it does so using Set_Length and Replace_Element.
Here's how this looks when timed on my computer:
Now, let us rewrite this little gem with the aid of Set_Length and re-time it:
And here are the numbers for this version:
This is a massive improvement in execution time, so be sure to use Set_Length if you need to add lots of new elements to a vector.
The above results are an average of 10 runs.

#### Vectors.To_Vector

The specification for To_Vector looks like this:
It should be very obvious what the two To_Vector functions do. I'm going to lump the examples for both functions together, as they are so alike. Add this to the body of the Quotes program:
And the output is:
So what have we learned? Well, the first To_Vector function creates a new vector with Length empty elements, and the second To_Vector functions creates a new vector with Length elements that all have the value New_Item.
As we'll learn later, it is actually a bounded error to read an empty element, so the second To_Vector is probably the safest to use, as you force a valid value into each element.

## Inserting data and/or empty elements

Inserting data into a vector is a crucial part of working with vectors. Empty vectors are usually not that interesting, so lets see how we can add some data to them.

#### Vectors.Append

The specification for Append looks like this:
What Append does, is add elements or other vectors to the end of a vector.
If we add some output functionality to the program, we can see how this works:
The Element function will be discussed in detail later, but for now it's enough to know that the Element function returns the element (Element_Type) found at the given index (Index_Type), in this case index 10.
Executing the program should result in the following output:
To make it absolutely clear what Append does, lets add these three lines to the body of the program:
Executing the program, we now get this output:
Looking at the specification for the first Append procedure, you might've noticed the optional Count parameter. The functionality of this parameter closely matches its name, as it enables you to repeat the insertion of the element Count amount of times. Lets see it in action. Try adding this to the body of the Quotes program:
And the output:
In the above example, we've used a Count value of three, and as expected the element "Test append" is appended to the vector three times.
The second Append procedure in the Vectors package appends a vector to another vector. To see how this works, we'll need to create a new vector, which requires an addition to the declarative part of the program. Add the following declaration:
What we've just done is declare a new vector, My_Quotes. This is the vector we will append to the Quotes vector.
Now add this to the body of the program:
The output of executing this program is:
Notice that we're using the same method as if we had appended a regular element, except that there's no Count parameter used in the Append procedure that appends vectors.
There's not much else to say about the Append procedure, so lets move on to its sibling: Prepend.

#### Vectors.Prepend

The specification for Prepend looks like this:
Prepend is very similar to Append, the only difference being that data is inserted at the beginning of the vector instead of at the end. To see it in action, add the following to the body of the basic Quotes program:
The result when running the program is, as expected:
As you can see, the "Prepended!" text is added to the vector before the ego quote from Linus.
It is also possible with Prepend to join two vectors. The functionality is the same, the only difference being that a vector is being copied instead of a single element.

#### Vectors.Insert

The specification for Insert looks like this:
Insert enables us to insert one or more identical elements anywhere in the list. It also introduces us to a new concept: Cursors.
But lets start with the Insert procedures that are using the index to identify individual elements. Since they are very similar to each other, I'll put them all in the same example. Add the following to the body of the Quotes program:
The output of this program is:
Note that the last call of Insert does not insert two empty elements. Instead, two elements of the vectors default value is inserted. As far as I know, this value currently is empty, but I wouldn't rely on it. If you need to insert empty elements, look further down for the Insert_Space procedures.
With Insert you can insert new elements anywhere in the vector, simply by setting the Before parameters appropriately. The vector will expand as necessary and elements following the Before index will be pushed down by the amount of the Count parameter (the default for which is 1).
The remaining Insert procedures all make use of a cursor. With a cursor you address both the vector and the element. In the next example I'll show all the Insert procedures using cursors. In order to use a cursor, we must first declare it, so add the following to the Quotes declaration:
And add the following to the body:
The result of running the program is:
When I first wrote this, I had a hard time coming up with good reasons for using cursors with the Ada.Containers.Vectors library - To me, using the index seemed more natural and more "in tune" with a vector, but later on I learned that there are in fact at least two good reasons for having cursors available:
* They make generic algorithms possible
* If a program is created using cursors, it is easy to switch to one of the other sequence containers
So there are in fact at least two good reasons for using cursors. Of course, if you are computing index values, using cursors doesn't gain you anything. As usual we have to think hard about what we need, and choose the tools accordingly.

#### Vectors.Insert_Space

When you need to insert a bunch of "empty" elements, then Insert_Space is just the right tool for the job. The specification for Insert_Space looks like this:
An important thing to note when working with Insert_Space, is that "empty" elements really don't mean that. Here's what the Reference Manual has to say about it:
And later:
The important thing to take away from all this, is that "empty" really means "unpredictable value". It all depends on the compiler, and how it treats the new element, so don't expect Insert_Space to deliver neat and tidy empty elements. It may well just hand you a bunch of copies of already existing elements or some other value, as long as it is valid in the context of the Vector, in our case meaning an Unbounded_String. Also please note that some compilers may raise Constraint_Error or Program_Error if you try to use an Element function on the "empty" element. In the case of GNATMAKE GPL 2008 (20080521), which is what I'm currently using, the empty elements are treated as having any normal value of the element type.
With that out of the way, lets move on and see how Insert_Space behaves in actual code. First add this to the declarative part of the Quotes program:
Now we can also use the cursor edition of Insert_Space. Next we add this to the body of the program:
And here's the resulting output:
The first thing we notice, is that the vector is 18 positions long after the various calls to Insert_Space. This matches perfectly with the three Count parameters: 2 + 3 + 3 = 8 plus the original length of 10. Then we notice that in some places Insert_Space has inserted what appear to be empty elements (positions 14-16), whereas in other places, copies of already existing elements have been inserted (positions 0-1 and 8-9). This shows the "unpredictable value" behavior I mentioned at the beginning.
So when you're using Insert_Space, don't think of it as a procedure that does exactly what its name would seem to imply. Think of it as a procedure that simply expands the length of the vector at a specified index. In other words: Insert_Space should primarily be used to quickly expand the vector with Count elements at the Before position and you should never rely on those elements having any real meaning until you've filled them with some valid data. Thus the access we're doing above in the final loop is not a very bright thing to do. We could've experienced a raised exception due to the call to Element, as noted in the reference manual quote.
We can avoid this "issue" by using the Replace_Element procedure to fill the empty elements with some real data:
And the output is:
With those Replace_Element calls in place, we can safely read the entire vector in the final loop, using the Element function, without having to worry about raising any exceptions.

## Reading and querying the vector

Just as important as adding data to a vector, is being able to read the data, so lets have a look at some of the methods available to do just that.

#### Vectors.Element

We've already met the Element function while going over the previous procedures and functions, so what it does and how it works should be apparent by now. That won't stop us, however, from giving it the once-over just like the rest of the Vectors package. Here's the specification for the two Element functions:
We have one Element function where we access the elements using an index, and one where we're using a cursor. Both do the same thing: Return the Element_Type on the given location in the vector. Here's an example using the index:
And the output of this is:
The cursor example requires an addition to the declarative part of the Quotes program:
After that we add this to the body of the Quotes program:
And the output is:
Element is quite straightforward. Whether you use an index or a cursor is a matter of taste. It seems worth noting that most of the cursor related procedures and functions in the Vectors package internally translate the cursor to an index and then use the index procedure/functions to do the actual heavy lifting. Due to this, I usually avoid the cursor forms. I think an index is much easier to work with and relate to. The primary benefit of using the cursor forms is one of flexibility. If you use cursors, your code will work, with very few changes, with the other sequence based containers, such as doubly linked lists.

#### Vectors.Query_Element

Here's the specification for Query_Element:
Query_Element is both similar to and different from the Element function. Where Element returns the Element_Type on the given index, Query_Element hands the Element_Type over to the Process parameter. This parameter accepts a procedure that handles the Element_Type. To show how this work, we'll first need to add a new procedure to the specification of the Quotes program:
All we do in this procedure is output the element and concatenate 42 to the output.
Also in order to use a cursor, we must declare it first:
The next bit of code should be added to the body of the Quotes program. We'll lump both the index and the cursor versions of Query_Element into the same example:
Note the 'Access attribute. This returns a pointer to the Add_42 subprogram, and it is absolutely required for the Process parameter. It is not enough to just write Ada_42, as you can also see from the specification, where it says not null access procedure.
The output from this program is:
Whether to use Element or Query_Element is a matter of performance vs. clutter. Query_Element is faster than Element, but the mess of using a call-back procedure is, perhaps, not worth the cluttering of the source code, unless performance is crucial. The larger the elements are, the more performance will suffer when using Element.

## Alteration - how to "poke" the elements

Changing the data in a vector is a common task. Here are two methods to accomplish this.

#### Vectors.Update_Element

If you need to update elements in a vector, the Update_Element procedures will come in handy. They work in much the same way as Query_Element, i.e. you must create a callback procedure to handle whatever it is you're trying to do to the elements in the vector. Here's the specification for Update_Element:
Notice how these two procedures look almost exactly like the Query_Element procedures. The most crucial difference is the Process parameter, where the parameter mode is now in out, as opposed to in for the Query_Element procedures. Because they are so alike we'll declare the same cursor and the callback procedure from the Query_Element example. We will only change the parameter mode:
And just as with Query_Element, I'll show both the index and the cursor example in the same listing:
The output from this program is:
Update_Element is not the only way to alter the contents of an element in a vector. There are other ways to concatenate "42" to our Linus quotes, and one of those methods is Replace_Element, which we will look at next.

#### Vectors.Replace_Element

Where the previously discussed Update_Element procedure updates an element in situ, Replace_Element assigns a new Element_Type to a given index or position, regardless of the content already there. Here's the specification for Replace_Element:
Using Replace_Element is very similar to the Insert procedures, except you're now working on an index that already contains a value instead of creating a new index. Leave the Q_Cursor declaration from Update_Element and add the following to the body of the Quotes program:
And the output from this program is:
When to use Replace_Element and when to use Update_Element is highly dependent on what it is you're trying to accomplish. Just remember that Update_Element updates the element on the given index in situ, whereas Replace_Element completely replaces the element on the given index, and just as with Element and Query_Element, Update_Element is faster than Replace_Element, but it clutters the source code with a call-back procedure. So as with so many other things, it's a trade-off between clean code and performance, so be sure to test things and choose the best fit for the project.

## Deleting and clearing

It is, of course, possible to delete elements from a vector and clear a vector completely. I bet the attentive reader will already gave guessed the names of these procedures. There are some surprises, however.

#### Vectors.Clear

Here's the specification for Clear:
Clear removes all the elements from a vector, but does not change the capacity of the vector. So if you want to reset a vector, while retaining the vectors capacity, then Clear is the way to go. Try adding this to the body of the Quotes program:
And the output is:
Note that your capacity numbers might differ from the above. The important thing to note is that the capacity is the same, before and after calling Clear. What is changed is the length value for the contents of the vector.
Unfortunately therefore, you cannot trust Clear to actually delete data from the vector. What this means is that if you resize the now "empty" vector you will probably find your data again. To see how that works, add this to the program:
The output is not as expected, certainly not as desired:

#### Vectors.Delete

Here's the specification for Delete:
These two procedures delete elements from the vector. As you can see, there's a version using an index and one using a cursor. I'll show both in the same example, as they are very similar in use.
The Count parameter tells Delete how many elements to remove from the vector. If there are fewer elements than Count, all the elements are removed from the vector. If you try to delete an index that does not exist, a CONSTRAINT_ERROR exception is raised.
We need a cursor for the Delete example, so add this to the declarative part of the Quotes program:
And here's the body code for the Delete example:
And this is the output generated by the above:
If the Count parameter is 0, then nothing is done.

#### Vectors.Delete_First

Here's the specification for Delete_First:
It should be quite obvious what the Delete_First procedure does. It deletes the first Count elements from the vector. To see an example, add this to the body of the Quotes program::
The output from the above program is:
Delete_First completely removes the element(s) from the vector - it doesn't just delete the contents and leave you with one or more empty elements. If you try to delete more elements than the vector contains, a CONSTRAINT_ERROR is raised, after the entire vector has been deleted. If Count is 0, then Delete_First does nothing.

#### Vectors.Delete_Last

Here's the specification for Delete_Last:
The observant reader will notice that this procedure is very similar to the above mentioned Delete_First procedure and because they are so similar, the following will be an almost letter-by-letter copy of the Delete_First text.
The Delete_Last procedure does deletes the last Count elements from the vector. To see an example, add this to the body of the Quotes program::
The output from the above program is:
Delete_Last completely removed the element(s) from the vector. It also doesn't just delete the contents and leave you with one or more empty elements and the constraints on Count are the same as for Delete_First.

## Moving, swapping and reversing

Sometimes it is necessary to move an entire vector to a new vector, to swap values in a vector or perhaps reorder the vector in reverse direction. The following methods will help with that.

#### Vectors.Move

Here's the specification for Move:
Moving data from one vector to another can be done using the Move procedure. Add this to the declarative part of the Quotes program:
And this to the body:
The output is:

#### Vectors.Reverse_Elements

Here's the specification for Reverse_Elements:
The Reverse_Elements procedure is yet another one of those procedures that does exactly what its name says: It reorders the elements in the vector in reverse order. Lets try it:
And the output is:
And that's more or less all there is to say about Reverse_Elements.

#### Vectors.Swap

Here's the specification for Swap:
The two Swap procedures exchange the values at positions I and J with each other, meaning the value at position I is moved to position J and vice versa. It's the same functionality for both the Index_Type procedure and for the Cursor procedure, so I'll show both in the same example. First we must add two new variables to the declarative part of the Quotes program:
And the add this to the body:
The output from this program is:
Note that a Constraint_Error is raised if one of the indexes that is being swapped is out of range.
Using Swap, it is very easy to add a Bubble Sort to the Quotes program. Please note that I'm just using a Bubble Sort as an example - you should never use something like this to sort your data, as it is very slow and inefficient.

## Information about a vector

A fair deal of information about a given vector can be obtained using the following methods.

#### Vectors.Capacity

Capacity returns the current capacity of a vector. It's important to understand, that the capacity is not the same as the size of the vector; instead you should think of it as the size to which a vector can grow before new data structures must be allocated to the vector.
Here's the specification for Capacity:
Lets see how it works. Add this to the body of the Quotes program:
And the output:
The capacity of the Quotes vector is 16, even though we know there are only 10 quotes in it. Even if we clear the Quotes vector, the capacity stays at 16. Lets expand a bit on the Quotes program to see how the vectors package handles capacity. I'll post the full source here:
And the output:
Notice how length and capacity don't necessarily match. The capacity of a vector will always be the same or higher than the length. Capacity seems to expand as a power of 2 when we leave it to the system to adjust it, while the length is a linear count of the data actually added.

#### Vectors.Is_Empty

The specification for Is_Empty looks like this:
With the Is_Empty function we can test if a vector is empty or not. Add this to the Quotes body, to see how it works:
And the output:
Note that empty in this context means a vector with no elements, not a vector with a bunch of empty elements. What the Is_Empty function checks for is the length of the vector. If it's > 0, then Is_Empty returns FALSE and if the length equals 0, then Is_Empty returns TRUE.

#### Vectors.Length

Here's the specification for Length:
We've already encountered the Length function in previous examples, but in case you didn't pay any attention to them, here's the short explanation of what Length does: It returns the length (i.e., the count of elements) of a vector. I bet that was a surprise!
Lets see an example. Add this to the body of the Quotes program:
And the output:
And that concludes our look at Length.

## Iterating the vector

Iterating a vector can be done using simple loops, but why not use the built-in methods instead? These are not as flexible as a customized loop, but if a simple "array-walk" is all we need, then these methods are the way to go.

#### Vectors.Iterate

As easy as it is to iterate a vector using either the index or a cursor, there's an even simpler way: The Iterate procedure.
Here's the specification for Iterate:
Iterate enables you to apply the Process procedure to each element in the vector. As with the Query_Element and Update_Element procedures, we must create a procedure to handle the individual elements. For this example we'll go for a small procedure that reverses each quote and then outputs the resulting gibberish to the screen. Add this to the declarative part of the Quotes program:
And next add this to the body:
The output from this program is:
Very elegant. Using Iterate for simple manipulations of the elements in a vector is exactly what it's made for. For very complex things it might not be the best solution, as you can't pass any parameter to the Process procedure, which naturally limits it somewhat in use.

#### Vectors.Reverse_Iterate

Here's the specification for Reverse_Iterate:
Where Iterate works its way through the vector from first to last, Reverse_Iterate does the opposite. Other than that little difference, the two procedures are exactly the same, so the example I'm going to show on how to use Reverse_Iterate is almost the same as the example for Iterate.
First we add the Reverse_Quote procedure to the declarative part of the Quotes program:
And the output is, as expected:
Reversed quotes in reversed direction. As with Iterate, you cannot use any parameters with the Process procedure.

## First and last positions in the vector

Whether you're using cursors or the index, sometimes you're going to need to identify the beginning and/or the end of a vector. For this specific job, we have 4 methods available. Lets take a look at them.

#### Vectors.First_Index

Here's the specification for First_Index:
The benefits of using First_Index (besides clarity) are not readily apparent unless we create a few vectors with different Index_Type parameters. I'll show the full source here, as the changes required are extensive:
The output from this program is:
Note how the number preceeding all the quotes is set accordingly to the subtype used as Index_Type for the vector. You should always use the First_Index function instead of the actual numeric value, mainly because it makes it possible to change the Index_Type later on, without having to search all your code for references to a hard-coded numeric value. It is much safer using First_Index.

#### Vectors.Last_Index

Here's the specification for Last_Index:
Last_Index is a bit of an oddball, as it returns a value of type Extended_Index, instead of the expected Index_Type. Where the First_Index function returns the first value of the Index_Type (0 for Natural, 1 for Positive and so on), Last_Index returns the No_Index constant if the vector is empty. This No_Index constant is equal to Index_Type'First-1, which of course puts it just outside the range of the Index_Type.
Let's see how it works. Add this to the declarative part of the program:
And this to the body of the program:
And the output is:
As you can see, the Last_Index function returns -1 for the Empty vector. We've instantiated this vector with a Natural as the Index_Type, and as you might know, the Natural type has the range 0 .. Integer'Last, and 0 minus 1 equals -1.
To further prove this point, try adding these two if blocks to the body:
Now the output is:
In his excellent book Programming in Ada 2005 (ISBN 0321340787), John Barnes has this to say about it:
I don't know if I'd call Extended_Index irritating, but it is an oddity that you should be aware of and understand.

#### Vectors.First

Here's the specification for First:
The First function returns a cursor pointing to the first element in a vector. As with First_Index, the primary reason for using First is to avoid having to hard-code the first value of the Index_Type in your program.
Add this to the declarative part of the Quotes program:
And this to the body:
The output is:
You should always use either First_Index or First to identify the first position in a vector. You should never ever hard-code the numerical value of the first position, as such a method risks failing if you change the Index_Type for the vector.

#### Vectors.Last

Here's the specification for Last:
Last is much more straightforward than its sibling Last_Index, simply because it doesn't have to handle the "irritating" Extended_Index type. The Last function simply returns a cursor pointing at the last element in a vector, or in case of an empty vector, it returns No_Element. Lets see how it works.
Add this to the declarative part of the Quotes program:
And this to the body:
The output from this program is:
As you can see, when a vector is empty, the value of the cursor is set to No_Element. If you try to "read" a No_Element cursor, a CONSTRAINT_ERROR is raised.

## Next and previous with a cursor

We've already seen how to iterate a vector using Iterate, Reverse_Iterate and regular loops using the index, so I think it's about time we learn how to do it with cursors. For this we have the Next and Previous methods available.

#### Vectors.Next

Here's the specification for Next:
Depending on taste and preference, you can choose between using the Next function or the Next procedure. They both do the same thing: Move the cursor to the next element in the vector. I will show both versions in the following example.
First add this to the declarative part of the program:
And the this to the body:
And the output is:
Hopefully further explaining is unnecessary.

#### Vectors.Previous

Here's the specification for Previous:
Where the Next methods move the cursor forward, the Previous methods move the cursor backwards. I'm confident this is well understood by all, so without further ado, lets see how Previous works in actual code.
Add this to the declarative part:
And this to the body:
And the output:
There should be no surprises here.

## Finding things

Finding out if a specific element is present in a vector or if a given position contains a value are fairly common tasks when working with vectors, hence a handful of valuable tools have been made available to us.

#### Vectors.Find_Index

Here's the specification for Find_Index:
The Find_Index function is actually just a wrapper around a simple loop, where each element in the vector is compared to Item. There's nothing "magical" about it. It wont be any faster than a homegrown loop, but it will save you a few lines of code. Find_Index searches the vector from Index and forward. Index defaults to Index_Type'First, in this case 0.
To see how it works, we must first add a few variables to the declarative part of our program:
And then add this to the body:
And finally the output:
There are three interesting things going on in this example:
* Pos is of type Extended_Index. This is necessary to cope with the No_Index value.
* Using the Index parameter of Find_Index enables us to start the search at a specified index.
* The No_Index value is returned if no matching element is found.
Besides those three things, there's really not a whole lot more to say about Find_Index.

#### Vectors.Find

On this example, you will find the Find_Index example from above, modified to work with Find. These two functions are so alike in usage and functionality, that it would be a complete waste of space to do any further explaining. So all you get is the specification and the example.
Here's the specification for Find:

#### Vectors.Reverse_Find_Index

Reverse_Find_Index is similar to Find_Index, except it searches from Index and backwards until it reaches the beginning of the vector or finds a match. On the Example Source page you will find the Find_Index example modified to reflect this.
The specification for Reverse_Find_Index is astonishingly similar to Find_Index:

#### Vectors.Reverse_Find

Reverse_Find is similar to Find, except it searches backwards from Position until it reaches the beginning of the vector or finds a match. On the Example Source page you will find the Find example modified to reflect this.
The specification for Reverse_Find is astonishingly similar to Find:

#### Vectors.Contains

Here's the specification for Contains:
With Contains you basically get Find_Index with a Boolean return value instead of an Extended_Index, and actually the Contains function is just a thin wrapper around the Find_Index function. If you're working with very large vectors and you know that the element you're looking for is situated near the end of the vector, from a performance point of view, you're probably better off using a homegrown solution. I'll show you one such later.
But first lets try our hand at a little Contains example. Add this to the declaration:
And the body:
And finally the output:
As stated earlier, Contains searches the vector from start to end, but that might not be the best solution. Sometimes you know a piece of data is placed near the end of the vector, if it's there at all. In such a case, a Contains function that searches in reverse is what we're looking for. One such would be easy to do:
. With Reverse_ editions available for the two Find functions, I find it odd that there isn't a Reverse_Contains function available. Luckily, as the preceding source shows, it is very easy to add it yourself.

#### Vectors.Has_Element

Here's the specification for Has_Element:
Has_Element enables you to check if a cursor identifies an element, or as the ARM say:
So Has_Element enables you to check if a cursor is still pointing at a valid element in a vector. Remember that a cursor designates both a container and a position in the container. This means that a cursor can point to a position in a vector that no longer exists. Lets see how it's used. Add this to the declaration:
And now add this to the body:
And the output is:
I've honestly never had any use for Has_Element while working with vectors, but that's probably because I hardly ever use cursors with vectors. With Ada.Containers.Doubly_Linked_Lists it is a different story though, as the only means of navigating a doubly linked list is by using cursors.

## From index to cursor, and vice versa

Sometimes it's necessary to convert an index to a cursor or vice-versa. This is done using the aptly named To_Cursor and To_Index functions.

#### Vectors.To_Cursor

Here's the specification for To_Cursor:
I will show you how the above function works, even though it should be painfully obvious. First add this to the declarative part of the program:
And this to the body:
The resulting output from this program is:
The important thing to take away from this example, is the No_Element part. If the given Index is out of range, then No_Element is returned.

#### Vectors.To_Index

Here's the specification for To_Index:
Above we took a quick look at how to convert Index_Type to a cursor; now we'll do the opposite: Going from a cursor to Index_Type. This is done using the To_Index function.
You can keep the declarative part from the To_Cursor example, but replace the body with the following:
This is not exactly rocket science, but it is worth noting that No_Element is "transformed" into No_Index when To_Index is called on a cursor which point at No_Element.

## Vectors.Generic_Sorting

The vector package also contains facilities to sort the vector, in the form of the generic package Generic_Sorting, so there's no need to mess around with home-grown sorting routines. The sorting is done in ascending order. The specification for Generic_Sorting looks like this:
In order to use these procedures, we will first need to instantiate the Generic_Sorting package. This is done the obvious way:
Simply add the above to the declarative part of the Quotes program, and you're good to go. In the following we will look at how to use the generic procedures to sort a vector.

#### Generic_Sorting.Sort

Here's the specification for Sort:
Other than instantiating the Generic_Sorting generic (see Vectors.Generic_Sorting), we needn't specify anything else in the declarative part of the Quotes program. Instead we'll jump straight to the body, where we add these few lines to see how sorting works:
The output from the program is:
And there you have it: A perfectly sorted quotes.txt file. It just doesn't get any simpler than that.

#### Generic_Sorting.Is_Sorted

Here's the specification for Is_Sorted:
Figuring out if a vector has or has not been sorted is done using this little gem of a function. It will return Boolean TRUE if sorting has been done, and Boolean FALSE if not. This is how it works:
The output from the program is:
I'm certain the above result came as no surprise to you.
But what happens if a vector is sorted and then a new element is added? Will Is_Sorted still return Boolean TRUE? Add the following code to the above, and lets see:
Now the output looks like this:
As could be expected, the Is_Sorted function will return Boolean FALSE if changes are made to the vector, but this is not actually done using an internal "flag" of some sorts. No, the FALSE value is found by simply iterating over the entire vector while checking if position i + 1 is lesser than position i. If this is the case, then Is_Sorted return Boolean FALSE.
In case of large vectors, this is not very efficient. Given that, you should of course only use Is_Sorted if it's absolutely necessary.

#### Generic_Sorting.Merge

Here's the specification for Merge:
Merge is special. It merges Target with Source and leaves Source empty. What is special is that it doesn't sort the resulting vector, which one might expect, Merge being a part of the Generic_Sorting. If you want Merge to result in a sorted vector, then you must have sorted both Target and Source prior to the Merge call.
Lets see how it works. Add this to the declarative part of the Quotes program:
And this to the body:
The output from this program is:
Note how the contents of the two vectors are merged into the Target vector and also that the Quotes vector is empty after the merge. Lets delete the A_Sorter.Sort (Container => Foo_Bar); line and see what happens:
As you can see, the Target vector is no longer sorted.
When using Merge it is very important to remember this: Both the Target and Source vectors must be sorted prior to the Merge call, else you'll end up with an un-sorted vector as the result. (If that is all you want, then it's probably faster to use one of the following procedures: Append, Insert, Prepend. Or you could simply use the & function to get the job done.)

## Concatenate using the & operator

As seen earlier, it is possible to join two vectors/vector elements using a number of different techniques, such as append, prepend, insert and merge, but there's also the "&" operator. The specification for the "&" functions look like this:
You can concatenate one vector to another, an element to a vector, a vector to an element and finally two elements. All 4 functions return a vector.
Lets see how it works. Add this to the declarative part of the program:
And this to the body:
And the output is:
While the "&" operator works exactly as expected, there is one caveat, which John Barnes explains in his Ada 2005 book:
Lets try a small example, where we compare append and the & operator. First I'll show the "&" example in full:
Timing this on my computer, yields these results (average of 10 runs):
Now with append and prepend:
Timing this yields these results (average of 10 runs):
If you care about performance, it is probably best to avoid using the "&" functions. As the simple benchmark shows, there are faster ways to concatenate vectors and/or vector elements than using "&".
The & operator Example Source

## Equality

Checking for equality between two vectors is done using the "=" function. Two vectors are only considered equal if both vectors are equal in length and the elements of each index are completely the same.
To see how it works, we must add this to the declarative part of the program:
And this to the body:
And the result is:
This is of course as expected.
Equality Example Source

## See also
### Wikibook

* Ada Programming
* Ada Programming/Libraries
* Ada Programming/Libraries/Ada
* Ada Programming/Libraries/Ada.Containers

### External examples

* Search for examples of Ada.Containers.Vectors in: Rosetta Code, GitHub (gists), any Alire crate or this Wikibook.
* Search for posts related to Ada.Containers.Vectors in: Stack Overflow, comp.lang.ada or any Ada related page.

### Ada 2005 Rationale

* 8.2 Lists and vectors

### Ada Reference Manual
#### Ada 2005

* A.18.2: The Package Containers.Vectors

#### Ada 2012

* A.18.2: The Generic Package Containers.Vectors

### Open-Source Implementations

FSF GNAT
* Specification: a-convec.ads
* Body: a-convec.adb
drake
* Specification: containers/a-convec.ads
* Body: containers/a-convec.adb