This should be easy for many of you, but for me it's just another bit of rust needing to be chipped away as I get back into basic Java coding. Using bloody associative arrays for so long in other languages have turned me nice and spoiled. :P
My problem is simple: I'm storing a set of objects, each containing a string and a number, in a list. I would like each object inserted into this list to be sorted alphabetically by its string. I would also like to be able to retrieve objects from the list by their string as well. I would like to do this as formally and/or efficiently as possible.
Is there something already available in the Java standard libraries for this?
there is a great writeup on java collections which covers most of your needs.
short advice concerning this: use a TreeMap(Comparator c) with a custom Comparator.
key ist the string, value is the composite object with string and number
That sound like a SortedMap. With the String as the key and the Number as the value.
Apart from the SortedMap, you could have a TreeSet<Pair<String, Number>> where you supply the comparator to sort the pairs (or make your Pair class implement Comparable and do it there).
This keeps the objects separate from the data structure, and whilst for a trivial example like this it isn't a big deal, you can imagine that a TreeSet<MyObject> where MyObject implements Comparable is easier to grok in the long term.
If you have duplicate string value among the objects in the list, you might want to look at
Google Collections' TreeMultimap. With TreeMultimap, not only the keys are sorted but all values having the same key are also stored in a collection.
Multimap<String, Pair> mm = new TreeMultimap<String, Pair>(
new Comparator<String>(){...},
new PairComparator());
mm.put("A", new Pair("A", 1));
mm.put("B", new Pair("B", 2));
mm.put("B", new Pair("B", 3));
Collection values = mm.values();
// values are [Pair("A", 1), Pair("B", 2), Pair("B", 3)]
Collection bValues = mm.get("B");
// bValues are [Pair("B", 2), Pair("B", 3)]
I have done something similar to that, I used ArrayList to store them objects and write comparators for sorting the arraylist.
Related
As we know that, SortedMap maintains the entries as sorted by keys. I read many threads in this forum and saw lots of example which sorts the SortedMap, by values. However, as you know when I put an item to default SortedMap it does not sort the Map again just put this new entry where it is supposed to be.
For example,
SortedMap<String,Person> sortedMap = new TreeMap();
Person p1 = new Person("John",38);
sortedMap.put(p1.getName(), p1);
Person p2 = new Person("Tom",34);
sortedMap.put(p2.getName(), p2); // does not sort, maintains sorted set by comparing the other values
Person p3 = new Person("Susan",21);
sortedMap.put(p3.getName(), p3); // does not sort, maintains sorted set by comparing the other values
In many threads in this forum, I saw many many code that sorts the SortedMap by values by calling a sort method like:
sortedMap.sort(sortedMap.entries());
This or something else method is being called to get values as sorted.
But, I need to a Map implementation which keeps the values as sorted without a calling sort method as I explained in above. For example, in above code I just can call the firstKey() method; but instead I need to call a firstValue() method.
Person minimumAgePerson = sortedMap.firstValue().
System.out.println(minimumAgePerson.getName()); // it should print "Susan"
SortedSet is not appropriate for my requiremenets because I can put some new Objects ( Person ) whose key values already in the map, these just added entries should override the existing objects ( so I need a map ):
Person p4 = new Person("Susan",39);
sortedMap.put(p4.getName(),p4);
Person newMinimumAgePerson = sortedMap.firstValue();
System.out.println(newMinimumAgePerson.getName()); // it should print "Tom"
Is there an implementation to accomplish this taks or do I need to implement SortedSet myself?
Often, the simplest and safest way of dealing with this type of problem is to write a class that uses two different standard collections. The class can offer exactly the methods you need, not necessarily conforming to any of the java.util interfaces.
Given the stated requirements, I would use a SortedMap to contain the values, combined with a HashMap mapping keys to values. To prevent duplicate keys, put the key-value pair in the HashMap, checking the put result. If the key was already present, remove the old value from the SortedMap before adding the new value.
If you have additional requirements, this particular design may not cover everything, but the concept of combining java.util structures is a generally useful one.
I think the best way for you is to create a custom type containing both a Map (for the key association), and a SortedSet (in order to sort values)
It is not clear to me if you want to be able to have the same value for two different keys. In this case, you'd need to use some kind of SortedMultiSet.
There is no implementation that contains a sort by both Key and Value. But really any implementation that did do this would need a separate datastructure for the sorting by value anyway, so you might as well create that datastructure yourself.
I would suggest either just implementing a sorted structure, using an existing one Like TreeMap or a PriorityQueue depending on your needs. Once that was done I would probably extend TreeMap with a custom structure and override the put,remove,addAll, etc methods to place it both in the super map, and also in your sort by keys structure.
As described in the answer to Double in HashMap, Doubles shouldn't be used in HashMaps because they are difficult to compare for equality. I believe my case is different, but I thought I'd ask to make sure since I didn't see anything about this.
I'm going to have a series of double values associated with objects, and I want them to be sorted by the double values. Is TreeMap an appropriate solution? Would there be a better one? The double values are generated a bunch of math, so the likelihood of a duplicate value is extremely low.
EDIT: I should clarify: all I need is to have this list of objects sorted by the doubles they're associated with. The values of the doubles will be discarded and I'll never call map.get(key)
Doubles shouldn't be used in HashMaps because they are difficult to compare for equality.
Will you ever try to get the values based on certain keys?
If yes, then the reasoning about "difficult to compare" applies and you should probably avoid such data structure (or always rely on tailMap / headMap / submap and fetch ranges of the map).
If no (i.e. you'll typically just do for (Double key : map.keySet()) ... or iterate over the entrySet) then I would say you're fine using Double as keys.
The double values are generated a bunch of math, so the likelihood of a duplicate value is extremely low.
Is it a bug if you actually do get a duplicate?
If yes then it's not the right data structure to use. You could for instance use a Multimap from Guava instead.
If no, (i.e. it doesn't matter which of the two values it maps to, because they can only differ by a small epsilon anyway) then you should be fine.
The problem with doubles in tree maps is exactly the same as it is with doubles in hash map - comparing for equality. If you avoid calls of treeMap.get(myDouble) and stay with range queries instead (e.g. by using submap) you should be fine.
TreeMap<Double,String> tm = new TreeMap<Double,String>();
tm.put(1.203, "quick");
tm.put(1.231, "brown");
tm.put(1.233, "fox");
tm.put(1.213, "jumps");
tm.put(1.243, "over");
tm.put(1.2301, "the");
tm.put(1.2203, "lazy");
tm.put(1.2003, "dog");
for (Map.Entry<Double,String> e : tm.subMap(1.230, 1.232).entrySet()) {
System.out.println(e);
}
This prints
1.2301=the
1.231=brown
See this snippet on ideone.
If you only want to sort them, the best thing would be to create a wrapper object around the double and the object, implement the "comparable" interface on this wrapper, and use a simple collection to sort them
If you just want them sorted, there are better collections (for instance SortedSet). You can also use any list and use the utilities for sorting (I think they are in java.util.Collection).
Only use Maps and Tables when you want to directly access an item by its key.
I really like the Multimap class of the google guava library. It is a map type where you can add multiple values for a key, so it effectively maps from a key to a collection of some type. What I especially love is the Multimaps.index() function which takes an Iterable and a key function and returns a Multimap which groups (or indexes or maps) the elements of the Iterable by the value the function returns for each of those elements.
What I find a bit strange is that Multimap.values() returns a flat collection instead of a collection of collections? So the grouping the index function gave me is lost once Ì retrieve the values. I can circumvent that problem by calling Multimap.asMap() and then call values() on that.
Does anyone know why it may make sense that Multimap behaves that way?
Multimap.asMap().values() isn't a way around the problem -- it was deliberate that Multimap provides both ways of accessing it, getting a Collection<Collection<V>> via asMap().values() and getting the flattened Collection<V> with values().
More generally speaking, Multimap tries not to just be "a map to collections," but rather "a general way to associate keys with multiple values." So you get the entries() method in addition to values() and keys(). The asMap() view provides a way to treat it as a "map to collections," but that has very different semantics that aren't always what you're looking for.
In any event, the values method is just meant to fill a different niche than the one filled by asMap().values().
Does anyone know why it may make sense that Multimap behaves that way?
A Multimap should be viewed as ordinary map, where the keys does not need to be unique.
Key Val
a -> 1
b -> 2
a -> 3
Values: {1, 2, 3}
I want to maintain a collection of objects of type Odp. Odp implements Comparable. I need to be able to refer to an object in the collection with its integer name. This integer must correspond to its sort order (not insertion order). Each integer only applies to one Odp, and vice versa.
I have a function compareOdp(Odp o1, Odp o2) that returns a numeric value representing the similarity of the two arguments. I the Odp collection to be set up in such a way that it's easy to ask questions like "What is the closest Odp to foo in the collection?" or "Of these several collections of Odp objects, how close are they to each other?"
What is the best way to do this? TreeMap? HashBiMap?
Related Question:
Let's say I have the following set of objects: o1, o2, o3 contained in collection col. Their sort order is
o2
o3
o1
I want to ask col: "what is the nth object in the list?" From what I can see, SortedSet and TreeMap don't have a way to do this. I guess I could iterate over, but it feels like there should be an easier way.
If you are using Java 6, the NavigableSet API (implemented by TreeSet) can help.
public static Odp nearest(Odp o, NavigableSet<? extends Odp> set) {
Odp f = set.floor(o), c = set.ceiling(o);
if (f == null)
return c;
if (c == null)
return f;
int df = compareOdp(o, f), dc = compareOdp(c, o);
return (df <= dc) ? f : c;
}
Any implementation of SortedSet will keep the items in order, according to the Comparable interface.
TreeSet is an implementation of SortedSet.
TreeMap is a build-in solution and as far as I've used it. I think it is pretty good.
I would use the lowly LinkedList. Then use Collections.binarySearch for searching the list. The binary search function will return you the insertion point in your linked list. To find which one is closest, simply use your compareOdp function with the item below and the item above in the linkedlist.
// What is the nth object in the list...
Object oneObject = TreeMap.get(nthKey);
Okay, so far / so good. If you do not care how they are contained in the Data Structure, you can use a ( might have to look it up but it would be called a ) HashMap == in which case there would be a key that you supply, which can easily be:
Object placement= HashMap.put(new Integer(++index),new Object(data));// see docs for exact
if placement is null, the map did not contain the object previously, used in conjuction with containsKey() exact management of data set is obtainable
Which will work find for get and put by ordered key, it's only that if you do toArray() you get a bizarre ordering that is only comprehensible to whomever wrote the HashMap implementation.
What is the most straightforward way to create a hash table (or associative array...) in Java? My google-fu has turned up a couple examples, but is there a standard way to do this?
And is there a way to populate the table with a list of key->value pairs without individually calling an add method on the object for each pair?
Map map = new HashMap();
Hashtable ht = new Hashtable();
Both classes can be found from the java.util package. The difference between the 2 is explained in the following jGuru FAQ entry.
You can use double-braces to set up the data. You still call add, or put, but it's less ugly:
private static final Hashtable<String,Integer> MYHASH = new Hashtable<String,Integer>() {{
put("foo", 1);
put("bar", 256);
put("data", 3);
put("moredata", 27);
put("hello", 32);
put("world", 65536);
}};
Also don't forget that both Map and Hashtable are generic in Java 5 and up (as in any other class in the Collections framework).
Map<String, Integer> numbers = new HashMap<String, Integer>();
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);
Integer one = numbers.get("one");
Assert.assertEquals(1, one);
import java.util.HashMap;
Map map = new HashMap();
What Edmund said.
As for not calling .add all the time, no, not idiomatically. There would be various hacks (storing it in an array and then looping) that you could do if you really wanted to, but I wouldn't recommend it.
And is there a way to populate the table with a list of key->value pairs without individually calling an add method on the object for each pair?
One problem with your question is that you don't mention what what form your data is in to begin with. If your list of pairs happened to be a list of Map.Entry objects it would be pretty easy.
Just to throw this out, there is a (much maligned) class named java.util.Properties that is an extension of Hashtable. It expects only String keys and values and lets you load and store the data using files or streams. The format of the file it reads and writes is as follows:
key1=value1
key2=value2
I don't know if this is what you're looking for, but there are situations where this can be useful.
It is important to note that Java's hash function is less than optimal. If you want less collisions and almost complete elimination of re-hashing at ~50% capacity, I'd use a Buz Hash algorithm Buz Hash
The reason Java's hashing algorithm is weak is most evident in how it hashes Strings.
"a".hash() give you the ASCII representation of "a" - 97, so "b" would be 98. The whole point of hashing is to assign an arbitrary and "as random as possible" number.
If you need a quick and dirty hash table, by all means, use java.util. If you are looking for something robust that is more scalable, I'd look into implementing your own.
Hashtable<Object, Double> hashTable = new Hashtable<>();
put values
...
get max
Optional<Double> optionalMax = hashTable.values().stream().max(Comparator.naturalOrder());
if (optionalMax.isPresent())
System.out.println(optionalMax.get());