Unexpected behavior when removing items from ArrayList - java

In my Android app Java code I have an ArrayList of a custom data type. From what I know about ArrayLists when you perform a ArrayList.remove(location) all items located above location get shifted down by one. But for some reason when I iterate backwards from end to start of the array in order to remove selected items in the array, each time and item is removed all the items below it get shifted up one instead of the normal behavior of the higher items getting shifted down one. So when I remove the first item in the middle of the array, the item at 0 shifts to 1 and 0 becomes null. Is there something in particular that I could have done or a certain situation that would cause this to happen?
I apologize for not being clear about exactly what I was doing. Im not iterating through the ArrayList. but instead I have another List containing all of the items in the original list i need to remove. I perform a Collections.sort on the second list and the iterate from end to start on that second list and on each iteration I perform a ArrayList.remove one the first list with the location read from the second list. therefore the removes go from highest location to lowest which i though would prevent any shifts from occurring of the lower items on each removal. but what i see happening is the items lower than the removed item all getting shifted up to fill in the gap. why is this happening?

If you need to iterate through a list, removing items from the list as you go, try using an Iterator and a while loop. The Iterator interface defines a remove() method that deletes the last item retrieved with next() from the Collection being iterated over.
List list = new ArrayList( );
Iterator iter = list.iterator( );
while ( iter.hasNext( ) )
{
Object item = iter.next( );
if ( shouldDelete( item ) )
{
iter.remove( );
}
}

so when i remove an item somewhere in the middle of the list, the item at 0 moves to 1, and 0 then becomes null. Why is this happening?
That shouldn't happen, something else must be wrong. Please post an SSCCE.
I recommend you to solve it like this however:
firstList.removeAll(secondList);

It seems like you only sort the second list, so I'm not surprised at your outcome. Since the first list is unsorted, you will not be iterating through it in reverse order, and strange shifts can be expected.
Why not instead go through the first list using a list iterator, and if the item is contained in the second list, remove it from the first using the iterator?
Or even better, use the ArrayList#removeAll<Collection<?> c) method.

Related

Remove all the strings in an array list containing certain characters

so i have an array list that contains strings such as:
ArrayList<String> list = new ArrayList<>();
list.add("bookshelf");
list.add("bookstore");
list.add("library");
list.add("pencil");
Now i wanna search and remove all the strings in the arraylist that contain the word "book" in them. As far as i understand list.remove("book"); will only search for the particular string "book" and not the strings that contain the word "book". How can i solve this?
You can use removeIf like this:
list.removeIf(s -> s.contains("book"));
Note: this answers applies to Java version 7 and below (of course that it will work for higher versions as well but YCF_L's answer is simpler to implement in versions 8 and above).
The requirement is to iterate the list, check every element, and if it answers a certain condition: remove it.
Since this is the case we fall into a risky scenario where we modify the list while iterating it which is problematic because when we remove an element in the list its size changes.
In order to work around this problem we can iterate the list by index from the last element and back until the first one, this way, removing an element at index n will not effect accessing any element at index < n.
I'll leave the implementation details to you in order not to "spoon feed" and destroy your exercise :)

Can anyone explain how to remove duplicates in ArrayList using indexOf,lastIndexOf

I have this code, I can't understand how the code works, how does it remove duplicates with that indexOf and lastIndexOf?
ArrayList<String> lst = new ArrayList<String>();
lst.add("ABC");
lst.add("ABC");
lst.add("ABCD");
lst.add("ABCD");
lst.add("ABCE");
System.out.println("Duplicates List "+lst);
Object[] st = lst.toArray();
for (Object s : st) {
if (lst.indexOf(s) != lst.lastIndexOf(s)) {
lst.remove(lst.lastIndexOf(s));
}
}
System.out.println("Distinct List "+lst);
how does it remove duplicates with that indexOf and lastIndexOf?
if (lst.indexOf(s) != lst.lastIndexOf(s))
The code above takes the index of the first occurrence of s(if it exists otherwise -1) within the list and also takes the index of the last occurrence of s (if it exists otherwise -1) within the list, if both of the indices are the same then there is no duplicate and if they are not the same then we've found a duplicate thus it gets removed from the list.
From the code you're currently working on, we can confirm that the string "ABC" appears twice within the collection, one at index 0 and the other at index 1 thus when this is checked by the if statement we can guarantee that control will go inside the if statement because they're not the same indices(meaning there are at least two occurrences of the string s), likewise the string "ABCD" appears twice within the list at index 1 and 2(note by this time the last occurrence of "ABC" is already removed from the list hence "ABCD" takes the slot of index 1 due to the nature of a list resizing itself) meaning control will once again go inside the if block because they're not the same indices.
Eventually, the last occurrence of s will be removed from the list and the same procedure is repeated for each iteration of the loop.
There are more efficient ways in which you can remove duplicates from a list, and you should look into it as it can improve performance time.
Try throwing a few more print statements in there, I think you'll be able to see for yourself!
for (Object s : st) {
System.out.println(s);
System.out.println(lst.indexOf(s));
System.out.println(lst.lastIndexOf(s));
if (lst.indexOf(s) != lst.lastIndexOf(s)) {
lst.remove(lst.lastIndexOf(s));
}
}
It is a pretty clunky (i.e. inefficient) way to do it1.
How does it work?
Well this predicate:
lst.indexOf(s) != lst.lastIndexOf(s)
is testing to see if there are two (or more) instances of s in the list. The logic is that if the position of the first s and the last s in the list are different, then there must be at least two of them. Then we remove the last instance.
Since we do this for every string in the original list, if there are M instances of a given string in the list, that test will be performed M times for that string, and will succeed M - 1 times and hence remove M - 1 of those instances. At the end, you are left with just one instance of each distinct string; i.e. no duplicates.
1 - It is O(N^2) and it is possible to eliminate duplicates in O(N) with O(N) temporary space. The general approach is as follows:
Create a temporary array to hold the list content, and a HashSet.
For each s in the original list:
If s is not in the set:
add it to the set, and
add it to the temporary array.
Clear the original list.
Add back all elements in the temporary array.

Adding to a list in java

I'm using List of strings and trying to insert strings to specified indices using
lst.add(index, string);
(lst is of type `List`).
First I initiated the list to 20 spots of null (now lst.size() = 20).
When I'm inserting the first string it's all good : lst.add(1,"Hi") and the list:
[null,Hi,null,...null], lst.size() is still 20
But when I try to add the next strings it extends the list. I mean if I use lst.add(0,"Bye") the list looks like this: [Bye,null,Hi,null,null,...null] and lst.size() = 21 ! Why ?
it added the string "Bye" before the null though it should have replaced it
Any help ? thanks :)
The add() method inserts into the list.
From JavaDoc:
Inserts the specified element at the specified position in this list
(optional operation). Shifts the element currently at that position
(if any) and any subsequent elements to the right (adds one to their
indices).
You want to use the set() method instead.
When you do 'add' it is actually an insert to a list - therefore you would have 21 elements instead of 20 after the add.
A list can be thought as a resizeable array. You shouldn't need to initialise an array with 20 nulls inserted in it.
If you want total control on the size of the collection, simply use array instead of list (String[] instead of List<String>)

If we delete a element from a string arraylist records should not get shifted

I am working on project in which i am using string arraylist. In that i am supposed to delete a element from an arraylist but that element should become null, and arraylist should not get shifted after removing the element.
for ex..
List<String> lister= new ArrayList<String>();
lister.add("abc");
lister.add("def");
lister.add("tur");
now if i want to remove second element from list i wil use lister.remove(1). But i want that position should become null after deleting and elements should not get shifetd one step back, because i need to add an element on that position again.
Don't remove it. Set it to null.
lister.set(1, null);
Perhaps you want to use a String[] instead, then. Or don't use remove, instead just set the index you want to make go away to a null value.

In java to remove an element in an array can you set it to null?

I am trying to make a remove method that works on an array implementation of a list.
Can I set the the duplicate element to null to remove it? Assuming that the list is in order.
ArrayList a = new ArrayList[];
public void removeduplicates(){
for(a[i].equals(a[i+1]){
a[i+1] = null;
}
a[i+1] = a[i];
}
No you can't remove an element from an array, as in making it shorter. Java arrays are fixed-size. You need to use an ArrayList for that.
If you set an element to null, the array will still have the same size, but with a null reference at that point.
// Let's say a = [0,1,2,3,4] (Integer[])
a[2] = null;
// Now a = [0,1,null,3,4]
Yes, you can set elements in an array to null, but code like a[i].equals(a[i+1]) will fail with a NullPointerException if the array contains nulls, so you just have to be more careful if you know that your array may contain nulls. It also doesn't change the size of the array so you will be wasting memory if you remove large numbers of elements. Fixed size arrays are generally not a good way to store data if you are often adding and removing elements - as you can guess from their name.
Can I set the the duplicate element to null to remove it?
You can set an element of the array null but this doesn't remove the element of the array... it just set the element to null (I feel like repeating the first sentence).
You should return a cleaned copy of the array instead. One way to do this would be to use an intermediary java.util.Set:
String[] data = {"A", "C", "B", "D", "A", "B", "E", "D", "B", "C"};
// Convert to a list to create a Set object
List<String> list = Arrays.asList(data);
Set<String> set = new HashSet<String>(list);
// Create an array to convert the Set back to array.
String[] result = new String[set.size()];
set.toArray(result);
Or maybe just use a java.util.Set :)
Is this a homework question?
Your problem is analogous to the stream processing program uniq: Preserve -- by way of copying -- any element that doesn't match the one before it. It only removes all duplicates if the sequence is sorted. Otherwise, it only removes contiguous duplicates. That means you need to buffer at most one element (even if by reference) to use as a comparison predicate when deciding whether to keep an element occurring later in the sequence.
The only special case is the first element. As it should never match any preceding element, you can try to initialize your buffered "previous" element to some value that's out of the domain of the sequence type, or you can special-case your iteration with a "first element" flag or explicitly copy the first element outside the iteration -- minding the case where the sequence is empty, too.
Note that I did not propose you provide this operation as a destructive in-place algorithm. That would only be appropriate with a structure like a linked list with constant-time overhead for removing an element. As others note here, removing an element from an array or vector involves shuffling down successor elements to "fill the hole", which is of time complexity n in the number of successors.
The straight-forward answer to your question is that setting an array or ArrayList element to null gives you a null entry in the array or ArrayList. This is not the same thing as removing the element. If just means that a[i] or a.get(i) will return null rather than the original element.
The code in the question is garbled. If you are going to use an ArrayList, the simplisitic solution would be something like this:
ArrayList a = new ArrayList();
public void removeduplicates() {
for (int i = 0; i < a.size() - 1; ) {
if (a.get(i).equals(a.get(i + 1)) {
a.remove(i);
} else {
i++;
}
}
}
but in the worst case, that is O(N**2) because each call to remove copies all elements at indexes greater than the current value of i.
If you want to improve the performance, do something like this:
public ArrayList removeduplicates() {
ArrayList res = new ArrayList(a.size());
if (a.size() == 0) {
return res;
}
res.add(a.get(0));
for (int i = 1; i < a.size(); i++) {
if (!a.get(i - 1).equals(a.get(i)) {
res.add(a.get(i));
}
}
return res;
}
(This is a quick hack. I'm sure it could be tidied up.)
Your code example was quite confusing. With ArrayList[] you showed an array of ArrayList objects.
Assuming that you're talking about just the java.util.ArrayList, then the most easy way to get rid of duplicates is to use a java.util.Set instead, as mentioned by others. If you really want to have, startwith, or end up with a List for some reasons then do:
List withDuplicates = new ArrayList() {{ add("foo"); add("bar"); add("waa"); add("foo"); add("bar"); }}; // Would rather have used Arrays#asList() here, but OK.
List withoutDuplicates = new ArrayList(new LinkedHashSet(withDuplicates));
System.out.println(withoutDuplicates); // [foo, bar, waa]
The LinkedHashSet is chosen here because it maintains the ordering. If you don't worry about the ordering, a HashSet is faster. But if you actually want to have it sorted, a TreeSet may be more of value.
On the other hand, if you're talking about a real array and you want to filter duplicates out of this without help of the (great) Collections framework, then you'd need to create another array and add items one by one to it while you check if the array doesn't already contain the to-be-added item. Here's a basic example (without help of Arrays.sort() and Arrays.binarySearch() which would have eased the task more, but then you would end up with a sorted array):
String[] array1 = new String[] {"foo", "bar", "foo", "waa", "bar"};
String[] array2 = new String[0];
loop:for (String array1item : array1) {
for (String array2item : array2) {
if (array1item.equals(array2item)) {
continue loop;
}
}
int length = array2.length;
String[] temp = new String[length + 1];
System.arraycopy(array2, 0, temp, 0, length);
array2 = temp;
array2[length] = array1item;
}
System.out.println(Arrays.toString(array2)); // [foo, bar, waa]
Hope this gives new insights.
If you are implementing your own list and you have decide to use a basic primitives storage mechanism. So using an array (rather than an arraylist) could be where you start.
For a simple implementation, your strategy should consider the following.
Decide how to expand your list. You could instantiate data blocks of 200 cells at a time. You would only use 199 because you might want to use the last cell to store the next allocation block.
Such linked list are horrible so you might decide to use a master block to store all the instances of blocks. You instantiate a master block of size 100. You start with one data block of 200 and store its ref in master[0]. As the list grows in size, you progressively store the ref of each new data blocks in master[1] .... master[99] and then you might have to recreate the master list to store 200 references.
For the reason of efficiency, when you delete a cell, you should not actually exterminate it immediately. You let it hang around until enough deletion has occurred for you to recreate the block.
You need to somehow flag a cell has been deleted. So the answer is obvious, of course you can set it to null because you are the king, the emperor, the dictator who decides how a cell is flagged as deleted. Using a null is a great and usual way. If you use null, then you have to disallow nulls from being inserted as data into your list class. You would have to throw an exception when such an attempt is made.
You have to design and write a garbage collection routine and strategy to compact the list by recreating blocks to remove nullified cells en mass. The JVM would not know those are "deleted" data.
You need a register to count the number of deletions and if that threshold is crossed, garbage collection would kick in. Or you have the programmer decide to invoke a compact() method. Because if deletions are sparse and distributed across various data blocks, might as well leave the null/deleted cells hang around. You could only merge adjacent blocks and only if the sum of holes in both blocks count up to 200, obviously.
Perhaps, when data is appended to a list, you deliberately leave null holes in between the data. It's like driving down the street and you see house addresses incremented by ten because the the city has decided that if people wish to build new houses in between existing houses. In that way you don't have to recreate and split a block every time an insertion occurs.
Therefore, the answer is obvious to yourself, of course you can write null to signify a cell is deleted, because it is your strategy in managing the list class.
No, an array element containing a null is still there, it just doesn't contain any useful value.
You could try moving every element from further down in the list up by 1 element to fill the gap, then you have a gap at the end of the array - the array will not shrink from doing this!
If you're doing this a lot, you can use System.arraycopy() to do this packing operation quickly.
Use ArrayList.remove(int index).
if(a[i].equals(foo()))
a.remove(i)
But be careful when using for-loops and removing objects in arrays.
http://java.sun.com/j2se/1.3/docs/api/java/util/ArrayList.html

Categories

Resources