I have a list of names, say 8 names: Joe, Bob, Andrew, Bill, Charlie, Sarah, Ann, Victor
The count of names might differ**.
1) What should I use as name list? Hashmap, Vector, Hashtable, List, ArrayList?
2) I need to match them up like this: Joe-Bob, Andrew-Bill, Charlie-Sarah, Ann-Victor. Could you please show me an example how to make a loop which would do so?
Thank you!
Hashmaps have no order. If you want to insert a list of names like you want, you'd do something like the following (with an array):
for(int i = 0; i < myArray.length - 1; i += 2) {
hashMap.add(myArray[i], myArray[i+1]);
}
1) What should I use as name list? Hashmap, Vector, Hashtable, List, ArrayList?
Well, it depends on your needs :) But, because of the question and because you are mixing collection interfaces (e.g. List) and concrete implementations (e.g. ArrayList or Vector), I think that you should start with the basics. An awesome resource for this is the Trail: Collections from The Java(tm) Tutorials, a really highly recommended reading.
First, you need to understand the various collection interfaces and their purpose. Then you'll choose a concrete implementations. The Interfaces section of the tutorial that I'm quoting below will help you for the first step:
The following list describes the core
collection interfaces:
Collection — the root of the collection hierarchy. A collection
represents a group of objects known as
its elements. The Collection interface
is the least common denominator that
all collections implement and is used
to pass collections around and to
manipulate them when maximum
generality is desired. Some types of
collections allow duplicate elements,
and others do not. Some are ordered
and others are unordered. The Java
platform doesn't provide any direct
implementations of this interface but
provides implementations of more
specific subinterfaces, such as Set
and List. Also see The Collection
Interface section.
Set — a collection that cannot contain duplicate elements. This
interface models the mathematical set
abstraction and is used to represent
sets, such as the cards comprising a
poker hand, the courses making up a
student's schedule, or the processes
running on a machine. See also The Set
Interface section.
List — an ordered collection (sometimes called a sequence). Lists
can contain duplicate elements. The
user of a List generally has precise
control over where in the list each
element is inserted and can access
elements by their integer index
(position). If you've used Vector,
you're familiar with the general
flavor of List. Also see The List
Interface section.
Queue — a collection used to hold multiple elements prior to processing.
Besides basic Collection operations, a
Queue provides additional insertion,
extraction, and inspection operations.
Queues typically, but do not
necessarily, order elements in a FIFO
(first-in, first-out) manner. Among
the exceptions are priority queues,
which order elements according to a
supplied comparator or the elements'
natural ordering. Whatever the
ordering used, the head of the queue
is the element that would be removed
by a call to remove or poll. In a FIFO
queue, all new elements are inserted
at the tail of the queue. Other kinds
of queues may use different placement
rules. Every Queue implementation must
specify its ordering properties. Also
see The Queue Interface section.
Map — an object that maps keys to values. A Map cannot contain duplicate
keys; each key can map to at most one
value. If you've used Hashtable,
you're already familiar with the
basics of Map. Also see The Map
Interface section.
In your case, I don't think that you want a Queue, I'm not sure you need a Map, I think you want to allow duplicate elements so you don't want a Set and this leaves us with a List.
Regarding the concrete implementation, if a thread safe implementation is not needed, ArrayList - or LinkedList, depending on the algorithm in 2) - might be a good choice). But really, have a look at the Implementations section of the tutorial to learn more.
2) I need to match them up like this: Joe-Bob, Andrew-Bill, Charlie-Sarah, Ann-Victor. Could you please show me an example how to make a loop which would do so?
If the initial list can contain duplicate elements, I wouldn't use a Map to store matched names (because a Map cannot contain duplicate keys). So, I'd create a Couple class to store associated names:
public class Couple {
private name1;
private name2;
...
}
and use a List<Couple> to store matched names. But, because the logic of the algorithm is still not clear (does the initial list always contain an odd number of elements? is one element always associated with the immediate next one?), I can't provide more guidance.
It's not exactly clear what you mean by "associating" the names and what you need to do with them. Also you don't say whether the names are unique.
You can pair names this way:
String input = "Joe, Bob, Andrew, Bill, Charlie, Sarah, Ann, Victor";
String names[] = input.split(",");
Map<String, String> output = new HashMap<String, String>();
for (int i=0; i<names.length; i+=2) {
output.put(names[i].trim(), names[i+1].trim());
}
and then do:
output.remove("Joe"); // Joe is paired with Bob
If you also need to associate Bob with Joe, you could do it this way:
String input = "Joe, Bob, Andrew, Bill, Charlie, Sarah, Ann, Victor";
String names[] = input.split(",");
Map<String, String> output = new HashMap<String, String>();
for (int i=0; i<names.length; i+=2) {
String first = names[i].trim();
String second = names[i+1].trim();
output.put(first, second);
output.put(second, first);
}
and then do:
String other = output.remove("Joe");
output.remove(other);
Your choice should depend on the purpose for which you want these names. Do you want efficient search over the list for a given last name? If yes, then you should follow Anon's proposal.
Otherwise, you could just create a class where you could keep information about each person (first name, last name, telephone, etc.) and use a Vector to keep the instances of this class. An example of this class could be:
class PersonDetails {
String firstName;
String lastName;
public PersonDetails(String fn, String ln) {
firstName = fn;
lastName = ln;
}
}
For the insertion of the names in the vector you could use something like the following:
for(int i = 0; i < nameArray.length; i += 2) {
vector.add(new PersonDetails(nameArray[i], nameArray[i+1]));
}
If you have an array to start with but want to remove elements as you process them, first convert it to a List of some kind. Also, removing items from the beginning of an ArrayList can be very expensive, so if you have a lot of names, you may want to use either a LinkedList (though there are very few reasons to actually use this between performance and memory utilization, it is better to have a circular List but this does not come standard with Java).
So, in your case, you know you will be processing the list sequentially so the most efficient I can think of is to create an ArrayList and reverse it then remove from the end, like this:
private Map matchNames(String[] names) {
List namesList = new ArrayList(Arrays.asList(names));
Collections.reverse(namesList);
Map result = new HashMap(namesList.size() / 2);
while (!namesList.isEmpty()) {
result.put(namesList.remove(namesList.size() - 1),
namesList.remove(namesList.size() - 1));
}
return result;
}
Related
In Java there are the SortedSet and SortedMap interfaces. Both belong to the Java Collections framework and provide a sorted way to access the elements.
However, in my understanding there is no SortedList in Java. You can use java.util.Collections.sort() to sort a list.
Any idea why it is designed like that?
List iterators guarantee first and foremost that you get the list's elements in the internal order of the list (aka. insertion order). More specifically it is in the order you've inserted the elements or on how you've manipulated the list. Sorting can be seen as a manipulation of the data structure, and there are several ways to sort the list.
I'll order the ways in the order of usefulness as I personally see it:
1. Consider using Set or Bag collections instead
NOTE: I put this option at the top because this is what you normally want to do anyway.
A sorted set automatically sorts the collection at insertion, meaning that it does the sorting while you add elements into the collection. It also means you don't need to manually sort it.
Furthermore if you are sure that you don't need to worry about (or have) duplicate elements then you can use the TreeSet<T> instead. It implements SortedSet and NavigableSet interfaces and works as you'd probably expect from a list:
TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding
for (String s : set) {
System.out.println(s);
}
// Prints out "cat" and "lol"
If you don't want the natural ordering you can use the constructor parameter that takes a Comparator<T>.
Alternatively, you can use Multisets (also known as Bags), that is a Set that allows duplicate elements, instead and there are third-party implementations of them. Most notably from the Guava libraries there is a TreeMultiset, that works a lot like the TreeSet.
2. Sort your list with Collections.sort()
As mentioned above, sorting of Lists is a manipulation of the data structure. So for situations where you need "one source of truth" that will be sorted in a variety of ways then sorting it manually is the way to go.
You can sort your list with the java.util.Collections.sort() method. Here is a code sample on how:
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
Collections.sort(strings);
for (String s : strings) {
System.out.println(s);
}
// Prints out "cat" and "lol"
Using comparators
One clear benefit is that you may use Comparator in the sort method. Java also provides some implementations for the Comparator such as the Collator which is useful for locale sensitive sorting strings. Here is one example:
Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing
Collections.sort(strings, usCollator);
Sorting in concurrent environments
Do note though that using the sort method is not friendly in concurrent environments, since the collection instance will be manipulated, and you should consider using immutable collections instead. This is something Guava provides in the Ordering class and is a simple one-liner:
List<string> sorted = Ordering.natural().sortedCopy(strings);
3. Wrap your list with java.util.PriorityQueue
Though there is no sorted list in Java there is however a sorted queue which would probably work just as well for you. It is the java.util.PriorityQueue class.
Nico Haase linked in the comments to a related question that also answers this.
In a sorted collection you most likely don't want to manipulate the internal data structure which is why PriorityQueue doesn't implement the List interface (because that would give you direct access to its elements).
Caveat on the PriorityQueue iterator
The PriorityQueue class implements the Iterable<E> and Collection<E> interfaces so it can be iterated as usual. However, the iterator is not guaranteed to return elements in the sorted order. Instead (as Alderath points out in the comments) you need to poll() the queue until empty.
Note that you can convert a list to a priority queue via the constructor that takes any collection:
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"
4. Write your own SortedList class
NOTE: You shouldn't have to do this.
You can write your own List class that sorts each time you add a new element. This can get rather computation heavy depending on your implementation and is pointless, unless you want to do it as an exercise, because of two main reasons:
It breaks the contract that List<E> interface has because the add methods should ensure that the element will reside in the index that the user specifies.
Why reinvent the wheel? You should be using the TreeSet or Multisets instead as pointed out in the first point above.
However, if you want to do it as an exercise here is a code sample to get you started, it uses the AbstractList abstract class:
public class SortedList<E> extends AbstractList<E> {
private ArrayList<E> internalList = new ArrayList<E>();
// Note that add(E e) in AbstractList is calling this one
#Override
public void add(int position, E e) {
internalList.add(e);
Collections.sort(internalList, null);
}
#Override
public E get(int i) {
return internalList.get(i);
}
#Override
public int size() {
return internalList.size();
}
}
Note that if you haven't overridden the methods you need, then the default implementations from AbstractList will throw UnsupportedOperationExceptions.
Because the concept of a List is incompatible with the concept of an automatically sorted collection. The point of a List is that after calling list.add(7, elem), a call to list.get(7) will return elem. With an auto-sorted list, the element could end up in an arbitrary position.
Since all lists are already "sorted" by the order the items were added (FIFO ordering), you can "resort" them with another order, including the natural ordering of elements, using java.util.Collections.sort().
EDIT:
Lists as data structures are based in what is interesting is the ordering in which the items where inserted.
Sets do not have that information.
If you want to order by adding time, use List. If you want to order by other criteria, use SortedSet.
Set and Map are non-linear data structure. List is linear data structure.
The tree data structure SortedSet and SortedMap interfaces implements TreeSet and TreeMap respectively using used Red-Black tree implementation algorithm. So it ensure that there are no duplicated items (or keys in case of Map).
List already maintains an ordered collection and index-based data structure, trees are no index-based data structures.
Tree by definition cannot contain duplicates.
In List we can have duplicates, so there is no TreeList(i.e. no SortedList).
List maintains elements in insertion order. So if we want to sort the list we have to use java.util.Collections.sort(). It sorts the specified list into ascending order, according to the natural ordering of its elements.
JavaFX SortedList
Though it took a while, Java 8 does have a sorted List.
http://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html
As you can see in the javadocs, it is part of the JavaFX collections, intended to provide a sorted view on an ObservableList.
Update: Note that with Java 11, the JavaFX toolkit has moved outside the JDK and is now a separate library. JavaFX 11 is available as a downloadable SDK or from MavenCentral. See https://openjfx.io
For any newcomers, as of April 2015, Android now has a SortedList class in the support library, designed specifically to work with RecyclerView. Here's the blog post about it.
Another point is the time complexity of insert operations.
For a list insert, one expects a complexity of O(1).
But this could not be guaranteed with a sorted list.
And the most important point is that lists assume nothing about their elements.
For example, you can make lists of things that do not implement equals or compare.
Think of it like this: the List interface has methods like add(int index, E element), set(int index, E element). The contract is that once you added an element at position X you will find it there unless you add or remove elements before it.
If any list implementation would store elements in some order other than based on the index, the above list methods would make no sense.
In case you are looking for a way to sort elements, but also be able to access them by index in an efficient way, you can do the following:
Use a random access list for storage (e.g. ArrayList)
Make sure it is always sorted
Then to add or remove an element you can use Collections.binarySearch to get the insertion / removal index. Since your list implements random access, you can efficiently modify the list with the determined index.
Example:
/**
* #deprecated
* Only for demonstration purposes. Implementation is incomplete and does not
* handle invalid arguments.
*/
#Deprecated
public class SortingList<E extends Comparable<E>> {
private ArrayList<E> delegate;
public SortingList() {
delegate = new ArrayList<>();
}
public void add(E e) {
int insertionIndex = Collections.binarySearch(delegate, e);
// < 0 if element is not in the list, see Collections.binarySearch
if (insertionIndex < 0) {
insertionIndex = -(insertionIndex + 1);
}
else {
// Insertion index is index of existing element, to add new element
// behind it increase index
insertionIndex++;
}
delegate.add(insertionIndex, e);
}
public void remove(E e) {
int index = Collections.binarySearch(delegate, e);
delegate.remove(index);
}
public E get(int index) {
return delegate.get(index);
}
}
(See a more complete implementation in this answer)
First line in the List API says it is an ordered collection (also known as a sequence). If you sort the list you can't maintain the order, so there is no TreeList in Java.
As API says Java List got inspired from Sequence and see the sequence properties http://en.wikipedia.org/wiki/Sequence_(mathematics)
It doesn't mean that you can't sort the list, but Java strict to his definition and doesn't provide sorted versions of lists by default.
I think all the above do not answer this question due to following reasons,
Since same functionality can be achieved by using other collections such as TreeSet, Collections, PriorityQueue..etc (but this is an alternative which will also impose their constraints i.e. Set will remove duplicate elements. Simply saying even if it does not impose any constraint, it does not answer the question why SortedList was not created by java community)
Since List elements do not implements compare/equals methods (This holds true for Set & Map also where in general items do not implement Comparable interface but when we need these items to be in sorted order & want to use TreeSet/TreeMap,items should implement Comparable interface)
Since List uses indexing & due to sorting it won't work (This can be easily handled introducing intermediate interface/abstract class)
but none has told the exact reason behind it & as I believe these kind of questions can be best answered by java community itself as it will have only one & specific answer but let me try my best to answer this as following,
As we know sorting is an expensive operation and there is a basic difference between List & Set/Map that List can have duplicates but Set/Map can not.
This is the core reason why we have got a default implementation for Set/Map in form of TreeSet/TreeMap. Internally this is a Red Black Tree with every operation (insert/delete/search) having the complexity of O(log N) where due to duplicates List could not fit in this data storage structure.
Now the question arises we could also choose a default sorting method for List also like MergeSort which is used by Collections.sort(list) method with the complexity of O(N log N). Community did not do this deliberately since we do have multiple choices for sorting algorithms for non distinct elements like QuickSort, ShellSort, RadixSort...etc. In future there can be more. Also sometimes same sorting algorithm performs differently depending on the data to be sorted. Therefore they wanted to keep this option open and left this on us to choose. This was not the case with Set/Map since O(log N) is the best sorting complexity.
https://github.com/geniot/indexed-tree-map
Consider using indexed-tree-map . It's an enhanced JDK's TreeSet that provides access to element by index and finding the index of an element without iteration or hidden underlying lists that back up the tree. The algorithm is based on updating weights of changed nodes every time there is a change.
We have Collections.sort(arr) method which can help to sort ArrayList arr. to get sorted in desc manner we can use Collections.sort(arr, Collections.reverseOrder())
In Java there are the SortedSet and SortedMap interfaces. Both belong to the Java Collections framework and provide a sorted way to access the elements.
However, in my understanding there is no SortedList in Java. You can use java.util.Collections.sort() to sort a list.
Any idea why it is designed like that?
List iterators guarantee first and foremost that you get the list's elements in the internal order of the list (aka. insertion order). More specifically it is in the order you've inserted the elements or on how you've manipulated the list. Sorting can be seen as a manipulation of the data structure, and there are several ways to sort the list.
I'll order the ways in the order of usefulness as I personally see it:
1. Consider using Set or Bag collections instead
NOTE: I put this option at the top because this is what you normally want to do anyway.
A sorted set automatically sorts the collection at insertion, meaning that it does the sorting while you add elements into the collection. It also means you don't need to manually sort it.
Furthermore if you are sure that you don't need to worry about (or have) duplicate elements then you can use the TreeSet<T> instead. It implements SortedSet and NavigableSet interfaces and works as you'd probably expect from a list:
TreeSet<String> set = new TreeSet<String>();
set.add("lol");
set.add("cat");
// automatically sorts natural order when adding
for (String s : set) {
System.out.println(s);
}
// Prints out "cat" and "lol"
If you don't want the natural ordering you can use the constructor parameter that takes a Comparator<T>.
Alternatively, you can use Multisets (also known as Bags), that is a Set that allows duplicate elements, instead and there are third-party implementations of them. Most notably from the Guava libraries there is a TreeMultiset, that works a lot like the TreeSet.
2. Sort your list with Collections.sort()
As mentioned above, sorting of Lists is a manipulation of the data structure. So for situations where you need "one source of truth" that will be sorted in a variety of ways then sorting it manually is the way to go.
You can sort your list with the java.util.Collections.sort() method. Here is a code sample on how:
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
Collections.sort(strings);
for (String s : strings) {
System.out.println(s);
}
// Prints out "cat" and "lol"
Using comparators
One clear benefit is that you may use Comparator in the sort method. Java also provides some implementations for the Comparator such as the Collator which is useful for locale sensitive sorting strings. Here is one example:
Collator usCollator = Collator.getInstance(Locale.US);
usCollator.setStrength(Collator.PRIMARY); // ignores casing
Collections.sort(strings, usCollator);
Sorting in concurrent environments
Do note though that using the sort method is not friendly in concurrent environments, since the collection instance will be manipulated, and you should consider using immutable collections instead. This is something Guava provides in the Ordering class and is a simple one-liner:
List<string> sorted = Ordering.natural().sortedCopy(strings);
3. Wrap your list with java.util.PriorityQueue
Though there is no sorted list in Java there is however a sorted queue which would probably work just as well for you. It is the java.util.PriorityQueue class.
Nico Haase linked in the comments to a related question that also answers this.
In a sorted collection you most likely don't want to manipulate the internal data structure which is why PriorityQueue doesn't implement the List interface (because that would give you direct access to its elements).
Caveat on the PriorityQueue iterator
The PriorityQueue class implements the Iterable<E> and Collection<E> interfaces so it can be iterated as usual. However, the iterator is not guaranteed to return elements in the sorted order. Instead (as Alderath points out in the comments) you need to poll() the queue until empty.
Note that you can convert a list to a priority queue via the constructor that takes any collection:
List<String> strings = new ArrayList<String>()
strings.add("lol");
strings.add("cat");
PriorityQueue<String> sortedStrings = new PriorityQueue(strings);
while(!sortedStrings.isEmpty()) {
System.out.println(sortedStrings.poll());
}
// Prints out "cat" and "lol"
4. Write your own SortedList class
NOTE: You shouldn't have to do this.
You can write your own List class that sorts each time you add a new element. This can get rather computation heavy depending on your implementation and is pointless, unless you want to do it as an exercise, because of two main reasons:
It breaks the contract that List<E> interface has because the add methods should ensure that the element will reside in the index that the user specifies.
Why reinvent the wheel? You should be using the TreeSet or Multisets instead as pointed out in the first point above.
However, if you want to do it as an exercise here is a code sample to get you started, it uses the AbstractList abstract class:
public class SortedList<E> extends AbstractList<E> {
private ArrayList<E> internalList = new ArrayList<E>();
// Note that add(E e) in AbstractList is calling this one
#Override
public void add(int position, E e) {
internalList.add(e);
Collections.sort(internalList, null);
}
#Override
public E get(int i) {
return internalList.get(i);
}
#Override
public int size() {
return internalList.size();
}
}
Note that if you haven't overridden the methods you need, then the default implementations from AbstractList will throw UnsupportedOperationExceptions.
Because the concept of a List is incompatible with the concept of an automatically sorted collection. The point of a List is that after calling list.add(7, elem), a call to list.get(7) will return elem. With an auto-sorted list, the element could end up in an arbitrary position.
Since all lists are already "sorted" by the order the items were added (FIFO ordering), you can "resort" them with another order, including the natural ordering of elements, using java.util.Collections.sort().
EDIT:
Lists as data structures are based in what is interesting is the ordering in which the items where inserted.
Sets do not have that information.
If you want to order by adding time, use List. If you want to order by other criteria, use SortedSet.
Set and Map are non-linear data structure. List is linear data structure.
The tree data structure SortedSet and SortedMap interfaces implements TreeSet and TreeMap respectively using used Red-Black tree implementation algorithm. So it ensure that there are no duplicated items (or keys in case of Map).
List already maintains an ordered collection and index-based data structure, trees are no index-based data structures.
Tree by definition cannot contain duplicates.
In List we can have duplicates, so there is no TreeList(i.e. no SortedList).
List maintains elements in insertion order. So if we want to sort the list we have to use java.util.Collections.sort(). It sorts the specified list into ascending order, according to the natural ordering of its elements.
JavaFX SortedList
Though it took a while, Java 8 does have a sorted List.
http://docs.oracle.com/javase/8/javafx/api/javafx/collections/transformation/SortedList.html
As you can see in the javadocs, it is part of the JavaFX collections, intended to provide a sorted view on an ObservableList.
Update: Note that with Java 11, the JavaFX toolkit has moved outside the JDK and is now a separate library. JavaFX 11 is available as a downloadable SDK or from MavenCentral. See https://openjfx.io
For any newcomers, as of April 2015, Android now has a SortedList class in the support library, designed specifically to work with RecyclerView. Here's the blog post about it.
Another point is the time complexity of insert operations.
For a list insert, one expects a complexity of O(1).
But this could not be guaranteed with a sorted list.
And the most important point is that lists assume nothing about their elements.
For example, you can make lists of things that do not implement equals or compare.
Think of it like this: the List interface has methods like add(int index, E element), set(int index, E element). The contract is that once you added an element at position X you will find it there unless you add or remove elements before it.
If any list implementation would store elements in some order other than based on the index, the above list methods would make no sense.
In case you are looking for a way to sort elements, but also be able to access them by index in an efficient way, you can do the following:
Use a random access list for storage (e.g. ArrayList)
Make sure it is always sorted
Then to add or remove an element you can use Collections.binarySearch to get the insertion / removal index. Since your list implements random access, you can efficiently modify the list with the determined index.
Example:
/**
* #deprecated
* Only for demonstration purposes. Implementation is incomplete and does not
* handle invalid arguments.
*/
#Deprecated
public class SortingList<E extends Comparable<E>> {
private ArrayList<E> delegate;
public SortingList() {
delegate = new ArrayList<>();
}
public void add(E e) {
int insertionIndex = Collections.binarySearch(delegate, e);
// < 0 if element is not in the list, see Collections.binarySearch
if (insertionIndex < 0) {
insertionIndex = -(insertionIndex + 1);
}
else {
// Insertion index is index of existing element, to add new element
// behind it increase index
insertionIndex++;
}
delegate.add(insertionIndex, e);
}
public void remove(E e) {
int index = Collections.binarySearch(delegate, e);
delegate.remove(index);
}
public E get(int index) {
return delegate.get(index);
}
}
(See a more complete implementation in this answer)
First line in the List API says it is an ordered collection (also known as a sequence). If you sort the list you can't maintain the order, so there is no TreeList in Java.
As API says Java List got inspired from Sequence and see the sequence properties http://en.wikipedia.org/wiki/Sequence_(mathematics)
It doesn't mean that you can't sort the list, but Java strict to his definition and doesn't provide sorted versions of lists by default.
I think all the above do not answer this question due to following reasons,
Since same functionality can be achieved by using other collections such as TreeSet, Collections, PriorityQueue..etc (but this is an alternative which will also impose their constraints i.e. Set will remove duplicate elements. Simply saying even if it does not impose any constraint, it does not answer the question why SortedList was not created by java community)
Since List elements do not implements compare/equals methods (This holds true for Set & Map also where in general items do not implement Comparable interface but when we need these items to be in sorted order & want to use TreeSet/TreeMap,items should implement Comparable interface)
Since List uses indexing & due to sorting it won't work (This can be easily handled introducing intermediate interface/abstract class)
but none has told the exact reason behind it & as I believe these kind of questions can be best answered by java community itself as it will have only one & specific answer but let me try my best to answer this as following,
As we know sorting is an expensive operation and there is a basic difference between List & Set/Map that List can have duplicates but Set/Map can not.
This is the core reason why we have got a default implementation for Set/Map in form of TreeSet/TreeMap. Internally this is a Red Black Tree with every operation (insert/delete/search) having the complexity of O(log N) where due to duplicates List could not fit in this data storage structure.
Now the question arises we could also choose a default sorting method for List also like MergeSort which is used by Collections.sort(list) method with the complexity of O(N log N). Community did not do this deliberately since we do have multiple choices for sorting algorithms for non distinct elements like QuickSort, ShellSort, RadixSort...etc. In future there can be more. Also sometimes same sorting algorithm performs differently depending on the data to be sorted. Therefore they wanted to keep this option open and left this on us to choose. This was not the case with Set/Map since O(log N) is the best sorting complexity.
https://github.com/geniot/indexed-tree-map
Consider using indexed-tree-map . It's an enhanced JDK's TreeSet that provides access to element by index and finding the index of an element without iteration or hidden underlying lists that back up the tree. The algorithm is based on updating weights of changed nodes every time there is a change.
We have Collections.sort(arr) method which can help to sort ArrayList arr. to get sorted in desc manner we can use Collections.sort(arr, Collections.reverseOrder())
Java offers us Collections, where every option is best used in a certain scenario.
But what would be a good solution for the combination of following tasks:
Quickly iterate through every element in the list (order does not matter)
Check if the list contains (a) certain element(s)
Some options that were considered which may or may not be good practice:
It could be possible to, for example, first use a LinkedList, and
then convert it to a HashSet when the amount of elements
is unknown in advance (and if duplicates will not be present)
Pick a solution for one of both tasks and use the same implementation for the other task (if switching to another implementation is not worth it)
Perhaps some implementation exists that does both (failed to find one)
Is there a 'best' solution to this, and if so, what is it?
EDIT: For potential future visitors, this page contains many implementations with big O runtimes.
A HashSet can be iterated through quickly and provides efficient lookups.
HashSet<Object> set = new HashSet<>();
set.add("Hello");
for (Object obj : set) {
System.out.println(obj);
}
if (set.contains("Hello")) {
System.out.println("Found");
}
Quickly iterate through every element in the list (order does not matter)
It the order does not matter, you should go with a Collection implementation with a time complexity of O(n), since each of them is implementing Iterable and if you want to iterate over each element, you have to visit each element at least once (hence there is nothing better than O(n)). Practically, of course, one implementation is more suited compared to another one, since more often you have multiple considerations to take into account.
Check if the list contains (a) certain element(s)
This is typically the user case for a Set, you will have much better time complexity for contains operations. One thing to note here is that a Set does not have a predefined order when iterating over elements. It can change between implementations and it is risky to make assumptions about it.
Now to your question:
From my perspective, if you have the choice to choose the data structure of a class yourself, go with the most natural one for that use case. If you can imagine that you have to call contains a lot, then a Set might be suited for your use case. You can also use a List and each time you need to call contains (multiple times) you can create a Set with all elements from the List before. Of course, if you call this method often, it would be expensive to create the Set for each invocation. You may use a Set in the first place.
Your comment stated that you have a world of players and you want to check if a player is part of a certain world object. Since the world owns the players, it should also contain a Collection of some kind to store them. Now, in this case i would recommend a Map with a common identifier of the player as key, and the player itself as value.
public class World {
private Map<String, Player> players = new HashMap<>();
public Collection<Player> getPlayers() { ... }
public Optional<Player> getPlayer(String nickname) { ... }
// ...
}
I am developing an application where as a background I need to monitor the user activity on particular objects and later when they are visualized they need to be sorted based on the order of which the user used them ( the last used object must be visualized on the first row of a grid for example.)
So if I have an ArrayList where I store the objects which the user is dealing with in order to add the last used object I need to check if it is already in the list and then move it at the first position. If the object is not there I simply add it at the first position of the list.
So instead of doing all these steps I want to make my own list where the logic explained above will be available.
My question is which scenario is better:
Implement the list interface
Extend the ArrayList class and override the ADD method
Create a class that contains an ArrayList and handles any additional functionality.
I.e. prefer composition over inheritance (and in this case, implementing an interface). It's also possible to have that class implement List for relevant cases and just direct the (relevant) operations to the ArrayList inside.
Also note that LinkedHashMap supports insertion order (default) and access order for iteration, if you don't need a List (or if you can suitably replace it with a Map).
So instead of doing all these steps i want to make my own list where
the logic explained above will be available.
I would try to refactor your design parameters (if you can) in order to be able to use the existing Java Collection Framework classes (perhaps a linked collection type). As a part of the Collections Framework, these have been optimized and maintained for years (so efficiency is likely already nearly optimal), and you won't have to worry about maintaining it yourself.
Of the two options you give, it is possible that neither is the easiest or best.
It doesn't sound like you'll be able to extend AbstractList (as a way of implementing List) so you'll have a lot of wheel reinvention to do.
The ArrayList class is not final, but not expressly designed and documented for inheritance. This can result in some code fragility as inheritance breaks encapsulation (discussed in Effective Java, 2nd Ed. by J. Bloch). This solution may not be the best way to go.
Of the options, if you can't refactor your design to allow use of the Collection classes directly, then write a class that encapsulates a List (or other Collection) as an instance field and add instrumentation to it. Favor composition over inheritance. In this way, your solution will be more robust and easier to maintain than a solution based on inheritance.
I think LinkedHashMap already does what you need - it keeps the elements in the order they were inserted or last accessed (this is determined by the parameter accessOrder in one of the constructors).
https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html
EDIT
I don't have enough reputation to comment, so I'm putting it here: You don't actually need a map, so Venkatesh's LinkedHashSet suggestion is better.
You can do something like this:
<T> void update(Set<T> set, T value) {
set.remove(value);
set.add(value);
}
and then
LinkedHashSet<String> set = new LinkedHashSet<>();
update(set, "a");
update(set, "b");
update(set, "c");
update(set, "a");
Iterator<String> it = new LinkedList<String>(set).descendingIterator();
while (it.hasNext()) {
System.out.println(it.next());
}
Output:
a
c
b
You might try using HashMap<Integer, TrackedObject> where TrackedObject is the class of the Object you're keep track of.
When your user uses an object, do
void trackObject(TrackedObject object)
{
int x = hashMap.size();
hashMap.add(Integer.valueOf(x), object);
}
then when you want to read out the tracked objects in order of use:
TrackedObject[] getOrderedArray()
{
TrackedObject[] array = new TrackedObject[hashMap.size()];
for(int i = 0; i < hashMap.size(); i++)
{
array[i] = hashMap.get(Integer.valueOf(i));
}
return array;
}
A LinkedHashSet Also can be helpful in your case. You can keep on adding elements to it, it will keep them in insertion order and also will maintain only unique values.
Here is my problem (simplified):
Suppose we have a class:
public class MyClass{
String name;
Double amount;
String otherAttribute;
}
And a List<MyClass> myList
Suppose we have 2 elements from myList. Let's say object1 and object2
What I would like to do is:
if (object1.name.equals(object2.name){
//add amount of object2 to object1
//remove object 2 from the list
}
Considering I have a large list (maybe 100 elements) and I would like to find the best and less consuming way to do what I want.
What would you suggest ?
EDIT:
Yes 100 items is not large, but I would call this method (of merging similar objects) many times for many different sized lists. So that's way I would like to find the best practice for this.
I can't override equals or hashCode methods of MyClass, unfortunately (client requirement)
I'd add the objects to a HashMap where the name is the key and MyClass is the value being stored. Loop through each object in your list to add them to the map. If the name isn't in the map, just add the name, object pair. If it is already in the map, add the amount to the object already stored. When the loop completes, extract the objects from the map.
100 elements is a tiny size for a list, considering you're not going to repeat the operation some hundreds of thousands times. If it's the case, I'd consider creating a data structure indexing the list items by the search property (Map for instance), or ordering it if suitable and using an efficient search algorithm.
One approach (as suggested by Bill) would be to traverse the List adding every element to a Map, with the name property as key. You can take advantage of put's return to know if a name has been previously put into the map, and add the previosuly accumulated amounts in the current element. Finally, you could use values() to get the List without duplicates.
For instance:
List<MyClass> l;
Map<String, Myclass> m = new HashMap<MyClass>();
for (MyClass elem : l) {
MyClass oldElem = m.put(elem.getName(), elem);
if (oldElem != null) {
elem.setAmount(elem.getAmount() + oldElem.getAmount());
}
}
l = new ArrayList<MyClass>(m.values());
If you need to preserve order in the list, consider using a LinkedHashMap.
This is an O(n^2) problem unfortunately. You need to compare n elements to n-1 other elements. There is no way to do this but to brute force it.
If you used a HashMap however, you could check the map for an element before adding it to the Map which is an O(1) operation. It would look something like this:
HashMap<String, MyClass> map = new HashMap<String, MyClass>();
when you add an element:
if (map.get(obj1.name) != null) {
var obj2 = map.get(obj1.name);
obj2.amount = obj2.amount + obj1.amount;
map.put(obj1.name, obj2);
}
'Large' is relative, 100 items is definitely not large, imagine if you had to process a stream of 1.000.000 items/second. Then you would redefine large :D
In your example, what I think would be good to avoid would be to create a Set of your items' names. Searching a java HashSet takes O(1), so if an objects' name exists in the hash set, then update it on the list. An even better solution would be to create a HashMap, on which you could say e.g.
if(mymap.contains(thename)){
mymap.put(thename, newSum);
}
this being an example of how you could use it. Here's a link to get you started: http://java67.blogspot.gr/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html
I suggest to optimize (if possible) by not even doing the .add() to the list if an element with the same name exists. Using one of the hash based collections in combination with a proper equals() & hashCode() implementation based on MyClass.name should also give you somewhat good performance.
First, since you cannot override equals or hashCode, then you need to have the function that will do this functionality in the same package as your MyClass class, since no accessor methods are defined in MyClass
Second, try to have your items in a LinkedList, so that you can remove repeating elements from that list really quick without having to move around the other items.
Use a map to keep track of the amount that corresponds to a given name, while iterating the list, and removing repeating elements at the same time. In this way you don't have to create a new list.
List<MyClass> myClass_l;
Map<String, MyClass> nameMyClass_m = new HashMap<String, MyClass>();
for (Iterator<MyClass> iterator = myClass_l.iterator(); iterator.hasNext(){
MyClass m = iterator.next();
if (nameAmount_m.contains(m.name)){
MyClass firstClass = m.get(m.name);
firstClass.amount += m.amount;
iterator.remove();
}
else{
nameMyClass_m.put(m.name, m);
}
}
By the time you have finished the loop, you will have the items you want in your original list.