Iterators in Java - java

So I have a list of integers for each object of a class.
I am trying to iterate over list of each object, when i encounter a certain condition I move on to the next object and so forth.
My question here is, when i get back to the pervious object which still has unvisited elements in the list how do I remember the iterator for that particular object. Here is the code I have written:
void function(Object u, List<Integer> tour, Iterator it) {
Object e;
while (it.hasNext()) {
e = it.next();
if (!e.visited) {
tour.add(e);
e.visited = true;
Vertex v = e.otherEnd(e.from);
v.outgoing++;
it = v.adj.listIterator(v.outgoing - 1);
//So instead of re-assigning Iterator it each time is there way //to remember the iterator for each list associated with the object?
}
}

Theres three approachs:
Use a for on a new iterator (but it will be only for the second level of your graph and repeated code)
The second one is to change it to recursive.
Or you can do something about having a Stack and be pushing/poping it when necessary.

Create a map to store the index each iterator has gotten to for an object. You can update the map when you need to switch to a new object and pull from it whenever you come back to it

Related

Comparison Error when Storing values in a List, Boolean Map

I have a fully working version of MineSweeper implemented in Java. However, I am trying to add an additional feature that updates a Map to store the indexes of the locations of the mines within a 2D array. For example, if location [x][y] holds a mine, I am storing a linked list containing x and y, which maps to a boolean that is true to indicate that the space holds a mine. (This feature is seemingly trivial, but I am just doing this to practice with Collections in Java.)
My relevant private instance variables include:
public Class World{ ...
private LinkedList<Integer> index;
private Map<LinkedList<Integer>, Boolean> revealed;
"index" is the list to be stored in the map as the key for each boolean.
In my constructor I have:
public World(){ ...
tileArr = new Tile[worldWidth][worldHeight];
revealed = new TreeMap<LinkedList<Integer>, Boolean>();
index = new LinkedList<Integer>();
... }
Now, in the method in which I place the mines, I have the following:
private void placeBomb(){
int x = ran.nextInt(worldWidth); //Random stream
int y = ran.nextInt(worldHeight); //Random stream
if (!tileArr[x][y].isBomb()){
tileArr[x][y].setBomb(true);
index.add(x); //ADDED COMPONENT
index.add(y);
revealed.put(index, true);
index.remove(x);
index.remove(y); //END OF ADDED COMPONENT
} else placeBomb();
}
Without the marked added component my program runs fine, and I have a fully working game. However, this addition gives me the following error.
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
cannot be cast to java.lang.Comparable
If anyone could help point out where this error might be, it would be very helpful! This is solely for additional practice with collections and is not required to run the game.
There are actually about 3 issues here. One that you know about, one that you don't and a third which is just that using LinkedList as a key for a map is clunky.
The ClassCastException happens because TreeMap is a sorted set and requires that every key in it implement the Comparable interface, or else you have to provide a custom Comparator. LinkedList doesn't implement Comparable, so you get an exception. The solution here could be to use a different map, like HashMap, or you could write a custom Comparator.
A custom Comparator could be like this:
revealed = new TreeMap<List<Integer>, Boolean>(
// sort by x value first
Comparator.comparing( list -> list.get(0) )
// then sort by y if both x values are the same
.thenComparing( list -> list.get(1) )
);
(And I felt compelled to include this, which is a more robust example that isn't dependent on specific elements at specific indexes):
revealed = new TreeMap<>(new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> lhs, List<Integer> rhs) {
int sizeComp = Integer.compare(lhs.size(), rhs.size());
if (sizeComp != 0) {
return sizeComp;
}
Iterator<Integer> lhsIter = lhs.iterator();
Iterator<Integer> rhsIter = rhs.iterator();
while ( lhsIter.hasNext() && rhsIter.hasNext() ) {
int intComp = lhsIter.next().compareTo( rhsIter.next() );
if (intComp != 0) {
return intComp;
}
}
return 0;
}
});
The issue that you don't know about is that you're only ever adding one LinkedList to the map:
index.add(x);
index.add(y);
// putting index in to the map
// without making a copy
revealed.put(index, true);
// modifying index immediately
// afterwards
index.remove(x);
index.remove(y);
This is unspecified behavior, because you put the key in, then modify it. The documentation for Map says the following about this:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
What will actually happen (for TreeMap) is that you are always erasing the previous mapping. (For example, the first time you call put, let's say x=0 and y=0. Then the next time around, you set the list so that x=1 and y=1. This also modifies the list inside the map, so that when put is called, it finds there was already a key with x=1 and y=1 and replaces the mapping.)
So you could fix this by saying something like either of the following:
// copying the List called index
revealed.put(new LinkedList<>(index), true);
// this makes more sense to me
revealed.put(Arrays.asList(x, y), true);
However, this leads me to the 3rd point.
There are better ways to do this, if you want practice with collections. One way would be to use a Map<Integer, Map<Integer, Boolean>>, like this:
Map<Integer, Map<Integer, Boolean>> revealed = new HashMap<>();
{
revealed.computeIfAbsent(x, HashMap::new).put(y, true);
// the preceding line is the same as saying
// Map<Integer, Boolean> yMap = revealed.get(x);
// if (yMap == null) {
// yMap = new HashMap<>();
// revealed.put(x, yMap);
// }
// yMap.put(y, true);
}
That is basically like a 2D array, but with a HashMap. (It could make sense if you had a very, very large game board.)
And judging by your description, it sounds like you already know that you could just make a boolean isRevealed; variable in the Tile class.
From the spec of a treemap gives me this:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
The Java Linkedlist can not be compared just like that. You have to give it a way to compare them or just use another type of map, that does not need sorting.

Remove an object from an ArrayList without (implicitly) looping through it

I am looping through a list A to find X. Then, if X has been found, it is stored into list B. After this, I want to delete X from list A. As speed is an important issue for my application, I want to delete X from A without looping through A. This should be possible as I already know the location of X in A (I found its position in the first line). How can I do this?
for(int i = 0; i<n; i++) {
Object X = methodToGetObjectXFromA();
B.add(X);
A.remove(X); // But this part is time consuming, as I unnecessarily loop through A
}
Thanks!
Instead of returning the object from yhe method, you can return its index and then remove by index:
int idx = methodToGetObjectIndexFromA();
Object X = A.remove(idx); // But this part is time consuming, as I unnecessarily loop through A
B.add(X);
However, note that the remove method may be still slow due to potential move of the array elements.
You can use an iterator, and if performance is an issue is better you use a LinkedList for the list you want to remove from:
public static void main(String[] args) {
List<Integer> aList = new LinkedList<>();
List<Integer> bList = new ArrayList<>();
aList.add(1);
aList.add(2);
aList.add(3);
int value;
Iterator<Integer> iter = aList.iterator();
while (iter.hasNext()) {
value = iter.next().intValue();
if (value == 3) {
bList.add(value);
iter.remove();
}
}
System.out.println(aList.toString()); //[1, 2]
System.out.println(bList.toString()); //[3]
}
If you stored all the objects to remove in a second collection, you may use ArrayList#removeAll(Collection)
Removes from this list all of its elements that are contained in the
specified collection.
Parameters:
c collection containing elements to be removed from this list
In this case, just do
A.removeAll(B);
When exiting your loop.
Addition
It calls ArrayList#batchRemove which will use a loop to remove the objects but you do not have to do it yourself.

ListIterator allocation in Java

I have a real-time program that runs a continuous while loop...
example:
while(true)
{
}
Inside that loop I have a for(MyObject o: myobjects) loop. When I run my code I see that every iteration of the while loop a new iterator is created to loop over my LinkedList called myobjects
What is a better way of iterating over a LinkedList without having Java create a ListIterator every time?
New iterator is given out by design. Read this. It gives you a new iterator every time you call iterator() method and it doesn't maintain states.
Code for the iterator :
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
What is a better way of iterating over a LinkedList without having Java create a ListIterator every time?
"Better" is very situational. You should consider whether the behavior you have now is actually a problem, because what you describe is about the simplest Java code for the job.
If you do need to iterate specifically over a LinkedList (as opposed, say, to an ArrayList), and you want to do so without creating a new ListIterator every time, then your best bet is probably to create a single ListIterator up front, and reuse it at every iteration:
ListIterator<MyObject> iterator = myobjects.listIterator();
while (true) {
// Return to the beginning of the list:
while (iterator.hasPrevious()) {
iterator.previous();
}
// The desired iteration:
while (iterator.hasNext()) {
MyObject o = iterator.next();
// do something with o
}
}
Do be aware, however, that this opens you up to trouble if the underlying list is modified. Any such modification will invalidate the ListIterator (its methods should start throwing ConcurrentModificationExceptions). In your original code, that will affect just one iteration of the outer loop, but if you reuse the iterator then you may need different handling of that situation. If the list is modified elsewhere in the outer loop, then re-using the same iterator is right out.
On the other hand, if you could be sure that you have a RandomAccess list, such as an ArrayList, then you could reasonably avoid iterators altogether, and just iterate by index:
while (true) {
// The desired iteration:
for (int i = 0; i < myobjects.size(); i += 1) {
MyObject o = myobjects.get(i);
// do something with o
}
}
Do not do that if you have or may have a LinkedList, however, because it will increase the cost of the iteration from O(n) to O(n2) for LinkedLists and similar sequential-access lists.
The source code of LinkedList says a new iterator is being created. If your list is dynamic, you will need a new iterator as it will become stale.
But, I believe if your list is a constant, you are better off running a normal for loop.
`int length = myobjects.size();
for (int i=0; i < length; i++)
{
//access here
}`

how to remove all occurrences without removeAll method?

suppose i want to write a method to remove a particular occurrence in a list.
if the key is not on the list, return the list as it is.
methods that are given and i can use :
boolean isEmpty- true if and only if the list is empty
addFirst - adds a given object to the front of the list
removeFirst - removes an object from the front of the list
boolean isMem - checks if the given object is a member in the list
reverse - reverse the order of the items in the list
one of the codes i have found regarding the question is this code:
public void removeAllOccurrences(Object key){
List ans = new List();
while (!isEmpty()){
Object data = removeFirst();
if (!data.equals(key)){
ans.addFirst(data);
}
}
while(!ans.isEmpty()){
addFirst(ans.removeFirst());
but i didn't get the last part : while the list is not empty, add an item in front of the list, and then remove it.. ?
also, i would suggest the following code, will gladly hear your professional opinion:
List ans = new list();
if (!(isMember(key) )
return new list();
else
while (!ans.isEmpty ) {
Link first = curr.link;
if (!(data.equals(key))
ans.addFirst(data) ;
curr=curr.next;
}
As for the first question: the while block has emptied this list, and the ans object contains the new list, so, at the end, the method drains the ans contents to this list.
Regarding the second code block, the while block will never execute because the ans object has just been created, so ans.isEmpty() will always return true.

Modifying a set during iteration java

I'm looking to make a recursive method iterative.
I have a list of Objects I want to iterate over, and then check their subobjects.
Recursive:
doFunction(Object)
while(iterator.hasNext())
{
//doStuff
doFunction(Object.subObjects);
}
I want to change it to something like this
doFunction(Object)
iIterator = hashSet.iterator();
while(Iterator.hasNext()
{
//doStuff
hashSet.addAll(Object.subObjects);
}
Sorry for the poor psuedo code, but basically I want to iterate over subobjects while appending new objects to the end of the list to check.
I could do this using a list, and do something like
while(list.size() > 0)
{
//doStuff
list.addAll(Object.subObjects);
}
But I would really like to not add duplicate subObjects.
Of course I could just check whether list.contains(each subObject) before I added It.
But I would love to use a Set to accomplish that cleaner.
So Basically is there anyway to append to a set while Iterating over it, or is there an easier way to make a List act like a set rather than manually checking .contains()?
Any comments are appreciated.
Thanks
I would use two data structures --- a queue (e.g. ArrayDeque) for storing objects whose subobjects are to be visited, and a set (e.g. HashSet) for storing all visited objects without duplication.
Set visited = new HashSet(); // all visited objects
Queue next = new ArrayDeque(); // objects whose subobjects are to be visited
// NOTE: At all times, the objects in "next" are contained in "visited"
// add the first object
visited.add(obj);
Object nextObject = obj;
while (nextObject != null)
{
// do stuff to nextObject
for (Object o : nextObject.subobjects)
{
boolean fresh = visited.add(o);
if (fresh)
{
next.add(o);
}
}
nextObject = next.poll(); // removes the next object to visit, null if empty
}
// Now, "visited" contains all the visited objects
NOTES:
ArrayDeque is a space-efficient queue. It is implemented as a cyclic array, which means you use less space than a List that keeps growing when you add elements.
"boolean fresh = visited.add(o)" combines "boolean fresh = !visited.contains(o)" and "if (fresh) visited.add(o)".
I think your problem is inherently a problem that needs to be solved via a List. If you think about it, your Set version of the solution is just converting the items into a List then operating on that.
Of course, List.contains() is a slow operation in comparison to Set.contains(), so it may be worth coming up with a hybrid if speed is a concern:
while(list.size() > 0)
{
//doStuff
for each subObject
{
if (!set.contains(subObject))
{
list.add(subObject);
set.add(subObject)
}
}
}
This solution is fast and also conceptually sound - the Set can be thought of as a list of all items seen, whereas the List is a queue of items to work on. It does take up more memory than using a List alone, though.
If you do not use a List, the iterator will throw an exception as soon as you read from it after modifying the set. I would recommend using a List and enforcing insertion limits, then using ListIterator as that will allow you to modify the list while iterating over it.
HashSet nextObjects = new HashSet();
HashSet currentObjects = new HashSet(firstObject.subObjects);
while(currentObjects.size() > 0)
{
Iterator iter = currentObjects.iterator();
while(iter.hasNext())
{
//doStuff
nextObjects.add(subobjects);
}
currentObjects = nextObjects;
nextObjects = new HashSet();
}
I think something like this will do what I want, I'm not concerned that the first Set contains duplicates, only that the subObjects may point to the same objects.
Use more than one set and do it in "rounds":
/* very pseudo-code */
doFunction(Object o) {
Set processed = new HashSet();
Set toProcess = new HashSet();
Set processNext = new HashSet();
toProcess.add(o);
while (toProcess.size() > 0) {
for(it = toProcess.iterator(); it.hasNext();) {
Object o = it.next();
doStuff(o);
processNext.addAll(o.subObjects);
}
processed.addAll(toProcess);
toProcess = processNext;
toProcess.removeAll(processed);
processNext = new HashSet();
}
}
Why not create an additional set that contains the entire set of objects? You can use that for lookups.

Categories