Java Collection order by different parameters - java

I have a List that contains objects like an adress - e.g. city, street, name.
And always need receive 3 lists: first ordered by city, second ordered by street, third ordered by name. It is clear that possible order consequentially the same list. But it consumes many time.
Is it possible to create something like Iterator that can depend from parameter return all members of collection in corresponding order? Or exists another solution?
Thanks.

Not with the standard java collection framework. What you are asking to work, you have to build different search trees that operate on same collection.
It is like having indices on different columns in DB

You can build 3 Comparators- 1 based on each criteria item, and then call Collections.sort(List, Comparator) to get each sorting.
If you're doing this numerous times on the same list, make 3 copies.
Note that the iterator idea wouldn't really be terribly efficient as it would need some way to trace through the list that would be at least as ugly as doing the sort. That is, the iterator would need to know which item comes next, in some way other than the natural order of the list. It's just as easy to re-sort the list as try to navigate it out of natural order.

Related

Data structure to represent Teams and Players

There is a Team object , that contains list of players List<Players>. All teams need to be stored in a Teams collection.
Conditions:
If a new player need to be added to a particular team , that particular team is retrieved from Teams and Player need to be added to Players list of that team
Each Team object in the collection Teams need to be unique based on the team name
Team objects in the collection need to be sorted based on team name.
Considerations:
In this scenario when I use List<Team> , I can achieve 1, 3 . But uniqueness cannot be satisfied.
If I use TreeSet<Team> 2,3 can be achieved. But as there is no get method on TreeSet , a particular team cannot be selected
So I ended up using TreeMap<teamName,Team>. This makes all 1,2,3 possible. But I think it's not the good way to do it
Which Data Structure is ideal for this use case? Preferably form Java collections.
You can utilize your TreeSet if you wish. However, if you're going to utilize the Set interface you can use remove(object o) instead of get. You'll remove the object, make your modifications, then add it back into the set.
I think extending (i.e. creating a subclass from) ArrayList or LinkedList and overriding the set(), add(), addAll(), remove(), and removeRange() methods in such way that they ensure the uniqueness and sortedness conditions (invariants) would be a very clean design. You can also implement a binary search method in your class to quickly find a team with a given name.
ArrayList is a better choice to base your class on, if you aren't going to add or remove teams too frequently. ArrayList would give you O(n) insertion and removal, but O(log n) cost for element access and ensuring uniqueness if you use binary search (where n is the number of elements in the array).
See the generics tutorial for subclassing generics.
How about using a Guava's MultiMap? More precisely, a SetMultimap. Specifically, a SortedSetMultimap. Even more specifically, its TreeMultimap implementation (1).
Explanations:
In a MultiMap, a Key points not to a single value, but rather to a Collection of values.
This means you can bind to a single Team key a collection of several Player values, so that's Req1 solved.
In a SetMultiMap, the Keys are unique.
This gets your Req2 solved.
In a SortedSetMultimap, the Valuess are also sorted.
While you don't specifically care for this, it's nice to have.
In a TreeMultimap, The Keyset and each of their Values collections are Sorted.
This gets your Req3 sorted (See what I did there?)
Usage:
TreeMultimap<Team, Player> ownership = new TreeMultimap<Team, Player>();
ownership.put(team1, playerA);
ownership.put(team1, playerB);
ownership.put(team2, playerC);
Collection<Player> playersOfTeamA = ownership.get(team1); // contains playerA, playerB
SortedSet<Team> allTeams = ownership.keySet(); // contains team1, team2
Gothas:
Remember to set equals and hashCode correctly on your Team object to use its name.
Alternatively, you could use the static create(Comparator<? super K> keyComparator, Comparator<? super V> valueComparator) which provides a purpose-built comparison if you do not wish to change the natural ordering of Team. (use Ordering.natural() for the Player comparator to keep its natural ordering - another nice Guava thing). In any case, make sure it is compatible with equals!
MultiMaps are not Maps because puting a new value to a key does not remove the previously held value (that's the whole point), so make sure you understand it. (for instance it still hold that you cannot put a key-value pair twice...)
(1): I am unsure wether SortedSetMultimap is sufficient. In its Javadoc, it states the Values are sorted, but nothing is said of the keys. Does anyone know any better?
(2) I assure you, I'm not affiliated to Guava in any way. I just find it awesome!

ArrayList - Creating comparators

I created an ArrayList that contains references to objects that contain various amounts of data.
I wish to sort the object by certain elements of their data.
I successfully did so using the collections.sort along with an anonymous class defining the new comparator.
I was successful in each of the new comparators I created which resulted in correct sorted order.
For the same ArrayList how could you make a comparator that would give the original input of the ArrayList order. I know that the ArrayList saves the order of the objects they are added, insertion order. But after sorting the ArrayList with other comparators, how would I then sort the ArrayList back to the original order that it was in? This is the one thing that is stumping me, and I can't seem to figure it out.
Edit: I should clarify this is for an assignment, in which the instructions say to include a default/original comparator to get the unsorted input. That just doesn't make sense to me, given what we mentioned. The ArrayList will save the insertion order when it is added, but when you sort that it is lost. i don't see how you could create a comparator that will sort what the original list was.
Edit2: I am given an ArrayList which is expected to store a few objects that contain some data elements which will be sorted, such as by their names or their age. This comparators were easy to make and I do so successfully. The assignment also wants to an original comparator that display the data as it was inserted. Now these comparators are being passed in a method call, in which that method will use the collections.sort on each different comparator. It is asking for the original comparator to be used first though.
The idea of the original comparator at all seems illogical. The fact that is being used first, so no other sorting has been done yet makes it seem that it was only made so it fits the argument list of calling the method to display the data. So in other words, I guess the original comparator should just return nothing..?
It is asking for the original comparator to be used first though.
This means that the comparator doesn't need to restore the original order, which is what wouldn't make any sense (impossible), but to do nothing when used to sort, i.e. leave the original order unchanged.
Quoting javadoc of sort():
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
This means that if you pass in a comparator that claims all objects to be equal, no reordered will take place.
// Java 8 example using lambda expression
Collections.sort(list, (a,b) -> 0); // No-op. All elements compare equal
Aside from making a clone (commented above) would be for the object to contain a separate field that tracks order inserted into the array and write a comparator for that.

Filter a list in Java

I have a list of people with additional information - let's say book rentals. So each Rental-object in my list would include a Person-object as an attribute and some rental information. For each person there will be 1..n entries in the list.
Now I need to filter this list based on some criteria. If ONE of the entries matches a certain criteria, I want to delete ALL entries for that person, even if the other entries do not match the criteria.
Is there a nice way to do this in one filter? Alternative would be to scan the list, identify people whose entries should be removed and then apply something like
Collections2.filter(myList, new MyPredicate(peopleIWantToRemove))
but I would like to do it with just one-time-list-traversal. How can I do this?
Going through the list twice (once to determine all the "bad" people, a second time to remove them) is, from an algorithm run-time standpoint, perfectly harmless. Whether you go through the list once or twice, the algorithm runs in O(n) time.
However, if you're really serious about only going through the list once, you could construct a temporary data structure that keeps track of all the places each person appears alongside a list of the people you want to get rid of.
Map<Person, List<Rental>> rentalsPerPerson;
List<People> badPeople;
When you parse the list the first time, you populate these two structures. Then you go through the list of badPeople, pull out their list of Rental objects, and purge those one at a time from your original list.
But honestly, that feels like a lot of bother for not much gain.
The way I'd recommend doing it? Go through the list twice. First pass: Compile a Set of bad people. Second pass: Create a new output List, initially empty. Go through your original List. For each element, if the Person isn't in the Bad set, add the entry to your output List.
Make the predicate stateful so that it not only matches if some criterion is met, but also if the person is known as a "bad" person.
Use Iterables.filter() instead. Per the class docs: "Unless otherwise noted, all of the iterables produced in this class are lazy, which means that their iterators only advance the backing iteration when absolutely necessary." That means you can compose multiple filters, but the traversal only happens once, when you do it yourself.
Sort list based on bad people criteria (bad entry first), and then filter using stateful predicate as C-Otto said
Now I need to filter this list based on some criteria. If ONE of the
entries matches a certain criteria, I want to delete ALL entries for
that person, even if the other entries do not match the criteria.
I think you were not doing filtering. because filtering return a subCollection (filtered) against on some condition. You are gonna always get the same Person List but with some element changed.
You want something like python's map(list, function). going through the list, for each person do something.
Guava's collections.transform can do it.
public static <F,T> Collection<T> transform(Collection<F> fromCollection,
Function<? super F,T> function)
check it out:
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection, com.google.common.base.Function)

LinkedHashSet or ArrayList

I wish to
Avoid duplicated item being inserted.
When I iterate through the collection class, the returned item is same as insertion order.
May I know, what thing I should consider, to choose either ArrayList (explicitly perform contains check before insertion) or LinkedHashSet?
Thanks.
Definitely use LinkedHashSet. It is made for what you need. Searching entire ArrayList every time you need to insert something will be performance killer (O(n) every time))
Use LinkedHashSet if you don't want duplicate items inserted.
A LinkedHashSet seems to fit the bill perfectly.
When you build your own objects, and plan to use them in a Collection like LinkedHashSet here. Don't forget to override both equals and hashcode for the item you are going to store in it.
Please check this out:
http://wiki3.cosc.canterbury.ac.nz/images/e/e9/JavaCollections.png
LinkedHashSet is what you need, because it's an implementation of the Set interface. Set has one very cool habit: it doesn't allows duplicates by default. So, we are done with your 1.
What about 2?
We know, that we need one of the Set implementation, but which ?
HashMap - you are able to store K,V pairs, but there is no order.
TreeSet - this is the slowest solution, because it's using a compareTo method to keep every item sorted and ordered. This is why you can pass a comparator to it, when you are constructing a TreeSet.
LinkedHashSet - Gives back the elements in order of INSERTING them. It is the ordered version of a HashSet.
Please find a cool description here:
http://java67.blogspot.co.uk/2014/01/when-to-use-linkedhashset-vs-treeset-vs-hashset-java.html?_sm_au_=iVVMtMLHSDQ5P0P7

Extract elements from list based on object property type

Often, I have a list of objects. Each object has properties. I want to extract a subset of the list where a specific property has a predefined value.
Example:
I have a list of User objects. A User has a homeTown. I want to extract all users from my list with "Springfield" as their homeTown.
I normally see this accomplished as follows:
List users = getTheUsers();
List returnList = new ArrayList();
for (User user: users) {
if ("springfield".equalsIgnoreCase(user.getHomeTown())
returnList.add(user);
}
I am not particularly satisfied with this solution. Yes, it works, but it seems so slow. There must be a non-linear solution.
Suggestions?
Well, this operation is linear in nature unless you do something extreme like index the collection based on properties you expect to examine in this way. Short of that, you're just going to have to look at each object in the collection.
But there may be some things you can do to improve readability. For example, Groovy provides an each() method for collections. It would allow you to do something like this...
def returnList = new ArrayList();
users.each() {
if ("springfield".equalsIgnoreCase(it.getHomeTown())
returnList.add(user);
};
You will need a custom solution for this. Create a custom collection such that it implements List interface and add all elements from original list into this list.
Internally in this custom List class you need to maintain some collections of Map of all attributes which can help you lookup values as you need. To populate this Map you will have to use introspection to find list of all fields and their values.
This custom object will have to implement some methods as List findAllBy(String propertyName, String propertyValue); that will use above hash map to look up those values.
This is not an easy straightforward solution. Further more you will need to consider nested attributes like "user.address.city". Making this custom List immutable will help a lot.
However even if you are iterating list of 1000's of objects in List, still it will be faster so you are better off iterating List for what you need.
As I have found out, if you are using a list, you have to iterate. Whether its a for-each, lambda, or a FindAll - it is still being iterated. No matter how you dress up a duck, it's still a duck. As far as I know there are HashTables, Dictionaries, and DataTables that do not require iteration to find a value. I am not sure what the Java equivalent implementations are, but maybe this will give you some other ideas.
If you are really interested in performance here, I would also suggest a custom solution. My suggestion would be to create a Tree of Lists in which you can sort the elements.
If you are not interested about the ordering of the elements inside your list (and most people are usually not), you could also use a TreeMap (or HashMap) and use the homeTown as key and a List of all entries as value. If you add new elements, just look up the belonging list in the Map and append it (if it is the first element of course you need to create the list first). If you want to delete an element simply do the same.
In the case you want a list of all users with a given homeTown you just need to look up that list in the Map and return it (no copying of elements needed), I am not 100% sure about the Map implementations in Java, but the complete method should be in constant time (worst case logarithmic, depending on the Map implementation).
I ended up using Predicates. Its readability looks similar to Drew's suggestion.
As far as performance is concerned, I found negligible speed improvements for small (< 100 items) lists. For larger lists (5k-10k), I found 20-30% improvements. Medium lists had benefits but not quite as large as bigger lists. I did not test super large lists, but my testing made it seem the large the list the better the results in comparison to the foreach process.

Categories