I want to iterate through some kind of list, checking if its elements meet a property, and if they don't deleting them from the array. What I've thought is something like this:
int index = 0;
for(int i = 0; i < list.size(); ++i) {
if(list.isProperty()) list.delete(index) //We delete the element at list[index]
else ++index;
}
Maybe those aren't the real methods of the list interface in java, but they're quite self-explanatory.
Is this a good approach? Which data structure would fit best if I have to run this operation many times? I don't think an arrayList would work as I'd have to be moving around elements each time I delete and I can't ensure the elements I'll remove are in the head or the tail of the list either.
You can achieve it using iterator.Without having concurrent modification exception.
Say your list consists of object A
List<A> list = new ArrayList<A>();
Iterator<A> iterator = list.iterator();
while (iterator.hasNext()) {
A current = iterator.next();
if(current.isProperty()) {
iterator.remove();;
}
}
You should remove an element from a List using an Iterator. You can use this with ArrayList.
List<YourDataType> yourList = new ArrayList<YourDataType>();
Iterator<YourDataType> it = yourList.iterator();
while (it.hasNext())
it.remove();
With this you can use if-else to specify the element, which should be removed.
This should give you some hints, why you should use an Iterator.
Related
Given a list of Elements. For example list1 = {a,b,c,d,e}
I want to return an iterator that only iterates over the elements that satisfy a certain condition.
If for example b,d,e meet that condition, then the return value is an iterator that iterates only over those elements b,d,e.
Not allowed to use builtin java collections.
Also I have built my own linked list and iterator.
I have tried doing this by creating a list and adding only the elements where the condition is met.
Then returning an iterator over that new list.
The problem is, that later on i want to remove elements with this iterator and those removed elements should also be removed in the original list.
Is this somehow possible?
I am aware that this does not work:
public Iterator<Y> iterator(X x) {
MyLinkedList<Y> temp = new MyLinkedList<>();
for (int i = 0; i < listY.size(); i++) {
if(!x.satisfies(listY.get(i))){
temp.addFirst(listY.get(i));
listY.remove(i);
}
}
MyIterator<Y> iter1 = new MyIterator<>(listY);
for (int i = 0; i < temp.size(); i++) {
listY.addFirst(temp.get(i));
}
return iter1;
}
Ok, this is a proof-of-concept I have on my head that has been bugging me for a few days:
Let's say I have:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
if (!str.equals("foo") || !str.equals("bar")) a.remove(str);
}
this would end up with the list ["foo", "bazz", "bar"] because it would read the string at index 1 ("buzz"), delete it, the string at index 2 ("bazz") would jump to index 1 and it would be bypassed without being verified.
What I came up with was:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
boolean removed = false;
if (!str.equals("foo") || !str.equals("bar"))
{
a.remove(str);
removed = true;
}
if (removed) i--;
}
It should work this way (atleast it does in my head lol), but messing with for iterators is not really good practice.
Other way I thought would be creating a "removal list" and add items to that list that needed to be removed from list a, but that would be just plain resource waste.
So, what is the best practice to remove items from a list efficiently?
Use an Iterator instead and use Iterator#remove method:
for (Iterator<String> it = a.iterator(); it.hasNext(); ) {
String str = it.next();
if (!str.equals("foo") || !str.equals("bar")) {
it.remove();
}
}
From your question:
messing with for iterators is not really good practice
In fact, if you code oriented to interfaces and use List instead of ArrayList directly, using get method could become into navigating through all the collection to get the desired element (for example, if you have a List backed by a single linked list). So, the best practice here would be using iterators instead of using get.
what is the best practice to remove items from a list efficiently?
Not only for Lists, but for any Collection that supports Iterable, and assuming you don't have an index or some sort of key (like in a Map) to directly access to an element, the best way to remove an element would be using Iterator#remove.
You have three main choices:
Use an Iterator, since it has that handy remove method on it. :-)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (/*...you want to remove `it.next()`...*/) {
it.remove();
}
}
Loop backward through the list, so that if you remove something, it doesn't matter for the next iteration. This also has the advantage of only calling list.size() once.
for (int index = list.size() - 1; index >= 0; --index) {
// ...check and optionally remove here...
}
Use a while loop instead, and only increment the index variable if you don't remove the item.
int index = 0;
while (index < list.size()) {
if (/*...you want to remove the item...*/) {
list.removeAt(index);
} else {
// Not removing, move to the next
++index;
}
}
Remember that unless you know you're dealing with an ArrayList, the cost of List#get(int) may be high (it may be a traversal). But if you know you're dealing with ArrayList (or similar), then...
Your first example will likely cause off-by-one errors, since once you remove an object your list's indexes will change. If you want to be quick about it, use an iterator or List's own .remove() function:
Iterator<String> itr = yourList.iterator();
while (itr.hasNext()) {
if ("foo".equals(itr.next()) {
itr.remove();
}
}
Or:
yourList.remove("foo");
yourList.removeAll("foo"); // removes all
ArrayList.retainAll has a "smart" implementation that does the right thing to be linear time. You can just use list.retainAll(Arrays.asList("foo", "bar")) and you'll get the fast implementation in that one line.
What is the best list/set/array in Java that combines the following aspects:
maintain order of added elements
make if possible to both iterate forwards and backwards
of course good performance
I thought about a LinkedList, I then could insert elements by add(0, element) which would simulate a reverse order. Most of the time I will be using backwards iteration, so using this I can just iterate trough.
And if not, I can list.listIterator().hasPrevious().
But are there better approaches?
ArrayList will probably be your best bet. You can iterate through it in the following manner:
for (ListIterator it = list.listIterator(list.size()); it.hasPrevious();) {
Object value = it.previous();
}
A LinkedList will work but it will have more object creation overhead since you need to instantiate a Link for each element you store.
If you can get by index and wish to iterate over the collection then you can use a List and get(index) allow you to get the object in that place in the list. Arrays allow you to do this, you can just reference the index as normal, however if your array might grow then a Collection is going to be easier to use.
You can use List.size() and element through the object using a for loop rather than using an Iterator object, this will allow you to iterator over the list both forwards and backwards. For example:
List<AnObject> myList = new ArrayList<AnObject>;
// Add things to the list
for (int i = 0 ; i < myList.size; i++) {
AnObject myObject = myList.get(i);
}
for (int i = myList.size()-1 ; i <= 0 ; i--) {
AnObject myObject = myList.get(i);
}
Set is not applicable as a Set does not maintain ordering.
How can I iterate through items of a LinkedHashSet from the last item to the first one?
If you want to continue to use collections, you could use the following:
LinkedHashSet<T> set = ...
LinkedList<T> list = new LinkedList<>(set);
Iterator<T> itr = list.descendingIterator();
while(itr.hasNext()) {
T item = itr.next();
// do something
}
If you're fine with using an array instead, you could take a look at hvgotcodes' answer.
This is another way:
LinkedHashSet<T> set = ...
List<T> list = new ArrayList<>(set);
Collections.reverse(list);
for( T item : list ){
...
}
er, assuming you mean LinkedHashSet...
I would use toArray and just use a reverse for loop.
There might be a better way to do it, but that should work. toArray guarantees any order is preserved
If this set makes any guarantees as to what order its elements are
returned by its iterator, this method must return the elements in the
same order.
Something like
Set<MyType> mySet = new LinkedHashSet();
...
MyType[] asArray = mySet.toArray(new MyType[0]);
for (int i = asArray.length - 1; i>=0; i--){
..
}
If you really meant LinkedHashSet, you could put the elements into an ArrayList and then use the ArrayList's ListIterator.
ListIterator<T> l = new ArrayList<T>(yourLinkedHashList).listIterator();
// ListIterator can iterate in reverse
while(l.hasPrevious()) {
T obj = l.previous();
}
From the javadoc: "This linked list defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order)."
So you can simply:
LinkedHashSet<Integer> numbers = new LinkedHashSet<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(33);
numbers.add(44);
numbers.add(108);
for (Integer i : numbers) {
System.out.println(i);
}
I have an ArrayList that I loop and through some logic I would remove an element at a particular index.
However while I am looping the Arraylist and removing on the way, the ArrayList size and index of particular items are changing as well, resulting in unexpected results.
Anyway to circumvent this?
Here's the code for the iterator approach - substitute your own condition and add generics types <> as needed:
Iterator it = list.iterator();
while(it.hasNext()){
Object o = it.next();
if(someCondition(o)){
it.remove();
}
}
And, as JohnB said in the comments, ArrayList isn't very efficient if you are removing lots of items from large lists...
You can use an Iterator.remove() or iterate backwards.
List<String> list = ...
for(int i= list.size()-1; i>=0; i--)
if(test(list.get(i)))
list.remove(i); // values before `i` are untouched.
or you can decrement the counter.
List<String> list = ...
for(int i= 0; i < list.size(); i++)
if(test(list.get(i)))
list.remove(i--); // move i back as there is one less element.
You could use an iterator which has a remove() method to do exactly that.
Use Iterator for looping. It can be used to remove the elements from the collection.