Solve Java ArrayList remove with recursion index? - java

I have a strange problem to which I know the work around but I want to do it with array list this time. Here is the problem:
I have a tree of employees. Employee is a simple class (under is list of employees that work for this employee) :
class Employee
{
String name;
ArrayList<Employee> under = new ArrayList<Employee>();
//fire function
}
My task is to recursively fire all the employees which do not have employees under them. I know how to this with work around with custom made list data structure but I want to do it with array list. Here is my code so far:
public boolean Fire()
{
if (under.isEmpty())
return true;
else
{
for (int x = 0; x < under.size(); x ++)
{
if (under.get(x).Fire())
under.remove(x);
}
}
return false;
}
But problem for this code is that when I remove under.remove(x) the under.size() gets smaller and indexes get messed up. I tried to set x = 0, after every under.remove(x) but it did not do exactly right. One employee to much still left. Any solutions with array list structure?

This is a classic problem with removal or deletion.
You have to iterate backwards through the List. That way, when you remove an element, you don't skip other elements or go past the end of the List.
public boolean Fire()
{
if (under.isEmpty())
return true;
else
{
for (int x = under.size() - 1; x >= 0; x--)
{
if (under.get(x).Fire())
under.remove(x);
}
}
return false;
}

Try using an iterator. You just keep traversing it using .next() on the iterator and whenever you find someone that doesn't have employees under him, you call .remove() (on the iterator) which will remove the last element that the iterator gave you.

That's why Iterator has remove() method. Look up Collection's iterator() call and use it in your for loop.

Related

Is removing elements from an ArrayList or LinkedList while iterating through it with a for loop in Java bad? If so, why?

I was showing my code to someone and they said that it would cause undefined behavior. Being a Java programmer, that's not something I understand well. In the following code block I am iterating through scenes, which is an ArrayList, and removing elements from it.
for(int i = 0; i < scenes.size() - 1; i++)
{
if(!(Double.valueOf(scenes.get(i + 1)) - Double.valueOf(scenes.get(i)) > 10))
{
scenes.remove(i + 1);
i--;
}
}
This compiles and doesn't throw an exception at runtime, but I'm still not sure if it's a programming no-no, why it's a programming no-no, and what is the right way to do it. I've heard about using Iterator.remove() and about just creating a whole new List.
In an ArrayList, removing an element from the middle of the list requires you to shift all of the elements with a higher index down by one. This is fine if you do it once (or a small number of times), but inefficient if you do it repeatedly.
You don't really want to use an Iterator for this either, because Iterator.remove() suffers from the same issue.
A better approach to this is to go through the list, moving the elements you want to keep to their new positions; and then just remove the tail of the list at the end:
int dst = 0;
for (int src = 0; src < scenes.size(); ++dst) {
// You want to keep this element.
scenes.set(dst, scenes.get(src++));
// Now walk along the list until you find the element you want to keep.
while (src < scenes.size()
&& Double.parseDouble(scenes.get(src)) - Double.parseDouble(scenes.get(dst)) <= 10) {
// Increment the src pointer, so you won't keep the element.
++src;
}
}
// Remove the tail of the list in one go.
scenes.subList(dst, scenes.size()).clear();
(This "shift and clear" approach is what is used by ArrayList.removeIf; you can't use that directly here because you can't inspect adjacent elements in the list, you only have access to the current element).
You can take a similar approach which will also work efficiently with non-random access lists such as LinkedList. You need to avoid repeatedly calling get and set, since these are e.g. O(size) in the case of LinkedList.
In that case, you would use ListIterator instead of plain indexes:
ListIterator<String> dst = scenes.listIterator();
for (ListIterator<String> src = scenes.listIterator(); src.hasNext();) {
dst.next();
String curr = src.next();
dst.set(curr);
while (src.hasNext()
&& Double.parseDouble(src.next()) - Double.parseDouble(curr) <= 10) {}
}
scenes.subList(dst.nextIndex(), scenes.size()).clear();
Or something like this. I've not tested it, and ListIterator is always pretty confusing to use.
This is straightforward and will work for either ArrayList or LinkedList:
Iterator<String> iterator = list.iterator();
double current = 0;
double next;
boolean firstTime = true;
while (iterator.hasNext()) {
if (firstTime) {
current = Double.parseDouble(iterator.next());
firstTime = false;
} else {
next = Double.parseDouble(iterator.next());
if (next - current > 10) {
current = next;
} else {
iterator.remove();
}
}
}

Iterating over List of Lists

I'm trying to iterate over a list os lists but I'm getting CME all the time even using Iterator to remove and add elements while iterating over the lists.
I searched here in the community for similar questions but those I found didn't help me. Really hope you guys help me to figure out how to do what I need to do.
I have I ListIterator<List<Event<T>>> itrListsEvent = partitionSubLists.listIterator();
partitionSubLists is A list of lists. So I have one bigger List and inside it I have four sublists.
I need to iterate over the sublists, and while iterating I remove and add elements. After finishing to iterate over the first sublist, I need to go forward to iterate over the second sublist and so on and so forth.
This is what I've done so far:
public List<List<Event<T>>> partitionedLists (List<Event<T>> list)
{
int listSize = list.size();
int partitionSize = listSize / 4;
List<List<Event<T>>> partitions = new ArrayList<>();
for (int i = 0; i < listSize; i += partitionSize)
{
partitions.add(list.subList(i, Math.min(i + partitionSize, list.size())));
}
return partitions;
}
List<List<Event<T>>> partitionSubLists = partitionedLists(List<Event<T>>);
ListIterator<List<Event<T>>> itrListsEvent = partitionSubLists.listIterator();
while(itrListsEvent.hasNext())
{
List<PrefixEvent<T>> listPE = new ArrayList<Event<T>>();
listPE = itrListsPrefixEvent.next();
ListIterator<Event<T>> itrEvent = listPE.listIterator();
while(itrEvent.hasNext())
{
//here I remove and add elements inside the sublist.
//when finished, I need to go back to first while and go forward to the next sublists
//and in this moment, i got ConcurrentModificationException
itrEvent.remove()
.
.
.
// some code here
itrEvent.add(new Event<T>);
}
}
It's rather unclear exactly what you're trying to achieve. As far as I understand, you could achieve it like this:
List<PrefixEvent<T>> listPE = itrListsPrefixEvent.next();
// No iterator.
for (int i = 0; i < listPE.size(); ++i) {
listPE.remove(i);
// some code here
listPE.add(i, new Event<>());
}
This avoids a ConcurrentModificationException because you don't structurally modify the list after creating an Iterator.
If you don't actually require the "one element removed" list in between the itrEvent.remove() and itrEvent.add(new Event<T>()), you can continue to use the ListIterator, and then set the value to a new value:
itrEvent.set(new Event<>());

Removing duplicates from an ArrayList?

Good afternoon everyone, I am currently studying for my Java Final and I have a review exercise that asks the reader to create a program that asks the user to input 10 integers and then to use a method to remove duplicates and display the distinct list. The method is provided for you as well.
I've gotten the majority of the code written, in fact I thought I was done until I realized that the for loop is removing more than just duplicates..
Here is my code:
public class lab25 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner input = new Scanner(System.in);
int i;
//Create array list
ArrayList<Integer> numbers = new ArrayList<>();
System.out.println("Please enter 10 numbers!");
//Populate
for(i=0; i<10; i++) {
numbers.add(input.nextInt());
}
System.out.println("Your numbers are: " + numbers.toString());
removeDuplicate(numbers);
System.out.println("The distinct numbers are: " +numbers.toString());
input.close();
}
public static void removeDuplicate(ArrayList<Integer> list) {
int i;
for(i=0; i<list.size(); i++) {
if(list.contains(list.get(i))) {
list.remove(i);
}
}
}
}
Just curious what I have done wrong here? I think my issue might lie in my for loop.. Thanks to all who answer.
list.contains(list.get(i)) always returns true, since the i'th element of the List is contained in the List.
Therefore removeDuplicate is trying to remove all the elements (but you only remove half of them, since after removing the i'th element you skip the new i'th element).
There are many ways to remove duplicates. The most efficient involve using a HashSet. If you want to find duplicates using only List methods, you can check if list.lastIndexOf(list.get(i)) > i.
The expression list.contains(list.get(i)) is always true, since you're asking if the list contains some element from the list. You need to check if list.get(i) is contained in the first i-1 items in the list, which I recommend doing with a loop.
Be aware that a loop with list.remove will run slowly, since removing item i from an ArrayList is done by replacing item i with i+1, then replacing item i+1 with i+2 and so on. This means it takes around length^2 time to make a loop that calls remove in every iteration. The function list.contains has the same problem, as it has to go through the entire list. This may not matter if you have 10 items, but if you had a list with a million items, it would take a long time to run.
The easiest ways is to use Stream.distinct():
public static List<Integer> removeDuplicate(List<Integer> list) {
return list.stream().distinct().collect(Collectors.toList());
}
In case you are free to choose collection, you should use LinkedHashSet instead. It holds ordered unique numbers.
A solution could be this one. I startet at the end of the list that I don't delete indexes the loop has to visit in the future.
public static void removeDuplicate(ArrayList<Integer> list) {
int i = list.size() - 1;
while (i > -1) {
// check for duplicate
for (int j = 0; j < i; j++) {
if (list.get(i) == list.get(j)) {
// is duplicate: remove
list.remove(i);
break;
}
}
i--;
}
}
You are taking the list.get(i) which of course is present in the list, and you will delete all of the values in the end.
You could remove them by using a set:
Set<String> hs = new HashSet<>();
hs.addAll(numbers);
numbers.clear();
numbers.addAll(hs);
If you want to keep the current order and do not want to use set.
List<String> notduplicatedList =
new ArrayList<>(new LinkedHashSet<>(String));

Why isn't my program removing "all"?

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();
}

Java LinkedList issues - how to remove items that meet certain qualifications

I have a question,
Here is what I need to do -
I have BankItems that are associated with numbers. I fill the list but when an objects enters that is $100 more than the lowest dollar value currently in the list, I want to delete the object that has the low value.
First - I create the list
List<BankItem> listOfBankItems = new LinkedList<BankItem>();
Later in the program I create a new BankItem object and it to the list
listOfBankItems.add(createdItem);
and after adding each item I want to check to see if the new item is $100 more than any object already in the list so I run something like this
for (int i = 0; i < listOfBankItems.size(); i++) {
int oldValue =listOfBankItems.get(i).getAmount();
int newValue = createdItem.getAmount();
int calculatedDif = newValue - oldValue;
if (calculatedDif > 100) {
listOfBankItems.remove(i);
}
}
Unfortunately, this isn't working. I don't know what it up. Maybe I shouldn't use a LinkedList? Maybe my logic is way off-base. Please help.
Thanks!!!
The problem is that the index of all items after the removed one will change after you remove that element; therefore you'll basically skip the next element after you remove one.
Use an iterator:
for (Iterator<BankItem> itr = listOfBankItems.iterator(); itr.hasNext();) {
BankItem item = itr.next();
int oldValue = item.getAmount();
int newValue = createdItem.getAmount();
int calculatedDif = newValue - oldValue;
if (calculatedDif > 100) {
itr.remove();
}
}
Your most significant issue probably relates to concurrent modification. For example, if element #49 is the one to be removed, once you remove it, the next element will now be #49, but you will be checking for #50 (as i was still incremented) - so you're probably missing elements from your check.
There are a few ways to handle this. You could remove the i++ from your for loop (leaving only the trailing semi-colon), then do this:
if (calculatedDif > 100) {
listOfBankItems.remove(i);
} else {
i++;
}
Alternatively, you could use an Iterator and its remove() method, which would handle this for you automatically.
You can also improve the performance of this by not obtaining newValue and recalculating calculatedDif on every step of the loop. Declare these 2 lines above the for loop.

Categories