How does an ArrayList retrieve data in constant time? [duplicate] - java

This question already has answers here:
Why is accessing any single element in an array done in constant time ( O(1) )?
(5 answers)
Closed 4 years ago.
One interview question which I couldn't answer and couldn't find any relevant answers online.
Suppose in an arraylist, there are 10000 data, and I want to find the number which is currently on 5000th index, how does the arraylist know the indexes and give result in constant time?
Because if we are traversing through the arraylist to find the data, it would take linear time and not constant time.
Thanks in advance.

The storage backing an ArrayList is an array. Whether primitive values or object references are stored, all objects in the array are in consecutive order in memory.
For array access, all the compiler has to do is have instructions that calculate the correct memory address based on the initial address and which index is desired, which is O(1). Then it can go directly to that calculated address. There is no traversing, so it is not O(n).

ArrayLists can be thought of as an array of Objects (Which happens to be exactly how they are implemented). You can index into it as any other array at O(1). The advantage over a true "Array" is that it tracks a "Length" independent of the array's length and automatically extends the array when it "overflows"--plus a few extra operations.
LinkedLists (probably the structure you are thinking of) require you to walk from one item to the next, so the implementation is O(n) to find an item at an index.

ArrayList
ArrayList uses an array under the hood, thus the name. Arrays are data-structures with a direct, fast, index-based access.
So if you ask for the element at index 5 000, it just asks its internal array:
// More or less
return array[5000];
Here's the full method from OpenJDK 8:
/**
* Returns the element at the specified position in this list.
*
* #param index index of the element to return
* #return the element at the specified position in this list
* #throws IndexOutOfBoundsException {#inheritDoc}
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
In particular, it does not traverse all elements up to that point. That's what other data-structures, without index-based access, need to do. Such as LinkedList. Note that there is an indicator interface, called RandomAccess (documentation). Classes implementing that interface have a direct index-based access. Current implementations are:
ArrayList, AttributeList, CopyOnWriteArrayList,
RoleList, RoleUnresolvedList, Stack, Vector
How arrays work
So, how does an array have direct access to that element? Well, arrays are of fixed size. When you create it, you need to tell it the size. For example 10 000:
Foo[] array = new Foo[10000];
Your computer will then allocate contiguous memory for 10 000 objects of Foo. The key is that the memory area is contiguous, not scattered around. So the third element comes directly after the second and directly before the fourth element in your memory.
When you now want to retrieve the element at position 5 000, your computer retrieves the Foo object at the following memory position:
startAddressOfArray + 5000 * sizeOfFoo
Everything is known since declaration of the array and the computation is fast, obviously in constant time O(1). Thus, arrays have direct index-based access to their elements. Because the stuff is stored together, contiguously in memory.
You may read more about arrays on Wikipedia.
Here is an image from techcrashcourse.com showing an array with the addresses of each element:
The array is of size 7 and stores integers that use 2 bytes (16 bits). Usually called short, so a new short[7] array. You can see that each element is offset by 2 bytes (the size of a short) to its previous element. Which makes it possible to access an element at a given position directly with a simple computation, as shown.

As its name suggests, ArrayList stores elements in an array. Here is the relevant piece of code in oracle JDK :
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
Thus, without surprise, list.get(index) only gets the nth element in the internal array :
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}

Related

Delete the ith element in constant time [duplicate]

This question already has answers here:
Ordered list with O(1) random access and removal
(2 answers)
Data structure that allows accessing elements by index and delete them in O(1)
(4 answers)
Closed 4 years ago.
I'm looking for a data structure in Java, that has the following properties:
Deletion in O(1) time using the index inside the structure, while maintaining the relative order of the elements (sorted initially).
Addition, only at the end of the structure.
No updation is required.
Single traversal after all deletions.
Options that I've tried:
Array: Can not delete in O(1) time, as shifting is required. Plus, if I use a HashSet of deleted (or not deleted) elements, then too I would have to go through the deleted elements once, while travelling through the array.
Linked List: Deletion is O(1) (if you have a reference to the Node to be deleted, and it's a doubly linked list, preferably), and shifting is not necessary. But there is no indexing, so I have to traverse from the start, to determine the Node that has to be deleted.
TreeSet: A treeset can maintain the order, and deletion is O(1) time,but via the element, but not the index inside the structure.
I'm looking for a data structure that can help me in the tasks mentioned above, if possible, in Java. If it is not built-in, then I would like to know the implementation of the said data structure.
The need:
I was trying to solve this question. A string of English characters is given initially, then a number of operations are to be performed on the string. Each operation has a character c and a number an alongside, which means that we have to delete nth occurrence of the character c.
My solution:
I would create an array of type X (the data structure I am looking for), of length 26 (for each character). I would add each occurrence of a character, say d, in the 3rd slot (starting from 0), in objects that contain the index in the String itself. I would do this for all the characters of the String. This would take a total time of O(n), if the length of the string is n.
Once this is done, I would start processing the queries. Each query requires us to delete the nth occurrence of the character c (variable, not the actual English character c), which we can do in O(1) time (as required). So, each such deletion would take O(q) time, where q is the number of queries.
Then we can make a charArray that has the length of that of the original string. Then traverse through all the elements remaining in each slot of the array of objects of type X, and put them in their respective places. Once this is done, we can traverse this charArray again and ignore all the empty places, and construct a string from the elements left.

ArrayList: insertion vs insertion at specified element

Consider an Arraylist. Internally it is not full, and the number of elements inserted so far is known. The elements are not sorted.
Choose the operations listed below that are fast regardless of the number of elements contained in the ArrayList. (In other words, takes only several instructions to implement).
Insertion
Insertion at a given index
Getting the data from a specified index
Finding the maximum value in an array of integers (not necessarily sorted)
Deletion at the given index
Replacing an element at a specified index
Searching for a specific element
I chose Insertion at specified index, Getting the data from a specified index, and replacing an element but answer key says Insertion. As I usually understand it, in an ArrayList, the insert operation requires all of the elements to shift left. If we did this at the beginning of the list, we would have $O(n)$ time complexity. However, if we did it at the end, it would be $O(1)$.
My question comes down to: (1) what, if any, difference is there between insertion and insertion at specified index and (2) given this particular time complexity for insertion why is it considered "fast"
First take a look at these two methods defined in java.util.ArrayList
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
Now if you see the first method (just adding element), it just ensures whether there's sufficient capacity and appends element to the last of the list.
So if there's sufficient capacity, then this operation would require O(1) time complexity, otherwise it would require shifting of all elements and ultimately time complexity increases to O(n).
In the second method, when you specify index, all the elements after that index would be shifted and this would definitely take more time then former.
For the first question the answer is this:
Insertion at a specified index i takes O(n), since all the elements following i will have to be shifted to right with one position.
On the other hand, simple insertion as implemented in Java (ArrayList.add()) will only take O(1) because the element is appended to the end of the array, so no shift is required.
For the second question, it is obvious why simple insertion is fast: no extra operation is needed, so time is constant.
ArrayList internally is nothing but an Array itself which uses Array.copyOf to create a new Array with increased size,upon add,but with original content intact.
So about insertion, whether you do a simple add (which will add the data at the end of the array) or on ,say, first(0th) index , it will still be faster then most data structures , keeping in mind the simplicity of the Data Structures.
The only difference is that simple add require no traversal but adding at index require shifting of elements to the left, similarly for delete. That uses System.arrayCopy to copy one array to another with alteration in index and the data.
So ,yeah simple insertion is faster then indexed insertion.
(1) what, if any, difference is there between insertion and insertion at specified index and
An ArrayList stores it's elements consecutively. Adding to the end of the ArrayList does not require the ArrayList to be altered in any way except for adding the new element to the end of itself. Thus, this operation is O(1), taking constant time which is favorable when wanting to perform an action repetitively in a data structure.
Adding an element to an index, however, requires the ArrayList to make room for the element in some way. How is that done? Every element following the inserted element will have to be moved one step to make room for the new insertion. Your index is anything in between the first element and and the nth element (inclusively). This operation thus is O(1) at best and O(n) at worst where n is the size of the array. For large lists, O(n) takes significantly longer time than O(1).
(2) given this particular time complexity for insertion why is it considered "fast"
It is considered fast because it is O(1), or constant time. If the time complexity is truly only one operation, it is as fast as it can possibly be, other small constants are also regarded fast and are often equally notated by O(1), where the "1" does not mean one single operation strictly, but that the amount of operations does not depend on the size of something else, in your example it would be the size of the ArrayList. However, constant time complexity can involve large constants as well, but in general is regarded as the fastest as possible time complexity. To put this into context, an O(1) operations takes roughly 1 * k operations in an ArrayList with 1000 elements, while a O(n) operation takes roughly 1000 * k operations, where k is some constant.
Big-O notation is used as a metric to measure how many operations an action or a whole programs will execute when they are run.
For more information about big O-notation:
What is a plain English explanation of "Big O" notation?

How ArrayList provides random access behaviour?

ArrayList is simply implemented as an Object[]. I know it implements the RandomAccess interface, but it is only a marker interface...
So, my question is: Why/How ArrayList provides the random access feature?
EDIT 1: perhaps I should make this clearer...what I want to understand is why it is constant time to access the element while it is an Object[]?
By comparing a LinkedList, an ArrayList and an Array visually should makes things easy:
Linked list:
+----+ +----+ +----+ +----+
|Head| ---> | e1 | ---> | e2 | ---> | e3 | ---> null
+----+ +----+ +----+ +----+
Now, let say I want to get element e2, however the linkedlist itself holds the reference of the headNode. To get to e2, I have to traverse all the way to e2 from the HeadNode. Clearly, this does not provides constant time operation as you can't access any of the elements directly without traversing through the list.
Array:
+----++----++----++----+
| e1 || e2 || e3 || e4 | (value)
+----++----++----++----+
| 01 || 02 || 03 || 04 | (address)
+----++----++----++----+
Imagine this, when you have a variable holding an array, only the address of the first element (e1) is held in the variable. The following array elements will be stored in the next available memory block. The array elements sit next to each other in a sequential sequence in memory. This makes it a constant time operation when you need to access a specific element. For example, when you want to access e3 and each memory block is 4 bytes. From the first element, move 2 blocks of memory (8 bytes) from the array reference. The key to constant time operation is: No traversing needed. It just has to calculate how many bytes to shift from current location according to size of each block and number of blocks to move (indicated by array index). In Java, when you try to shift beyond the bounds of the allocated memory for the array, it gives you an ArrayIndexOutOfBoundsException.
ArrayList:
Arraylist uses the same idea of an array. It will allocate a size of 10 initially. When it needs to grow (more elements added for instance), it creates a new array with added length for storage. Since the storage of the data is by array, the operation time will be same as array (i.e. constant time).
Elements of an ArrayList can be accessed randomly, i.e. you are free to choose an index at any time to get an element that is in the list:
myList.get(3);
myList.get(1);
myList.get(5);
The ArrayList's method to get() an element is implemented as:
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
#SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
To access a single element in an array by index, you need constant time. No matter how many elements are in the (array-based) list, you can always get your entry in the same time. It does not matter whether the element is at the beginning, in the middle or at the end of your list.
The opposite is sequential access, e.g. used in a LinkedList, where each element contains a reference to the next item in list. You cannot access elements randomly here, but you have to iterate through all prior items to reach your target elements:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
Here your access cost are dependent on the length of the list and the number of items in the list. Elements at the beginning of the list can be accessed faster, since the path to reach them is shorter. To access an element in the middle or at the end of the list is more costly, since you have to traverse all the other elements sequentially.
Why
Because that's one of the reasons you use an ArrayList, because you want constant-time access to the elements. So ArrayList has the marker RandomAccess to tell you that that's what it provides. If you didn't need that, you might use a LinkedList instead, which doesn't provide constant-time access but doesn't have to do the occasional big reallocations ArrayList has to do.
How
By using an array under the covers. Arrays provide constant-time access, so...
As you said, RandomAccess is purely a marker interface. By adding it to a collection class, one indicates (basically) that get(int) is implemented in constant time.
ArrayList does that because it litterarly is one memory access to get a value from a specific position.
An array is by definition a random-access data structure. An array has fixed offsets for each elements. If I wanted to access the n-th element of an array, I could simply access the memory address <base offset> + n * <element size>. There is no iteration required. An ArrayList is a List-implementation that is internally backed by an array such that it inherits this property. Whenever the backing array of an ArrayList cannot fit its elements any more, the ArrayList copies all elements into a new array. This is why a LinkedList can be more efficient when collecting an unknown number of elements. For this advantage, one cannot access an element by computing a fixed index any more.
The RandomAccess marker interface signals random accessability to users of this list to allow for implementing efficient algorithms if random-access is required. A LinkedList, for example, exposes the same interface as an ArrayList. A sorting algorithm that requires random-access but does never change the size of a list would then rather copy the elements of a non-randomly-accessible list into a more efficient data structure while sorting. It can then rather iterate the non-randomly-accessible list in a single pass-through for filling the list with the sorted elements.
In computer science, random access (more precisely and more generally called direct access) is the ability to access an item of data at any given coordinates in a population of addressable. Wikipedia
The ArrayList provide API to access any element using method get(int).
The int is the index of item in Object[].
This method allow you to access item randomly (at will). The opposite is sequential access (LinkedList) that you must move through the items of structure.

I want to add a row onto a 2d array

The method assigned in the assignment says:
boolean addLineSegment(int [] segment) - add a line segment to the database if its coordinates represent a valid line segment. This should increase the size of the lineSegment array by one and add the given line segment to the end. The method returns true if a line segment was added and false otherwise. The input should be an array of size 4.
I'm kind of stuck because I want to add a row into my array lineSegments[][] without having to reallocate it and erasing the previous contents of the array. How do I keep the contents of the array and add a new row to it so I can add the contents of segment[] to lineSegments[][]?
Use Java ArrayUtils static methods, there are many function that may help you there, like:
Add functions:
static int[] add(int[] array, int element)
Copies the given array and adds the given element at the end of the new array.
static int[] add(int[] array, int index, int element)
Inserts the specified element at the specified position in the array.
Remove functions:
static int[] remove(int[] array, int index)
Removes the element at the specified position from the specified array.
It looks like you're trying to simulate the action of an ArrayList! I'd recommend using an ArrayList to manage your list of arrays. If, however, you're only allowed to use an array, I'm afraid that unless you know how many maximum elements you're going to have in your outer array, you'll need to copy the way the ArrayList class works(with a few changes), which does indeed involve reallocating the array.
However, have no fear because you can indeed reallocate the array without losing the contents of it. In the Arrays class, there's a static method called copyOf(). This allows you to make a new array of the size you want while retaining the contents of your old array.
Let's have an example:
boolean addLineSegment(int[] segment){
if(segment is not valid)
return false;
lineSegments=Arrays.copyOf(lineSegments,lineSegments.length+1);
lineSegments[lineSegments.length-1]=segment;
return true;
}
This fulfills the requirement of increasing the size of the array by one while still retaining the old elements. For this to work, the array must start out with a size of zero, and it will then grow from then on.
This differs from the way the ArrayList class works in that while this one increases by one every time, the ArrayList class keeps track of the current index of the last element, and starts with an array of length 10, doubling every time the cap is reached. However, your requirements state that the size must increase by 1 each time so the solution I proposed should work fine.

Adding a value to the middle of an array without replacing an existing value in java

I am researching the difference of arrays and Arraylists. Can anyone clarify if it is possible to add an element to the middle of an array without replacing the existing value (like what the x.add() would to for an arraylist)?
For example: if I had an array of fruit [apples, pears, peaches, nectarines] and I want to insert plums [apples, pears, plums, peaches, nectarines]. Would this be possible and how would it be done?
An array size is fixed, so normally it's complicated to add an element into an array.
But in special cases it is possible:
It is only possible if the array is not yet fully filled.
Like [apples, pears, peaches, nectarines, NULL, NULL]
inserting is then possible if the current size, which you have to record on a separate place, is smaller than the array size.
Inserting then works using System.arraycopy(), where you first move all elements at inserting position and above to one position to the right. Then you add the new element.
The result would be [apples, pears, plums, peaches, nectarines, NULL]
leaving yet place for one more element.
Java's ArrayList uses this technic to provide a dynamic growing array.
In most cases it's better to use ArrayList for this task. In special cases where you have to read millions of elements such a self managed "growing" array is much more memory efficient (e.g 4 times less the memory, because ArrayList always uses Objects while array could also use primitive types, and they need less memory).
Note:
If one reads you question puristic the answer is "No", because you showed a fully filled array. The only chance to add any more element is to allocate a new array with bigger size and copy the old elements and the new one. But then you have a new array. The access to that array must be encapsulated that no one can reference it, except the class which manages the adding and getting values from it. Just look at the source code of ArrayList to get an Idea.
No, it's not possible.
You would need to allocate a new array larger than the existing one and copy everything over to it in the new positions.
No. Arrays have fixed length, and cannot be resized.
An array is a continuous region of memory - so no, you cannot add an element in the middle of an existing array. ArrayList is a List backed by an array. When necessary, ArrayList will allocate a new array and copy existing values into the newly allocate array.
392 public void add(int index, E element) {
393 rangeCheckForAdd(index);
394
395 ensureCapacity(size+1); // Increments modCount!!
396 System.arraycopy(elementData, index, elementData, index + 1,
397 size - index);
398 elementData[index] = element;
399 size++;
400 }
Read the code for ArrayList here.
const array = [1,2,3];
array.splice(2,0,5); \\ (index, number of items you want to replace, item to be replaced with separated by commas)
console.log(array); \\ [1,5,3]
array2 = [1,2,3];
array2.splice(1,0,4,5);
console.log(array2); \\ [1, 4, 5, 2, 3]
Hope this helps the JS Devs

Categories

Resources