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.
Related
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.
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.
ATTENTION: I CANNOT know if doSomething will remove the element or not. This is an exceptional case that my data structure needs to handle.
My problem is simples:
int size = list.size();
for(int i = 0; i < size; i++) {
MyObj mo = list.get(i);
mo.doSomething();
}
Now if doSomething() remove mo from the list, I eventually get an ArrayIndexOutOfBounds because the list has now shrunk.
What data structure should I use to allow iteration with the possibility of removing? I can NOT use an iterator here, in other words, I can NOT make doSomething return a boolean and call iterator.remove(). The data structure has to somehow handle the situation and continue to iterator through the rest of the elements still there.
EDIT: I CANNOT know if doSomething will remove the element or not. This is an exceptional case that my data structure needs to handle.
Part II => Making a smart listeners notifier to avoid code duplication everywhere
You can use an ArrayList, for example, as long as you update the index and size when something is removed.
List<MyObj> list = new ArrayList<MyObj>();
int size = list.size();
for(int i = 0; i < size; i++) {
MyObj mo = list.get(i);
mo.doSomething();
if (size > list.size()) {
size = list.size();
i--;
}
}
This only works if the item removed is the last one examined. For other changes to the list you will have to have more complicated logic.
What data structure should I use to allow iteration with the possibility of removing?
The simplest option is to take a copy of the list and iterate over that instead:
List<MyObj> copy = new ArrayList<MyObj>(list);
for (MyObj mo : copy) {
mo.doSomething();
}
Now it doesn't matter whether or not anything removes an idea from the original list - that won't change the copy of the list.
Another option is to use CopyOnWriteArrayList. You can then just iterate and remove or add items at will:
The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
I think you should change you doSomething(). If mo.doSomething() can remove mo from l, you mo must know your l.
You can change the code like this:
Create a valid flag, inside of your MyObj. Only listen if valid.
while(list.hasNext()) {
MyObj mo = list.next()
if(mo.isValid()){
mo.doSomething();
} else {
list.remove();
}
}
if I use a for-each loop on a linked list in java,
is it guaranteed that I will iterate on the elements in the order
in which they appear in the list?
I found 5 main ways to iterate over a Linked List in Java (including the Java 8 way):
For Loop
Enhanced For Loop
While Loop
Iterator
Collections’s stream() util (Java8)
For loop
LinkedList<String> linkedList = new LinkedList<>();
System.out.println("==> For Loop Example.");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
Enhanced for loop
for (String temp : linkedList) {
System.out.println(temp);
}
While loop
int i = 0;
while (i < linkedList.size()) {
System.out.println(linkedList.get(i));
i++;
}
Iterator
Iterator<String> iterator = linkedList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
collection stream() util (Java 8)
linkedList.forEach((temp) -> {
System.out.println(temp);
});
One thing should be pointed out is that the running time of For Loop or While Loop is O(n square) because get(i) operation takes O(n) time(see this for details). The other 3 ways take linear time and performs better.
Linked list is guaranteed to act in sequential order.
From the documentation
An ordered collection (also known as a
sequence). The user of this interface
has precise control over where in the
list each element is inserted. The
user can access elements by their
integer index (position in the list),
and search for elements in the list.
iterator()
Returns an iterator over the elements in this list in proper sequence.
As the definition of Linkedlist says, it is a sequence and you are guaranteed to get the elements in order.
eg:
import java.util.LinkedList;
public class ForEachDemonstrater {
public static void main(String args[]) {
LinkedList<Character> pl = new LinkedList<Character>();
pl.add('j');
pl.add('a');
pl.add('v');
pl.add('a');
for (char s : pl)
System.out.print(s+"->");
}
}
Linked list does guarantee sequential order.
Don't use linkedList.get(i), especially inside a sequential loop since it defeats the purpose of having a linked list and will be inefficient code.
Use ListIterator
ListIterator<Object> iterator = myLinkedList.listIterator();
while( iterator.hasNext()) {
System.out.println(iterator.next());
}
Each java.util.List implementation is required to preserve the order so either you are using ArrayList, LinkedList, Vector, etc. each of them are ordered collections and each of them preserve the order of insertion (see http://download.oracle.com/javase/1.4.2/docs/api/java/util/List.html)
Adding my inputs for future visitors.
First things first: as per $jls-14.14.2, for-each internally use Iterator.
Now, when you iterate over LinkedList using a for-each or an iterator then the looping is always sequential.
But this is prone to thread safety issues. So, two things can happen:
If you use a non-threadsafe List implementation then you will run into ConcurrentModificationException
You can use a threadsafe List implementation like CopyOnWriteArrayList. And if you must use a LinkedList only then use Collections.synchronizedList() to convert your non-threadsafe LL into a threadsafe LL, but again you need to watch out for using iterator in a threadsafe manner.
What is the best way to get value from java.util.Collection by index?
You shouldn't. a Collection avoids talking about indexes specifically because it might not make sense for the specific collection. For example, a List implies some form of ordering, but a Set does not.
Collection<String> myCollection = new HashSet<String>();
myCollection.add("Hello");
myCollection.add("World");
for (String elem : myCollection) {
System.out.println("elem = " + elem);
}
System.out.println("myCollection.toArray()[0] = " + myCollection.toArray()[0]);
gives me:
elem = World
elem = Hello
myCollection.toArray()[0] = World
whilst:
myCollection = new ArrayList<String>();
myCollection.add("Hello");
myCollection.add("World");
for (String elem : myCollection) {
System.out.println("elem = " + elem);
}
System.out.println("myCollection.toArray()[0] = " + myCollection.toArray()[0]);
gives me:
elem = Hello
elem = World
myCollection.toArray()[0] = Hello
Why do you want to do this? Could you not just iterate over the collection?
I agree with Matthew Flaschen's answer and just wanted to show examples of the options for the case you cannot switch to List (because a library returns you a Collection):
List list = new ArrayList(theCollection);
list.get(5);
Or
Object[] list2 = theCollection.toArray();
doSomethingWith(list[2]);
If you know what generics is I can provide samples for that too.
Edit: It's another question what the intent and semantics of the original collection is.
In general, there is no good way, as Collections are not guaranteed to have fixed indices. Yes, you can iterate through them, which is how toArray (and other functions) work. But the iteration order isn't necessarily fixed, and if you're trying to index into a general Collection, you're probably doing something wrong. It would make more sense to index into a List.
I agree that this is generally a bad idea. However, Commons Collections had a nice routine for getting the value by index if you really need to:
CollectionUtils.get(collection, index)
You must either wrap your collection in a list (new ArrayList(c)) or use c.toArray() since Collections have no notion of "index" or "order".
Convert the collection into an array by using function
Object[] toArray(Object[] a)
It would be just as convenient to simply convert your collection into a list whenever it updates. But if you are initializing, this will suffice:
for(String i : collectionlist){
arraylist.add(i);
whateverIntID = arraylist.indexOf(i);
}
Be open-minded.
you definitively want a List:
The List interface provides four methods for positional (indexed) access to list elements.
Lists (like Java arrays) are zero based.
Also
Note that these operations may execute in time proportional to the index value for some
implementations (the LinkedList class, for example). Thus, iterating over the elements in a > list is typically preferable to indexing through it if the caller does not know the
implementation.
If you need the index in order to modify your collection you should note that List provides a special ListIterator that allow you to get the index:
List<String> names = Arrays.asList("Davide", "Francesco", "Angelocola");
ListIterator<String> i = names.listIterator();
while (i.hasNext()) {
System.out.format("[%d] %s\n", i.nextIndex(), i.next());
}
use for each loop...
ArrayList<Character> al = new ArrayList<>();
String input="hello";
for (int i = 0; i < input.length(); i++){
al.add(input.charAt(i));
}
for (Character ch : al) {
System.Out.println(ch);
}
If your Collection is a List, simply cast it as a List and call get(final int index). Otherwise, it might make sense to consider finding the nth element in an ordered set, for example if it's a LinkedHashSet respecting insertion order (keep in mind that it's possible to create such an instance not respecting insertion order), you can use Collection.stream().skip(index).limit(1).findFirst().orElse(null).
You can get the value from collection using for-each loop or using iterator interface. For a Collection c
for (<ElementType> elem: c)
System.out.println(elem);
or Using Iterator Interface
Iterator it = c.iterator();
while (it.hasNext())
System.out.println(it.next());