Either I'm doing this wrong or i'm not understanding how this method works.
ArrayList<String> a = new ArrayList<String>();
a.ensureCapacity(200);
a.add(190,"test");
System.out.println(a.get(190).toString());
I would have thought that ensureCapacity would let me insert a record with an index up to that value. Is there a different way to do this?
I get an IndexOutOfBounds error on the third line.
No, ensureCapacity doesn't change the logical size of an ArrayList - it changes the capacity, which is the size the list can reach before it next needs to copy values.
You need to be very aware of the difference between a logical size (i.e. all the values in the range [0, size) are accessible, and adding a new element will add it at index size) and the capacity which is more of an implementation detail really - it's the size of the backing array used for storage.
Calling ensureCapacity should only ever make any difference in terms of performance (by avoiding excessive copying) - it doesn't affect the logical model of what's in the list, if you see what I mean.
EDIT: It sounds like you want a sort of ensureSize() method, which might look something like this:
public static void ensureSize(ArrayList<?> list, int size) {
// Prevent excessive copying while we're adding
list.ensureCapacity(size);
while (list.size() < size) {
list.add(null);
}
}
So as others have mentioned ensureCapacity isn't for that.
It looks like you want to start out with an ArrayList of 200 nulls? Then this would be the simplest way to do it:
ArrayList<String> a = new ArrayList<String>(Arrays.asList( new String[200] ));
Then if you want to replace element 190 with "test" do:
a.set(190, "test");
This is different from
a.add(190, "test");
which will add "test" in index 190 and shift the other 9 elements up, resulting in a list of size 201.
If you know you are always going to have 200 elements it might be better to just use an array.
Ensuring capacity isn't adding items to the list. You can only get element 190 or add at element 190 if you've added 191 elements already. "Capacity" is just the number of objects the ArrayList can hold before it needs to resize its internal data structure (an array). If ArrayList had a getCapacity(), then doing this:
ArrayList<String> a = new ArrayList<String>();
a.ensureCapacity(200);
System.out.println(a.size());
System.out.println(a.getCapacity());
would print out 0 and some number greater than or equal to 200, respectively
ArrayList maintains its capacity (the size of the internal array) separately from its size (the number of elements added), and the 'set' method depends on the index already having been assigned to an element. There isn't a way to set the size. If you need this, you can add dummy elements with a loop:
for (int i = 200; --i >= 0;) a.add(null);
Once again JavaDoc to clarify the situation:
Throws: IndexOutOfBoundsException
- if index is out of range (index < 0 || index > size()).
Note that size() returns the number of elements currently held by the List.
ensureCapacity just makes sure that the underlying array's capacity is greater than or equal to the argument. It doesn't change the size of the ArrayList. It does't make any changes visible through the API, so you won't notice a difference except that it will probably be longer before the ArrayList resizes it's internal array.
Adding 190 null entries to an ArrayList reeks of a misuse of the data structure.
Think about using a standard primitive array.
If you require a generics or want more efficient use of space then consider SparseArray or even a Map like a HashMap may be appropriate for your purposes.
public static void fillArrayList(ArrayList<String> arrayList, long size) {
for (int i = 0; i < size + 1; i++) {
arrayList.add(i,"-1");
}
}
public static void main(String[] args) throws Exception {
ArrayList<String> a = new ArrayList<String>(10);
fillArrayList(a, 190);
a.add(190,"test");
System.out.println(a.get(190).toString());
}
Related
Is there a better way to remove dups from the array list compared to the below code which does the work in O(n) when encountered with larger input. Any suggestions would be appreciated. Thank you.
Note :- Can't use any extra space and should be solved in place.
Input :- It will be a sorted array with dups.
Code :-
public int removeDuplicates(ArrayList<Integer> a) {
if(a.size()>1){
for( int i=0;i<a.size()-1;i++ ) {
if(a.get(i).intValue() == a.get(i+1).intValue() ) {
a.remove(i);
i--;
}
}
}
return a.size();
}
Please test the code here at coder pad link.
https://coderpad.io/MXNFGTJC
If this code is for removing elements of an unsorted list, then:
The algorithm is incorrect.
The Question is a duplicate of How do I remove repeated elements from ArrayList? (for example ...)
If the list is sorted, then:
The algorithm is correct.
The algorithm is NOT O(N). It is actually O(ND) on average where N is the list length and D is the number of duplicates.
Why? Because ArrayList::remove(int) is an on average O(N) operation!
There are two efficient ways to remove a large number of elements from a list:
Create a new list, iterate the old list and add the elements that you want to retain to the new list. Then either discard the old list or clear it and copy the new list to the old one.
This works efficiently (O(N)) for all standard kinds of list.
Perform a sliding window removal. The algorithm with arrays is like this:
int i = 0;
for (int j = 0; j < array.length; j++) {
if (should remove array[j]) {
// do nothing
} else {
array[i++] = array[j];
}
}
// trim array to length i, or assign nulls or something.
As you can see, this performs one pass through the array, and is O(N). It also avoids allocating any temporary space.
You can implement the sliding window removal using ArrayList::get(int) and ArrayList::set(int, <E>) ... followed by repeated removal of the last element to trim the list.
Here are some ideas to improve performance:
Removing elements one by one from an ArrayList can be expensive since you must shift the all contents after that element. Instead of ArrayList you might consider a different list implementation which allows O(1) removal. Alternatively, if you must use ArrayList and are not allowed any temporary data structures, you can rebuild the array by chaining together recursive calls that use set() instead of remove().
For lists with millions of elements, consider a parallel processing solution to leverage the power of multiple processes. Java streams are a simple way to achieve this.
List<Integer> l = new ArrayList<Integer>();
//add some elements to l
System.out.println(l.stream().distinct().collect(Collectors.toList()));
While working on ArrayList, I found after setting the initial size of array using the constructor with initialCapacity, then use set() will throw an exception although the array is created, but size isn't set correctly.
Using ensureCapacity() won't work either because it is based on the elementData array instead of size.
There are other side effects because of the static DEFAULT_CAPACITY with ensureCapacity().
The only way to make this work is to use add() as many time as required after using the constructor.
Please check the code below.
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List test = new ArrayList(10);
test.set(5, "test");
System.out.println(test.size());
}
I am not sure why java is throwing this exception.
Behaviour I expected: test.size() should return 10 and set(5, ...) should work.
ACTUAL: throws an Exception IndexOutOfBoundsException.
So is it set method that is causing problem ?
test.set(5, "test"); is the statement that throws this exception, since your ArrayList is empty (size() would return 0 if you got to that statement), and you can't set the i'th element if it doesn't already contain a value. You must add at least 6 elements to your ArrayList in order for test.set(5, "test"); to be valid.
new ArrayList(10) doesn't create an ArrayList whose size is 10. It creates an empty ArrayList whose initial capacity is 10.
The exception is not thrown by test.size() but by test.set(5, "test");. This is because you are setting an element at index 5 but the list is currently empty.
List test = new ArrayList(10);
does not create a list initialized with 10 null elements. It creates a list that has an initial capacity of 10 elements, that is the backing array has a size of 10, but the list itself is still empty.
If you want to initialize a list with 10 null elements, you can use
List<String> list = new ArrayList<>(Collections.nCopies(10, null));
As a side note, you should never use raw types like List but always prefer a parameterized list.
List test = new ArrayList(10); creates empty list which would have capacity of 10 elements.It is not containing element at 6th position so you will not be able to set element at this position.
List test = new ArrayList(10); creates empty ArrayList of raw types (Object) with initial capacity(capacity of list initially, initialize internal array with length 10) not to be confused with size(number of elements list contains).
To understand more first note that ArrayList is nothing but manipulation of array to dynamically change size of the array which we were not able to do with array directly. Ultimately, it's a smart array.
By setting initial capacity we are telling ArrayList to create size of array 10 initially (by default initial capacity is 10).
This is what constructor is doing,
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];//creates initial array
}
You can increase or decrease it accordingly, say you are sure that you are going to need list with 20 elements it'll be better to set initial capacity at start. It avoids resizing of array if you are sure about minimum number of elements you will definitely have in list.
test.set(5, "test"); method replaces element at specific position here it will going to replace element at index 5 with test. But our list is empty, it does not have any element.
Documentation of this method says,
IndexOutOfBoundsException - if the index is out of range (index < 0 ||
index >= size())
Note that size of list is 0 so index (5) is greater than size(0).
Replaces the element at the specified position in this list with the specified element.
set
Parameters
index
int: index of the element to replace
element
E: element to be stored at the specified position
Returns
E
the element previously at the specified position
Throws
IndexOutOfBoundsException
The thing is that your list "Test" is empty and when you are trying to
execute this statement test.set(5, "test"); it won't be able to set that
value in the list & will show Out of bounds exception as it will not create 10 empty spaces.
So, you can initialize your list before:
for(int i=0;i<10;i++) // As you took ten in your code
{
test.add(null); // some people also use test.add(0) instead of null ,
//but '0' also contains a value & considering your code please use null
}
I get exception Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 for the below code. But couldn't understand why.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
//Set index deliberately as 1 (not zero)
s.add(1,"Elephant");
System.out.println(s.size());
}
}
Update
I can make it work, but I am trying to understand the concepts, so I changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
ArrayList index starts from 0(Zero)
Your array list size is 0, and you are adding String element at 1st index. Without adding element at 0th index you can't add next index positions. Which is wrong.
So, Simply make it as
s.add("Elephant");
Or you can
s.add(0,"Elephant");
You must add elements to ArrayList serially, starting from 0, 1 and so on.
If you need to add elements to specific position you can do the following -
String[] strings = new String[5];
strings[1] = "Elephant";
List<String> s = Arrays.asList(strings);
System.out.println(s);
This will produce the sollowing output
[null, Elephant, null, null, null]
Your ArrayList is empty. With this line:
s.add(1,"Elephant");
You are trying to add "Elephant" at index 1 of the ArrayList (second position), which doesn't exist, so it throws a IndexOutOfBoundsException.
Use
s.add("Elephant");
instead.
ArrayList is not self-expandable. To add an item at index 1, you should have element #0.
If you REALLY want "Elephant" at index 1, then you can add another (e.g. null) entry at index 0.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
s.add(null);
s.add("Elephant");
System.out.println(s.size());
}
}
Or change the calls to .add to specify null at index 0 and elephant at index 1.
add(int index, E element) API says, Your array list has zero size, and you are adding an element to 1st index
Throws:
IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())
Use boolean add(E e) instead.
UPDATE based on the question update
I can make it work, but I am trying to understand the concepts, so I
changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
When you call new ArrayList<Integer>(10), you are setting the list's initial capacity to 10, not its size. In other words, when constructed in this manner, the array list starts its life empty.
For Android:
If you need to use a list that is going to have a lot of gaps it is better to use SparseArray in terms of memory (an ArrayList would have lots of null entries).
Example of use:
SparseArray<String> list = new SparseArray<>();
list.put(99, "string1");
list.put(23, "string2");
list.put(45, "string3");
Use list.append() if you add sequential keys, such as 1, 2, 3, 5, 7, 11, 13...
Use list.put() if you add non-sequential keys, such as 100, 23, 45, 277, 42...
If your list is going to have more than hundreds of items is better to use HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array.
You can initialize the size (not the capacity) of an ArrayList in this way:
ArrayList<T> list = new ArrayList<T>(Arrays.asList(new T[size]));
in your case:
ArrayList<String> s = new ArrayList<String>(Arrays.asList(new String[10]));
this creates an ArrayList with 10 null elements, so you can add elements in random order within the size (index 0-9).
Don't add index as 1 directly in list
If you want to add value in list add it like this
s.add("Elephant");
By default list size is 0
If you will add any elements in list, size will increased automatically
you cant add directly in list 1st index.
//s.add(0, "Elephant");
By the way, ArrayList<String> s = new ArrayList<>(10);
set the initialCapacity to 10.
Since the capacity of Arraylist is adjustable, it only makes the java knows the approximate capacity and try to avoid the performance loss caused by the capacity expansion.
Have a question.. I have a list of mp3 filenames
to add a new file name use:
musicList.add(new Mp3(id, filename));
now I wanted to create a array containing this lists
private static final int LIST_COUNT = 8;
public static List<List<Mp3>> musicLists = new ArrayList<List<Mp3>>(LIST_COUNT);
private void parseMus(){
musicLists = new ArrayList<List<Mp3>>(LIST_COUNT);
...
//gettin mp3 list id,filename, length
...
musicLists.get(listNr).add(new Mp3(id,filename,length));
...
}
but it gives me errors:
03-15 21:01:36.030: E/AndroidRuntime(3393): java.lang.IndexOutOfBoundsException: Invalid index 4, size is 0
Second quwestion!
now I edited code and have no errors... BUT now what i get is that all list is filled with same lists.. so when i try to see:
for(int i =0; i< Settings.musicLists.size();i++){
if(D)Log.e("visio added","MP3 file muslist nr="+i+"= "+Settings.musicLists.get(i));
}
I get 8 identical rows shown... what I do wrong?
musicLists = new ArrayList<List<Mp3>>(LIST_COUNT);
You seem to think that this line creates a list of 8 lists. However, what it really does is to create an empty list of lists with initial capacity 8. That is, it doesn't actually populate any lists into musicLists.
In order for this you do what you expect it to, you need to initialize musicLists and then use musicLists.add(foo) to add 8 List<Mp3> objects to it.
As VeeArr said you need to fill the list with lists before you can get the lists.
// this creates an empty list of initial capacity 8
musicLists = new ArrayList<List<Mp3>>(8);
// musicList.size() is still 0
// we can add as much items as we want, the list will dynamically grow.
for (int i = 0; i < 8; i++) {
List<Mp3> emptySubList = new ArrayList<Mp3>();
// emptySubList.size() is 0 each as well.
musicList.add(emptySubList);
}
// musicList.size() is 8 now.
// do your stuff...
musicLists.get(listNr).add(new Mp3(id,filename,length));
The initial capacity you can specify for an ArrayList is just a way to improve memory consumption.
ArrayLists can dynamically grow unlike array[]s. They do that by internally keeping an array[] where the data you put in your ArrayList is actually stored. If the size they need gets bigger than the array[] can hold they create a bigger array[] and copy the content to that new version (afaik the size is always doubled).
Thats nice because you don't need to worry about doing that yourself. But copying arrays is expensive. Therefore you can define an initial size to hint your ArrayList that it should expect a certain number of elements. It's usually fine if you don't give an initially capacity unless you really know how much items you need.
import java.util.ArrayList;
public class WTFAMIDOINGWRONG
{
public static void main(String[] args)
{
ArrayList<Integer> intsAR = new ArrayList<Integer>(5);
intsAR.add(3, 1);
}
}
So, I've been fooling around with this for about an hour and I haven't the slightest Idea what I could be doing wrong. No matter what I do, it's convinced the arraylist has no size and everything is therefore out of bounds. If anyone could tell me what I'm doing wrong I'd really appreciate it.
An ArrayList is backed by an array, so when you specify the initial capacity, you are specifying how large of an array to allocate. This is important because it specifies how much memory the ArrayList will occupy sequentially.
However, the size of the ArrayList specifies how many items are actually in the list. Once the list reaches a certain size (relative to the capacity of the backing array), the backing array will be reallocated to take up additional space.
If you wanted to create an ArrayList of 10 items, all with 0, you would do:
List<Integer> list = new ArrayList<Integer>();
for ( int i = 0; i < 10; i++ ) {
list.add(0);
}
Now you could insert an item at position 3 (or somewhere in the middle) if you wanted to.
Because the size of your list is ZERO. Yes, you are actually constructing it by specifying the initialCapacity, but that doesn't mean size. Are you getting my point? You can say that taht will just reserve the space for future.
BTW, size() documentation clearly states that, it is the number of elements in the list. Now, I hope you know what is happening.
You cannot insert into an empty list in position 3 - what would be the first 2 elements then? With empty list only intsAR.add(0, 1); will work