Maintaining order of items in java - java

What is the best way to do the following(make sure that items from List are following the same order as those in ListTwo):
List
harry~20
marry~22
peter~40
jerry~33
janice~20
ListTwo
harry
marry
peter
janice
Now the result should look like this
ListThree
harry
marry
peter
janice
jerry
Step by step :
For each item in List :
compare first part of the item to item in ListTwo
if they are equal add it to ListThree
if item exist in List but not in ListTwo dont do anything yet save it
somewhere
continue from step 1
you are at the end of the List add the item(s) you skipped before in step
3
I know this much(actually I don't, I think I know), there are better ways to do this I'm sure
Why did I get downvote, did I miss something ?

It may be easier if you reverse the roles (store the keys in the ArrayList, in order) and the key-value mappings in a SortedMap, such as TreeMap, or ConcurrentSkipListMap. The comparator for the sorted map can use List.indexOf as the basis for element comparison.
With this arrangement, the map defines the key/value mapping, which is natural for the map, and the list maintains the desired order, which is quite natural for a List.
Alternatively, use a regular Map, and not a sorted map, and use iteration over the list, and fetching values from the map. E.g.
ArrayList keysList;
Map keyValues;
for(String key: keysList) {
String value = keyValues.get(key);
}
EDIT: Commons collections has SetUniqueList - a list that ensures uniqueness like a Set. It also has has various types of OrderedMap, in particular a ListOrderedMap that maintains the key/value mappings in the order of a list. For generics support, see commons collections with generics.

Use LinkedHashMap
You can call something like
map.put(one,value1);
and later call
map.get(one);
which will return value1
also a hash map does not accept duplicate key, so if you call
map.put(one,value2);
after this the original value is replaced.
you can use
map.containsKey(one)
to check whether one already exists as a key

If you are only comparing the keys of element then you can store them in LinkedHashSet and use the contains method of linkedHashset to check whether the element exists in constant time O(1).
LinkeHashMap also serves the purpose, however it requires extra space to store the value and this is not required we are only interested in keys.
Refer : http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashSet.html

Related

I need to keep a collection of objects sorted by a "ranking" attribute even if I get more objects that are already in the collection

I'm programming an algorythm which is going to receive rows from a database, those rows will be defined within an object that has attributes that identify them and a "ranking" attribute. I have to use a collection (or find a way) to keep all those objects sorted by the ranking value, HOWEVER if I receive another object that is equal to other already in the collection (except for the ranking), I need to update the ranking value (adding up both objects' rankings) and keep the collection sorted.
I was thinking about a TreeSet, but there's no way I can update a value that is not on the root...
Okay let's say my collection is like:
(name='Federer', id='131', ranking='3000')
(name='Nadal', id='234', ranking='2500')
(name='Del Potro', id='180', ranking='1800')
If I receive this:
(name='Nadal', id='234', ranking='1000')
The collection should end up like this:
(name='Nadal', id='234', ranking='3500')
(name='Federer', id='131', ranking='3000')
(name='Del Potro', id='180', ranking='1800')
Thanks a lot in advance.
I've done some experimenting with TreeSet and TreeMap, but couldn't really find anything interesting for your situation. Depending on how often you add elements to the collection, it might be best to just use a HashMap and sort it when necessary.
For it to be more efficient, you could even keep track of some boolean flag that denotes that the HashMap is in a sorted state (if the Map is already sorted, then there's no need to sort it again if nothing has changed!).
var map = new HashMap<Element, Integer>();
map.put(new Element("Federer", 131), 3000);
map.put(new Element("Nadal", 234), 2500);
map.put(new Element("Del Potro", 180), 1800);
map.forEach((k, v) -> System.out.println(k + "=" + v));
System.out.println();
map.merge(new Element("Nadal", 234), 1000, Math::addExact);
map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEach(System.out::println);
Output:
[Federer, 131]=3000
[Nadal, 234]=2500
[Del Potro, 180]=1800
[Nadal, 234]=3500
[Federer, 131]=3000
[Del Potro, 180]=1800
Note: I defined a class Element with a name and id field and use those fields when overriding Object#equals and Object#hashCode.
You said in the comments that your teacher recommended trees, but I don't see how you could use TreeSet or TreeMap classes for that (at least as they are).
TreeMap keeps the ordering by keys (which in your case are ids),
whereas you want to order the entries based on their rankings.
TreeSet, on the other hand, needs some property that would help it
compare two entries against each other, to only keep the ones that
are unique (since it is a set). If you want to compare based on
rankings, then if two rankings are equal, the treeset will think that the
two entries are equivalent (which might not be true). If you compare
based on ids, then it will be sorted according to the ids and not
rankings.
I think the easiest for you will be to store entries in a HashMap. And then when you need a sorted list, you can call values() on the hashmap, sort them and then display. You also mentioned that the number of entries won't surpass 300, so sorting should be very fast anyways.
You can keep a live map of ranking by ID, and use that as the basis for a priority queue to keep items sorted:
Map<Integer, Integer> rankingById = new HashMap<>();
Queue<Integer> idsByRanking = new PriorityQueue<>(
Comparator.comparing(rankingById::get).reversed());
void addItem(Item item) {
Integer id = item.getId();
idsByRanking.remove(id);
rankingById.merge(id, item.getRanking(), Integer::sum);
idsByRanking.add(id);
}
It's necessary to remove the item before updating the ranking so it can be reinserted at the correct position. See also: Updating Java PriorityQueue when its elements change priority

Most suitable Java Collection for duplicate elements

I'm currently working on one project. I am trying to create a management system for a store. The problem is I need some sort of collection for stock, where while adding elements that already exist with the same name (updating the number of same item) it's state (attribute stockLevel) would update accordingly to the given quantity. Does anyone know which should be the best to let do such a thing? Map, Set or List?
That would a multiset (a.k.a. a bag). You can easily roll your own implementation as a Map<Item, Integer>, or use one of the existing classes, e.g. Google Guava's Multiset. Apache Commons Collections also have one, so do Eclipse Collections etc.
where while adding elements that already exist with the same name
(updating the number of same item) it's state (attribute stockLevel)
would update accordingly to the given quantity
In your usecase, Map is good to store the information :
Map<Product, StockLevel> for representing a map with as key Product and as value the number of it in stocks : StockLevel
For iterating on a Map, you could not keep the insertion order with a HashMap but for your need, it should not be a problem.
Nevertheless, if you want to keep the insertion order when you iterate on it or that you want that the map be ordered according to some needs (alphabetical or other), you can use more sophisticated map such as TreeMap or LinkedHashMap
Set do not allow duplicate values, but this is not a solution. If you insert a duplicate value it will be ignored. You can choose a List of Items and then loop to find duplicates. An if condition can be added to increment it's state.

Why don't TreeMap.values() reflect the order in which elements were originally added?

I need a data structure that will perform both the role of a lookup map by key as well as be able to be convertable into a sorted list. The data that goes in is a very siple code-description pair (e.g. M/Married, D/Divorced etc). The lookup requirement is in order to get the description once the user makes a selection in the UI, whose value is the code. The sorted list requirement is in order to feed the data into UI components (JSF) which take List as input and the values always need to be displayed in the same order (alphabetical order of description).
The first thing that came to mind was a TreeMap. So I retrieve the data from my DB in the order I want it to be shown in the UI and load it into my tree map, keyed by the code so that I can later look up descriptions for further display once the user makes selections. As for getting a sorted list out of that same map, as per this post, I am doing the following:
List<CodeObject> list = new ArrayList<CodeObject>(map.values());
However, the list is not sorted in the same order that they were put into the map. The map is declared as a SortedMap and implemented as a TreeMap:
SortedMap<String, CodeObject> map = new TreeMap<String, CodeObject>().
CodeObject is a simple POJO containing just the code and description and corresponding getters (setters in through the constructor), a list of which is fed to UI components, which use the code as the value and description for display. I used to use just a List and that work fine with respect to ordering but a List does not provide an efficient interface for looking up a value by key and I now do have that requirement.
So, my questions are:
If TreeMap is supposed to be a map in the ordered of item addition, why isn's TreeMap.values() in the same order?
What should I do to fulfill my requirements explained above, i.e. have a data structure that will serve as both a lookup map AND a sorted collection of elements? Will TreeMap do it for me if I use it differently or do I need an altogether different approach?
TreeMap maintain's the key's natural order. You can even order it (with a bit more manipulation and custom definition of a comparator) by the natural order/reverse order of the value. But this is not the same as saying "Insertion order". To maintain the insertion order you need to use LinkedHashMap. Java LinkedHashMap is a subclass of HashMap - the analogy is the same as LinkedList where you maintain the trace of the next node. However, it says it cannot "Guarantee" that the order is maintained, so don't ask your money back if you suddenly see an insertion order is maintained with HashMap
TreeMap's documentation says:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
So unless you're providing a Comparator and tracking the insertion order and using it in that Comparator, you'll get the natural order of the keys, not the order in which the keys were inserted.
If you want insertion order, as davide said, you can use LinkedHashMap:
Hash table and linked list implementation of the Map interface, with predictable iteration order...This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map.
What you need is LinkedHashMap
See another question as well.

How to replace a specific element in a LinkedHashSet?

With an ArrayList I would do something like
myList.set(myList.indexOf(oldItemNumber), newItemNumber);
Is there an easy way to achieve this for LinkedHashSet?
Just use the put() method. It will update that specific entry.
From the documentation of LinkedHashMap:
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, which is
normally the order in which keys were inserted into the map
(insertion-order). Note that insertion order is not affected if a key
is re-inserted into the map. (A key k is reinserted into a map m if
m.put(k, v) is invoked when m.containsKey(k) would return true
immediately prior to the invocation.)
So you can simply put() the new value in and it will keep it's current location in the ordering. If you want to add it to the end then remove the old value and then add the new one.
The question is meaningless for a Set. A new object added to the Set either equals an existing one (in which case nothing happens) or it doesn't (in which case it is added at the end).
There is no way to insert objects at specific locations without removing and re-adding everything.
You may want to look at TreeSet which allows you to keep an ordered set that will remain ordered by the provided comparator as you add and remove objects.
I solved this problem where a LinkedHashSet member object has a hash code which computes a value on only part of the object (ex its Name field). Yes this is unconventional but I also had a matching equals() method. Two different objects can have the same hash code and appear equal but internally they have some different values.
An example would be a database record which has key which has to be unique. You need to replace the record with a new set of values, so you find and replace the record with the matching key with new values.
// Rebuild Linked Hash Set in original order with new element.
ArrayList<Item> newSet = new ArrayList<>(oldSet);
newSet.add(newSet.indexOf(oldItem), newItem);
oldSet.clear();
oldSet.addAll(newSet);
The arrayList will have the old values in the same order and the new item will get added in front of the old. No need to remove the duplicate. When added to the old HashSet (LinkedHashSet) it only keeps the first occurrence of any duplicate hash codes.
use map.put(key ,value) if you will use same key it will replace previous value

When to use a Map instead of a List in Java?

I didn't get the sense of Maps in Java. When is it recommended to use a Map instead of a List?
Say you have a bunch of students with names and student IDs. If you put them in a List, the only way to find the student with student_id = 300 is to look at each element of the list, one at a time, until you find the right student.
With a Map, you associate each student's ID and the student instance. Now you can say, "get me student 300" and get that student back instantly.
Use a Map when you need to pick specific members from a collection. Use a List when it makes no sense to do so.
Say you had exactly the same student instances but your task was to produce a report of all students' names. You'd put them in a List since there would be no need to pick and choose individual students and thus no need for a Map.
Java map: An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
Java list: 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.
The difference is that they are different. Map is a mapping of key/values, a list of a list of items.
I thinks its a lot the question of how you want to access your data. With a map you can "directly" access your items with a known key, in a list you would have to search for it, evan if its sorted.
Compare:
List<MyObject> list = new ArrayList<MyObject>();
//Fill up the list
// Want to get object "peter"
for( MyObject m : list ) {
if( "peter".equals( m.getName() ) {
// found it
}
}
In a map you can just type
Map<String, MyObject> map = new HashMap<String, MyObject>();
// Fill map
MyObject getIt = map.get("peter");
If you have data to process and need to do it with all objects anyway, a list is what you want. If you want to process single objects with well known key, a map is better.
Its not the full answer (just my 2...) but I hope it might help you.
A map is used as an association of a key and a value. With a list you have basically only values.
The indexes in List are always int, whereas in Map you can have another Object as a key.
Resources :
sun.com - Introduction to the Collections Framework, Map
Depends on your performance concerns. A Map more explicitly a HashMap will guarantee O(1) on inserts and removes. A List has at worst O(n) to find an item. So if you would be so kind as to elaborate on what your scenario is we may help more.
Its probably a good idea to revise Random Access Vs Sequential Access Data Structures. They both have different run time complexities and suitable for different type of contexts.
When you want to map instead of list. The names of those interfaces have meaning, and you shouldn't ignore it.
Use a map when you want your data structure to represent a mapping for keys to values. Use a list when you want your data to be stored in an arbitrary, ordered format.
Map and List serve different purpose.
List holds collection of items. Ordered (you can get item by index).
Map holds mapping key -> value. E.g. map person to position: "JBeg" -> "programmer". And it is unordered. You can get value by key, but not by index.
Maps store data objects with unique keys,therefore provides fast access to stored objects. You may use ConcurrentHashMap in order to achieve concurrency in multi-threaded environments.
Whereas lists may store duplicate data and you have to iterate over the data elements in order to access a particular element, therefore provide slow access to stored objects.
You may choose any data structure depending upon your requirement.

Categories