I don't understand why this is happening. I was doing a bit of research on other questions and I found out that you can't modify an collection while using a for loop. However, I am using an Iterator, why is it not working?
int counter = 0;
int otherCounter = 0;
ArrayList<Character> chars = new ArrayList<Character>();
Iterator<Character> i = chars.iterator();
for (char s : e.getMessage().toCharArray()) {
chars.add(s);
}
while (i.hasNext()) {
char s = i.next();
if (chars.get(otherCounter + 1) == s) {
counter++;
} else {
counter = 0;
}
if (counter >= 2) {
i.remove();
}
otherCounter++;
}
I am getting an error on this line for some reason:
char s = i.next();
You're adding to the collection after creating the iterator.
This throws that exception.
You need to create the iterator after you finish modifying the collection.
This is because an "enhanced for loop" as you are using it creates an Iterator behind the scenes.
In fact, when you write:
for (X x: whatever) {
// do something with x
}
the "real", generated code goes something like:
Iterator<X> iterator = whatever.iterator();
while (iterator.hasNext()) {
X x = iterator.next();
// do something with x
}
And this is true if whatever implements Iterable<X> (which List does; in fact, any Collection does)
In your example, you create a List<Character>, create a first Iterator<Character> over it, and your for loop creates another Iterator<Character>, over the same list.
Note that you created the first before you modified your List... Hence the result. The first Iterator, which you reuse only afterwards, will detect that in the meanwhile, its underlying list has been modified: boom.
Related
I have a list of custom objects. I need to get/remove a specific object from that list but the equals implemented would not work based on what I need to search.
The following would work:
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject = list.remove(index);
// use CustomObject here
I was wondering if I could do the list.remove inside the for loop despite not using an iterator since the loop breaks immediately
Using the delete(int) method in your loop will work just fine.
Your loop is closed so you have full control on i and you can use the list as you please. You don't use i after having deleted the first element that matches, so there are no caveat. If you were to reuse it, you would have to not increment it.
To avoid any trouble, the following if both more readable and expressive. Also, it's totally implementation-agnostic.
CustomObject deletedObject = null;
for (Iterator<CustomObject> i = list.iterator(); i.hasNext(); ) {
CustomObject candidate = i.next();
if (candidate.getAttr().equals(arg)) {
deletedObject = candidate;
i.remove();
break;
}
}
if (deletedObject != null) {
// Do something with deletedObject
}
There is no special program state associated with “being inside a for loop”. What matters, are the actions your program performs.
So
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject o = list.remove(index);
// use CustomObject here
is identical to
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i);
// use CustomObject here
break;
}
}
as it performs the same actions (letting aside that the first variant will throw when no match has been found). The differences regarding local variables defined in these code snippets are, well, local and do not affect anything outside the containing method.
That said, the rule that you must not modify a collection (except through the iterator) while iterating over it, applies to iterator-based loops, where you are not in control of the iterator’s internal state. When you are using an index based loop and fully understand the implications of removing an object at a particular index (of a random access list), you can even continue iterating. The important aspects, to do it correctly, are that the indices of all subsequent elements decrease by one when removing an element, further the size decreases so you must either, reread the size or decrement a previously cached size value.
E.g., the following loop is valid
for(int i = 0; i < list.size(); i++) {// rereads size on each iteration
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i--); // decrease index after removal
// use CustomObject here
// continue
}
}
But, of course, it’s more idiomatic to use an Iterator or removeIf, as these approaches are not only easier to handle, they also work with other collections than random access lists. And especially removeIf may be more efficient when you remove more than one element.
Just another way using streams,
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("D");
str1.add("D");
Optional<Object> foundVal = str1.stream().filter(s ->
s.contains("D")).findFirst().map(val -> {
str1.remove(val);
return val;
});
System.out.println(str1);
System.out.print(" " + foundVal.get());
Output
[A, B, D] D
My problem is, when I output this code, it's not outputting what I want which is to remove the "all". It outputs the same exact thing the first print statement did.
Here's my code:
// RemoveAll
// Spec: To remove the "all"
// ArrayList remove() exercise
import java.util.ArrayList;
public class RemoveAll
{
public static void main(String args[])
{
ArrayList<String> ray;
ray = new ArrayList<String>();
int spot = ray.size() - 1;
ray.add("all");
ray.add("all");
ray.add("fun");
ray.add("dog");
ray.add("bat");
ray.add("cat");
ray.add("all");
ray.add("dog");
ray.add("all");
ray.add("all");
System.out.println(ray);
System.out.println(ray.size());
// add in a loop to remove all occurrences of all
while (spot >= 0)
{
if (ray.get(spot).equalsIgnoreCase("all"))
{
ray.remove(spot);
}
spot = spot - 1;
}
System.out.println("\n" + ray);
System.out.println(ray.size());
}
}
Any ideas?
you are determining size() before filling list
put this after once you have list filled (i.e. after all add())
int spot = ray.size() - 1;
Another way to remove items from the list is to use an Iterator:
for(Iterator<String> i = ray.iterator(); i.hasNext(); ) {
if(i.next().equalsIgnoreCase("all")) {
i.remove();
}
}
That way you don't have to keep track of where you are in the list with respect to removed items.
Two problems. You are setting the size of spot before the array has any values in it so it will have a value of -1 when you get to
while (spot >= 0)
also you are mutating (modifying) the array while you are iterating over it which will cause all sorts of errors. The way you want to do this is using an iterator
Iterator iter = ray.iterator();
while(iter.hasNext()){
String cur = iter.next();
//logic to determin if you need to remove
iter.remove();
}
I want to iterate over unique pairs in a HashSet, Ideally, I'd be able to make a copy of the iterator and when it has exhausted itself, iterate the iterator I am making copies of, but Iterators do not like being copied, and I suspect that there is a good reason for this, but I do not know what that is.
More specifically, what prevents this (below) behavior from being supported?
Iterator<Object> iter = myhash.iterator();
while(iter.hasNext()){
object=iter.next();
Iterator<Object> iterclone = iter.clone();
while(iterclone.hasNext()){
setOfObjectPairs.add(object,iterclone.next());
}
}
Edit: The point of doing this is to save the current state of the iterator, which is already pointing at the i-th element.
If you cloned an Iterator and the clone modified the underlying Iterable, the original Iterator will throw a ConcurrentModificationException.
You can simply use another iterator for the same set.
Iterator<Object> iter = h.iterator();
while(iter.hasNext()){
Object o=iter.next();
Iterator<Object> iterclone = h.iterator();
while(iterclone.hasNext()){
//logic
}
}
or you can use and array and iterate over it.
If the order to generate the pairs doesn't matter, then you could use the following code, which avoids having to clone the iterator, while still running in O(n*n) time:
HashSet<Integer> set = new HashSet<Integer>();
for (int i = 0; i < 10; i++) {
set.add(i);
}
int to = 0;
for (int a : set) {
int index = 0;
for (int b : set) {
if (index++ >= to) {
break;
}
System.out.println(a + ", " + b);
}
to++;
}
If you will not change iterator (you're only want to save state)
The best way is to do it here is with for each loop I guess
I expect iterators to be only for cases when I modify collection in loop through this collection. For example remove() method from this iterator.
The best way I see it is:
HashSet<Integer> set = new HashSet<Integer>();
for (int i = 0; i < 10; i++) {
set.add(i);
}
LinkedList<Integer> arr = new LinkedList<Integer>(set);
for (Integer i : set) {
arr.pollFirst();
for (Integer k : arr) {
System.out.println(i + " " + k);
}
}
Here you're coping you're hashset to LinkedList and you're droping first element from LinkedList every time you're iterating through hashset
Cloning anything in Java is not good practice. See for example Clone() vs Copy constructor- which is recommended in java
I have the following code:
System.out.println(dislist.size());
for (int k = 0; k < 10; k++) {
System.out.println(k + dislist.get(k).first + dislist.get(k).second);
if (!dislist.get(k).first.equals(Nodename)) {
if (dislist.get(k).first.equals(myfirst) ||
dislist.get(k).first.equals(mysecond) ||
dislist.get(k).second.equals(myfirst) ||
dislist.get(k).second.equals(mysecond)) {
dislist.remove(k);
}
}
}
}
The Problem is: the print at the beginning clearly says that dislist.size() is 10.
However, I get an array out of bounds exception, telling me that the size of the list is no more than 6.
And yes, I DID add new objects to the list a few lines before that.
I guess when the loop starts that has not been finished yet.
Is there a way to force Java (within the same method) to start the loop only when there is really 10 objects in the list?
You're removing elements from the List as you iterate though it. That's the reason the size is changing.
dislist.remove(k);
Create a new list, and add each element you want to remove to it. After your loop is finished, use disList.removeAll(listOfElementsToRemove) to remove them all at once.
Iterator<YourClass> iter = dislist.iterator();
while (iter.hasNext()) {
YourClass obj = iter.next();
if (/* your expression */) {
iter.remove();
}
}
private int checkLevel(String bigWord, Collection<String> dict, MinMax minMax)
{
/*value initialised to losing*/
int value = 0;
if (minMax == MinMax.MIN) value = 1;
else value = -1;
boolean go = true;
Iterator<String> iter = dict.iterator();
while(iter.hasNext())
{
String str = iter.next();
Collection<Integer> inds = naiveStringSearch(bigWord, str);
if(inds.isEmpty())
{
iter.remove();
}
for (Integer i : inds)
{
MinMax passin = minMax.MIN;
if (minMax == MinMax.MIN) passin = minMax.MAX;
int value2 = checkLevel(removeWord(bigWord, str, i), dict, passin);
if (value2 == -1 && minMax == minMax.MIN)
{
value = -1;
go = false;
}
if (value2 == 1 && minMax == minMax.MAX)
{
value = 1;
go = false;
}
}
if (go == false) break;
}
return value;
}
Error:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:810)
at java.util.HashMap$KeyIterator.next(HashMap.java:845)
at aStringGame.Main.checkLevel(Main.java:67)
at aStringGame.Main.test(Main.java:117)
at aStringGame.Main.main(Main.java:137)
What's the problem here?
Something somewhere is modifying dict. I suspect it might be happening inside this call:
int value2 = checkLevel(removeWord(bigWord, str, i), dict, passin);
^^^^
edit Basically, what happens is that the recursive call to checkLevel() modifies dict through another iterator. This makes the outer iterator's fail-fast behaviour to kick in.
You can't modify a Collection while you're iterating over it with an Iterator.
Your attempt to call iter.remove() breaks this rule (your removeWord method might, too).
You CAN modify a List while iterating IF you use a ListIterator to iterate.
You can convert your Set to a List and use a List iterator:
List<String> tempList = new ArrayList<String>(dict);
ListIterator li = tempList.listIterator();
Another option is to keep track of the elements you want to remove while iterating.
You could place them in a Set, for example.
You could then call dict.removeAll() after your loop.
Example:
Set<String> removeSet = new HashSet<String>();
for (String s : dict) {
if (shouldRemove(s)) {
removeSet.add(s);
}
}
dict.removeAll(removeSet);
When using a for each loop you are not allowed to modify the Collection you are iterating inside the loop. If you need to modify it, use a classic for loop
This is a common occurance in all Collections classes. For instance the entry in TreeSet uses failfast method.
The iterators returned by this class's iterator method are fail-fast:
if the set is modified at any time after the iterator is created, in
any way except through the iterator's own remove method, the iterator
will throw a ConcurrentModificationException. Thus, in the face of
concurrent modification, the iterator fails quickly and cleanly,
rather than risking arbitrary, non-deterministic behavior at an
undetermined time in the future.
http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html