private static ArrayList<String> places(ArrayList<Road> roads) {
ArrayList<String> places = new ArrayList<String>(10); // tried setting minimum capacity
places.ensureCapacity(10); // tried setting minimum capacity
for (int i = 0; i < roads.size(); i++) {
String from = roads.get(i).getFrom();
String to = roads.get(i).getTo();
for (int j = 0; j < places.size(); j++) { // this is where things go wrong, it doesn't iterate because the "j < places.size()" condition isn't met
if ((places.get(i).equals(from))==false) {
places.add(from);
}
if ((places.get(i).equals(to))==false) {
places.add(to);
}
}
}
return places;
}
Don't know why but the places-ArrayList doesn't set an initial capacity which leads to not being able to iterate places when I have to later on (the for-loop which deals with the j-variable).
Minimum capacity is different from size. Setting the capacity is just a hint to the list that it should have at least this much storage to avoid unnecessary array copies, but does not affect the size so even though you have the capacity for n elements the size() can be less, and calling get(n-1) might result in an IndexOutOfBoundsException.
To create a list of size n that is filled with null, try
List<String> myList = new ArrayList<String>(n);
for (int i = 0; i < n; ++i) { myList.add(null); }
The capacity attribute just gives the class a magnitude of how dimension its internal buffer. It does not add any values to the list (btw, which values would have been added?) so the list is still empty.
The only effect of capacity is to allow the class to avoid resizing its buffer (with the associated performance penalty) later in the execution. But it is not a hard limit. You can add or more elements at will as with any other List.
Use foreach :
for(Road road : roads)
{
String from = road.getFrom();
String to = roads.getTo();
for(Places place: places)
{
if(!place.equals(from)) places.add(from);
if(!place.equals(to)) places.add(to);
}
}
ArrayList.ensureCapacity() allocates enough memory to hold up to the given number of entries. It doesn't actually put anything into the ArrayList.
ArrayList.size() counts the number of actual entries, not the current capacity.
Your loop looks at places.size() before you call places.add() to actually put anything into places. When places.size() is called at the start of the loop it correctly returns 0 because at this point you haven't yet put anything into places.
One other point. You have a method called places and within that method a local variable also called places. That is a recipe for confusion. You should use a different name for one of them.
Related
If have a workflow that removes elements of a List by a certain criteria. However certain items are skipped? Why is this happening?
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i);
}
Above code is my loop. I replaced my condition with something simpler. If I run this code my second loop only runs for 67 turns instead of 100.
It is problematic to iterate over a list and remove elements while iterating over it.
If you think about how the computer has to reconcile it, it makes sense...
Here's a thought experiment for you to go through.
If you have a list that is size 10 and you want to remove elements 1, 5, and 9 then you would think maybe the following would work:
List<String> listOfThings = ...some list with 10 things in it...;
list.remove(0);
list.remove(4);
list.remove(8);
However, after the first remove command, the list is only size 9.. Then after the second command, it's size has become 8. At this point, it hardly even makes sense to do list.remove(8) anymore because you're looking at an 8-element list and the largest index is 7.
You can also see now that the 2nd command didn't even remove the element now that you wanted.
If you want to keep this style of "remove as I go" syntax, the more appropriate way is to use Iterators. Here's an SO that talks about it and shows you the syntax you would need (see the question). It's easy to read up on elsewhere too.
How Iterator's remove method actually remove an object
Skipping a value would be the result of your list getting out of sync with your loop index because the list is reduced in size. This causes you to hop over some locations since the reduction in size affects future locations that have not been reached.
So the first thing you could do is simply correct the synchronization by decrementing i when you remove a value from the list. This will keep index at the same spot as the list shifts "left" caused by the removal.
for (int i = 0; i < listWithAge.size(); i++) {
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i--);
}
The other option is to loop thru the list backwards.
for (int i = listWithAge.size()-1; i >= 0; i--) {
if ((listWithAge.get(i) % 3) == 2) {
listWithAge.remove(i);
}
}
This way, no values should be skipped since the removing of the element does affect the loop index's future positions relative to the changing size of the list.
But the best way would be to use an iterator as has already been mentioned by
Atmas
As a side note, I recommend you always use blocks {} even for single statements as I did above in the if block. It will save you some serious debugging time in the future when you decide you need to add additional statements and then wonder why things are no longer working.
And deleting like this from a list is very expensive, especially for large lists. I would suggest that if you don't have duplicate values, you use a Set. Otherwise, instead of deleting matching values, add the non-matching to a second list.
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
List<Integer> itemsToBeDeleted = new ArrayList<>();
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) {
itemsToBeDeleted.add(i);
}
//delete all outside the loop
//deleting inside the loop messes the indexing of the array
listWithAge.removeAll(itemsToBeDeleted);
When a for loop is being controlled by the size of an ArrayList
(...; i < someArrayList.size(); ...)
but each iteration of the loop is adding an element to the list, how does the loop then recognize that the size is changing? I would think that once the code is sent to the compiler, it would see the size once and then do it until it reaches the original size, but that's clearly not the case. As the size increases, so does the amount of times the loop runs. I would think it wouldn't, so clearly I don't understand how the computer reads this. I'm just wondering how the system reads something like this.
Also, is there some way to make a loop only run the amount of times of the original list size (again element added each iteration)? Assuming you don't know the size beforehand so you can't put in some constant number.
At the beginning of each iteration, the middle expression is evaluated. If it is true, the loop continues. If it is false, the loop breaks. So it will re-evaluate the size every iteration.
To make it loop the number of times of the original size of the list, do something like this:
int size = someArrayList.size();
for (i = 0 ; i < size ; i++) { ... }
Or if you want to get fancy:
for (i = 0, size = someArrayList.size() ; i < size ; i++) { ... }
In the implementation of add() method in ArrayList, a member variable which stores the size of the arraylist is updated and size() represents a getter for this member variable , so on each iteration this size is called so you get the updated version of the size.
The .size() is called for each iteration. That is why looping over iterators works, if you just evaluated .hasNext() only once the loop would never end. To only iterate over the list the number of times as the original size you could do something like this:
final int originalSize = list.size();
for (int i = 0; i < originalSize; i++) {
...
}
for(int i = 0; i < bag.length; i++)
{
if(bag[i].equals(a))
{
tmp = bag[i];
bag[i] = bag[bag.length-1];
bag[bag.length-1] = tmp;
numElements--;
break;
}
}
The goal of this is to find an object in the array and then remove it? is it possible??
Changing the length of an array is not possible. Recall that array is a static data structure whose size is determined before hand. Increasing or decreasing is not supported in this data structure. The fact that one has to increase or decrease the size depending on the usecase means that they have picked up the wrong data structure. They should perhaps go with an ArrayList.
Anyway, coming back to your question, you can simulate the 'size decrease' by maintaining a variable which you let track the array index and decrease the size of this variable. This lets you give the impression of shrinking the array.
The code you have provided does the same. Note however, that you should be using this modified index to track the contents of your array.
for(int i = 0; i < bag.length; i++)
{
if(bag[i].equals(a))
{
tmp = bag[i];
bag[i] = bag[bag.length-1];
bag[bag.length-1] = tmp;
numElements--;
break;
}
}
Whenever a particular bag at a given index equals to the item under question i.e., 'a', we swap elements so that the current bag element to be removed moves to the last and also we reduce the size of our new index - numElements by 1 to simulate this.
If you have the full code with you, please consider adding the following snippet at the end of that program to understand this more:
// Simulation of the array shrinking.
for(int i = 0; i < numElements; i++)
{
System.out.println( bag[i] );
}
// Movement of uninteresting elements to the end of the array.
for(int i = 0; i < bag.length; i++)
{
System.out.println( bag[i] );
}
It's not possible to change the length of an array. You can overwrite the element you wish to remove with the last element of the array and then copy the first bag.length - 1 elements of your array to a new array whose length is bag.length - 1.
for(int i = 0; i < bag.length; i++) {
if(bag[i].equals(a)) {
bag[i] = bag[bag.length-1];
bag = Arrays.copyOf (bag, bag.length - 1);
break;
}
}
public static String[] removeElements(String[] input) {
List<String> result = new ArrayList<String>();
String deleteValue = "somevalue";
for(String item : input)
if(!deleteValue .equals(item))
result.add(item);
return result.toArray(input);
}
This is one method you can fit this into your program.
You cannot decrease the size of an array. okay no problem! you can create your own data structure which supports that right?
Now, create a class named say MyArray with functions like increaseLenght(int) and decreseLength(int). Try it if you want to, will be fun for sure..
You cannot reduce the size of an array. Arrays are fixed length. What you can do is have a variable that indicates how many entries of the array you are using. This is what you are doing with numElements. The standard class ArrayList is implemented like this. The data is kept in an array and a private field size is used. With an ArrayList, when you remove an element, all the elements to the right are shifted left. However I also like your idea.
I would suggest 2 changes.
Make the last element null instead. If you are removing the element, why does it still need to be in the array?
Use numElements - 1 rather than bag.length-1 as the array could be bigger.
With these changes it becomes:
for(int i = 0; i < bag.length; i++)
{
if(bag[i].equals(a))
{
bag[i] = bag[numElements-1];
bag[numElements-1] = null;
numElements--;
break;
}
}
I have a Collection of objects I need to iterate over. The collection is of variable size. If the collection has more than 1 object in it, I need to perform special processing on objects 2 .. infinity.
What's the preferred method to do this? For example:
int count = 1;
for (CustomObject co : CustomObjectCollection) {
methodAll(co);
if(count > 1) {
methodSpecial(co);
}
count = count++;
}
What you have will work except for one bug: count = count++ does absolutely nothing. count = count + 1 would work, or count++, but count = count++ is a no-op.
You could also just use a boolean flag if you don't specifically need to track the count.
boolean first = true;
for (CustomObject co : CustomObjectCollection) {
methodAll(co);
if (!first) {
methodSpecial(co);
}
first = false;
}
Which to use
Which to use depends on your specific use case. Assuming that you are not looking to optimise every last op-per-second of performance then go with the option that declares your intent:
count++ is fine if you want to track or use the count outside the loop (as per Louis' answer, it is count++, not count = count++)
for(int i = 0; i < collection.size(); i++) is good as well if the collection supports a get(i) operation. This also lets you skip the first item by initialising int i to a different index. It might be used if you don't want the extra count variable hanging around outside the loop.
the above boolean first = true; (or the inverse boolean notFirst = false;) highlight that you want to treat the first and subsequent elements differently
If you have the List interface on your collection and want to skip a set number of elements, then subList is a good option
Performance
If performance is a concern, then measure it for your platform and implementation, but from general experience, from slowest to fastest, with <=20x difference between the first and last:
for each loop with iterator: slowest
for(int i = 0; i < list.size() ; i++)
declare int size = list.size() then a for(int i = 0; i < size; i++) loop
However these speed results depend on so many things that unless performance is a design goal or an identified issue go with the iterator until you have a reason to use one of the other two - the iterator is generally fast enough.
I think a better way to do this would be to work directly with the iterator like so:
Iterator<CustomObject> it = customObjectCollection.iterator();
if(it.hasNext()) { //first pass don't call methodSpecial
methodAll(it.next());
}
CustomObject customObj;
while(it.hasNext()) { //all the rest 2..infinity
customObj = it.next();
methodAll(customObj);
methodSpecial(customObj);
}
customObj = null; // for garbage collection
This way you don't have to check each iteration if this is the first run or not.
This will work with any Iterable (which is already needed for a foreach loop anyway).
Note: If this is not an ordered collection you might get different elements in the first iteration then you might expect.
Let's say I have a ArrayList<String> data;
I'm adding some data into this Array, using data.add() function.
Let's say I've added 10 Strings into this array. The size of the array is 10 now.
How do i destroy all elements in the array? The data.clear() function just set's all 10 elements to null. So when I try to add another element, it just get's on the 11 position. I want just to start it over from 0;
I can use a for loop to replace all the data, but I really think there is a way just to empty the ArrayList. Is it?
Your assumptions don't seem to be right. After a clear(), the newly added data start from index 0.
If you in any doubt, have a look at JDK source code
ArrayList.clear() source code:
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
You will see that size is set to 0 so you start from 0 position.
Please note that when adding elements to ArrayList, the backend array is extended (i.e. array data is copied to bigger array if needed) in order to be able to add new items. When performing ArrayList.clear() you only remove references to array elements and sets size to 0, however, capacity stays as it was.
ArrayList.clear(From Java Doc):
Removes all of the elements from this list. The list will be empty after this call returns
After data.clear() it will definitely start again from the zero index.
data.removeAll(data); will do the work, I think.
it's not true the clear() function clear the Arraylist and start from index 0
Source code of clear shows the reason why the newly added data gets the first position.
public void clear() {
modCount++;
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
clear() is faster than removeAll() by the way, first one is O(n) while the latter is O(n_2)