Efficient way to modify a list of objects using maps - java

I have a list of objects, these objects can shift position in the list or even be removed, this object that I am saving, each of them have an unique id, lets call this id "key". To quickly check membership in the list I have an additional map which is mapped as
<object.getKey(), object>
Currently I get a call to my method with this key, and I do a
map.contains(key)
// For delete
list.remove(map.get(key))
// For edit
Object oldObj = list.get(map.get(key))
// Modify the old obj, they are immutable so I need to set
// old position with new object
list.set(map.get(key), newObject)
For sure this code is shit, but I am not able to wrap my head around a clean solution for this, I want to avoid any O(n) searches on the list which is clearly happening in the case of the list.remove and list.get!
One of the solutions I could think of was to map the key to the list position but then in case the map has a delete operation or an insert operation I'd have to update the whole map from the position of change! Again O(n). Any suggestions on the most efficient way to do this?

You haven't specified what exactly you need to do with the list, but assuming you're looking for a collection with a predictable order, consider using a LinkedHashSet - it retains the order of insertion while stile allowing operations like add, remove or contains in O(1).

Related

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.

Java - remove last known item from HASHMAP on MAP!s

OK so this is a BIT different. I have a new HashMap
private Map<String, Player> players = new HashMap<String, Player>();
How do I remove last known item from that? Maybe somethign like this?
hey = Player.get(players.size() - 1);
Player.remove(hey);
The problem is, a HashMap is not sorted like a list. The internal order depends on the hashCode() value of the key (e.g. String). You can use a LinkedHashMap which preserves the insert order. To remove the last entry on this you can use an iterator in combination with a counter which compares to the size and remove the last entry.
It's so easy. Try this:
Map<String, Player> players = new LinkedHashMap<String, Players>();
List<String> list = new ArrayList<String>(players.keySet());
map.remove(list.get(list.size()-1));
I'm a little bit confused. First of all, you're saying that you've got a new ArrayList and you're illustrating this with a line that creates a new HashMap. Secondly, does the Player class really have static methods like get(int) and remove(Object)?
HashMap doesn't have a particular order, ArrayList (as any other List) does.
Removing from an ArrayList
If you've got a list of players, then you can do the following:
private List<Player> players = new ArrayList<Player>();
// Populate the list of players
players.remove(players.size() - 1);
Here, I've used the remove(int) method of List, which allows to remove an item at an arbitrary index.
Removing from a HashMap
If you've got a map of players, there's no such thing as "the last item". Sure, you can iterate over the map and one of the items will pop out last, but that doesn't mean anything. Therefore, first you have to find out what you want to remove. Then you can do the following:
private Map<String, Player> players = new HashMap<String, Player>();
// Populate the map of players
// Find the key of the player to remove
players.remove(toRemove);
Here, I've used the remove(Object) method of Map. Note that in order to remove some key-value pair, you have to show the key, not the value.
There's no "first" and "last" in a HashMap. It's unordered. Everything is accessible by its key, not by index.
You cannot delete from HashMap like that. You need to use LinkedHashMap.
Simple, just do something of this effect.
1) Get a keyset iterator;
2) Create a Key somelastKey = null
3) Iterate through the iterator and assigning somelastKey until iterator finishes.
4) finally, do players.remove(somelastKey);
Bear in mind that HashMap is unordered, it depends on Object's hashCode to determine insertion order.
Instead of using HashMap, try using LinkedHashMap which keeps a predictable iteration order.
Hope this helps....
You'll probably have to extend HashMap, override put so that it caches the key, and then create a new method that just removes the key that was cached.
Unfortunately, this will only let you remove the most recently added. If you need to remove the most recently added multiple times (without inserting in-between the removes), you're out of luck.
In that case, I'd probably do the same overrides, just write the keys to a List. So you'd have both a list and a Map.
When adding:
String key; Player value;
lastKey = key;
map.put(key, value);
//...later...
Player lastAdded = map.remove(lastKey);
Other than that there's really no way without using a LinkedHashMap or in some way creating your own wrapper map or extending HashMap.
You shouldn't be using a raw hashmap anywhere because things like this happen.
Get in the habit of wrapping your collections in business logic classes.
See, in your case right now you need to associate these two related variables--your hashmap and a "Last entered" item so you can remove it.
If you need to remove the last item from some other class, you need to pass both items.
Any time you find yourself passing 2 or more items together into more than one API, you are probably missing a class.
Create a new class that contains the hashmap and a "lastAdded" variable. Have put and remove methods that are just forwarded to the hashmap, but the put method would also set the lastAdded variable.
Also be sure to add a removeLast() method.
NEVER allow access to your hashmap outside this class, it needs to be completely private (this is what I mean by wrapped). In this way you can ensure it doesn't get out of sync with the lastAdded variable (also completely private).
Just to reiterate getters and setters for these variables would be a terrible idea (as they are with nearly all actual OO code).
You will quickly find a bunch of other methods that NEED to be in this class in order to access data inside your hashmap--methods that never felt right in their current location. You will probably also notice that those methods always have an additional parameter or two passed in--those parameters should probably be members of your new class.
Once you get in the habit of doing actual OO design (via refactoring in this case), you'll find your code MUCH more manageable. To illustrate this point, if you find later that you need multiple levels of "delete last", it will be TRIVIAL to add to your class because it will be extremely clear exactly what methods can modify your hashtable and where your new "stack" of lastItems should be located--in fact it's probably a 2 line code change.
If you do not make this wrapper class, various locations will each have code to set "lastAdded" when they add code to the hashtable. Each of those locations will have to be modified, some may be in other classes requiring you to pass your new stack around with the hashtable. It will be easier to get them out of synch if you forget to change one location.

Difference between HashMap and ArrayList in Java?

In Java, ArrayList and HashMap are used as collections. But I couldn't understand in which situations we should use ArrayList and which times to use HashMap. What is the major difference between both of them?
You are asking specifically about ArrayList and HashMap, but I think to fully understand what is going on you have to understand the Collections framework. So an ArrayList implements the List interface and a HashMap implements the Map interface. So the real question is when do you want to use a List and when do you want to use a Map. This is where the Java API documentation helps a lot.
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.
Map:
An object that maps keys to values. A
map cannot contain duplicate keys;
each key can map to at most one value.
So as other answers have discussed, the list interface (ArrayList) is an ordered collection of objects that you access using an index, much like an array (well in the case of ArrayList, as the name suggests, it is just an array in the background, but a lot of the details of dealing with the array are handled for you). You would use an ArrayList when you want to keep things in sorted order (the order they are added, or indeed the position within the list that you specify when you add the object).
A Map on the other hand takes one object and uses that as a key (index) to another object (the value). So lets say you have objects which have unique IDs, and you know you are going to want to access these objects by ID at some point, the Map will make this very easy on you (and quicker/more efficient). The HashMap implementation uses the hash value of the key object to locate where it is stored, so there is no guarentee of the order of the values anymore. There are however other classes in the Java API that can provide this, e.g. LinkedHashMap, which as well as using a hash table to store the key/value pairs, also maintains a List (LinkedList) of the keys in the order they were added, so you can always access the items again in the order they were added (if needed).
If you use an ArrayList, you have to access the elements with an index (int type). With a HashMap, you can access them by an index of another type (for example, a String)
HashMap<String, Book> books = new HashMap<String, Book>();
// String is the type of the index (the key)
// and Book is the type of the elements (the values)
// Like with an arraylist: ArrayList<Book> books = ...;
// Now you have to store the elements with a string key:
books.put("Harry Potter III", new Book("JK Rownling", 456, "Harry Potter"));
// Now you can access the elements by using a String index
Book book = books.get("Harry Potter III");
This is impossible (or much more difficult) with an ArrayList. The only good way to access elements in an ArrayList is by getting the elements by their index-number.
So, this means that with a HashMap you can use every type of key you want.
Another helpful example is in a game: you have a set of images, and you want to flip them. So, you write a image-flip method, and then store the flipped results:
HashMap<BufferedImage, BufferedImage> flipped = new HashMap<BufferedImage, BufferedImage>();
BufferedImage player = ...; // On this image the player walks to the left.
BufferedImage flippedPlayer = flip(player); // On this image the player walks to the right.
flipped.put(player, flippedPlayer);
// Now you can access the flipped instance by doing this:
flipped.get(player);
You flipped player once, and then store it. You can access a BufferedImage with a BufferedImage as key-type for the HashMap.
I hope you understand my second example.
Not really a Java specific question. It seems you need a "primer" on data structures. Try googling "What data structure should you use"
Try this link http://www.devx.com/tips/Tip/14639
From the link :
Following are some tips for matching the most commonly used data structures with particular needs.
When to use a Hashtable?
A hashtable, or similar data structures, are good candidates if the stored data is to be accessed in the form of key-value pairs. For instance, if you were fetching the name of an employee, the result can be returned in the form of a hashtable as a (name, value) pair. However, if you were to return names of multiple employees, returning a hashtable directly would not be a good idea. Remember that the keys have to be unique or your previous value(s) will get overwritten.
When to use a List or Vector?
This is a good option when you desire sequential or even random access. Also, if data size is unknown initially, and/or is going to grow dynamically, it would be appropriate to use a List or Vector. For instance, to store the results of a JDBC ResultSet, you can use the java.util.LinkedList. Whereas, if you are looking for a resizable array, use the java.util.ArrayList class.
When to use Arrays?
Never underestimate arrays. Most of the time, when we have to use a list of objects, we tend to think about using vectors or lists. However, if the size of collection is already known and is not going to change, an array can be considered as the potential data structure. It's faster to access elements of an array than a vector or a list. That's obvious, because all you need is an index. There's no overhead of an additional get method call.
4.Combinations
Sometimes, it may be best to use a combination of the above approaches. For example, you could use a list of hashtables to suit a particular need.
Set Classes
And from JDK 1.2 onwards, you also have set classes like java.util.TreeSet, which is useful for sorted sets that do not have duplicates. One of the best things about these classes is they all abide by certain interface so that you don't really have to worry about the specifics. For e.g., take a look at the following code.
// ...
List list = new ArrayList();
list.add(
Use a list for an ordered collection of just values. For example, you might have a list of files to process.
Use a map for a (usually unordered) mapping from key to value. For example, you might have a map from a user ID to the details of that user, so you can efficiently find the details given just the ID. (You could implement the Map interface by just storing a list of keys and a list of values, but generally there'll be a more efficient implementation - HashMap uses a hash table internally to get amortised O(1) key lookup, for example.)
A Map vs a List.
In a Map, you have key/value pairs. To access a value you need to know the key. There is a relationship that exists between the key and the value that persists and is not arbitrary. They are related somehow. Example: A persons DNA is unique (the key) and a persons name (the value) or a persons SSN (the key) and a persons name (the value) there is a strong relationship.
In a List, all you have are values (a persons name), and to access it you need to know its position in the list (index) to access it. But there is no permanent relationship between the position of the value in the list and its index, it is arbitrary.

HashMap: get arraylist to be a set of key or value

I have two arraylists A1, A2. Each element of A1 would be the key and each element of A2 the corresponding value. So the solution that I found is looping on A1 ( A1 and A2 have the same size) making hashmap.add(A1[i],A2[i]) but is there a way to directly send the key value pairs as two sets? I want to avoid loops it is going to slow my code..Thank you in advance!
"I want to avoid loops it is going to slow my code." Anytime code does anything, it slows down your code. The key is to avoid doing things you don't need to do. Something needs to iterate through your lists.
You have a list of key value pairs. The only way to take that list and add it into the Hashmap is to iterate the list. If you had your key value pairs stored in some other type of Map object then you could use the Hashmap constructor HashMap(Map<? extends K,? extends V> m) but not with your ArrayList.
Don't fear the iteration in your code if it is the right approach. Remember Polya: Find a solution, then see if you can find a better solution.
If you happen to be able to control the order in which you insert the items into the arrays, and you can do so in sorted order, you may not need the HashMap after all. While the hashed lookup will exhibit amortized constant time, you can get lookup in O(log n) time with binary search over a sorted random-access sequence like an array. Function Arrays#binarySearch() allows you to figure out which element, if any, matches your key in the first array and, given that position, you could the access the corresponding value in the parallel array.
This approach is most beneficial when you build up the data only once and look up entries frequently, and don't do any subsequent adding or removing of entries.
Maybe you would be able to avoid the iteration to insert the data into arrays doing it directly into the hashmap.
Good luck!

Categories