A Java collection of value pairs? (tuples?) - java

I like how Java has a Map where you can define the types of each entry in the map, for example <String, Integer>.
What I'm looking for is a type of collection where each element in the collection is a pair of values. Each value in the pair can have its own type (like the String and Integer example above), which is defined at declaration time.
The collection will maintain its given order and will not treat one of the values as a unique key (as in a map).
Essentially I want to be able to define an ARRAY of type <String,Integer> or any other 2 types.
I realize that I can make a class with nothing but the 2 variables in it, but that seems overly verbose.
I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.
I only need to store pairs in the collection, so I only need two values per entry. Does something like this exist without going the class route? Thanks!

AbstractMap.SimpleEntry
Easy you are looking for this:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
How can you fill it?
java.util.Map.Entry<String,Integer> pair1=new java.util.AbstractMap.SimpleEntry<>("Not Unique key1",1);
java.util.Map.Entry<String,Integer> pair2=new java.util.AbstractMap.SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
This simplifies to:
Entry<String,Integer> pair1=new SimpleEntry<>("Not Unique key1",1);
Entry<String,Integer> pair2=new SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
And, with the help of a createEntry method, can further reduce the verbosity to:
pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));
Since ArrayList isn't final, it can be subclassed to expose an of method (and the aforementioned createEntry method), resulting in the syntactically terse:
TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);

The Pair class is one of those "gimme" generics examples that is easy enough to write on your own. For example, off the top of my head:
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
assert left != null;
assert right != null;
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
And yes, this exists in multiple places on the Net, with varying degrees of completeness and feature. (My example above is intended to be immutable.)

Java 9+
In Java 9, you can simply write: Map.entry(key, value)
to create an immutable pair.
Note: this method does not allow keys or values to be null. If you want to allow null values, for example, you'd want to change this to: Map.entry(key, Optional.ofNullable(value)).
Java 8+
In Java 8, you can use the more general-purpose javafx.util.Pair to create an immutable, serializable pair. This class does allow null keys and null values. (In Java 9, this class is included in the javafx.base module). EDIT: As of Java 11, JavaFX has been decoupled from the JDK, so you'd need the additional maven artifact org.openjfx:javafx-base.
Java 6+
In Java 6 and up, you can use the more verbose AbstractMap.SimpleImmutableEntry for an immutable pair, or AbstractMap.SimpleEntry for a pair whose value can be changed. These classes also allow null keys and null values, and are serializable.
Android
If you're writing for Android, just use Pair.create(key, value) to create an immutable pair.
Apache Commons
Apache Commons Lang provides the helpful Pair.of(key, value) to create an immutable, comparable, serializable pair.
Eclipse Collections
If you're using pairs that contain primitives, Eclipse Collections provides some very efficient primitive pair classes that will avoid all the inefficient auto-boxing and auto-unboxing.
For instance, you could use PrimitiveTuples.pair(int, int) to create an IntIntPair, or PrimitiveTuples.pair(float, long) to create a FloatLongPair.
Hand-rolled implementations
As of Java 16, records have come out of preview status, so you can now do:
public record Pair<K, V>(K key, V value) {
public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, value);
}
}
The above implementation will have a big advantage in the future, as it'll allow you to do record deconstruction.
Prior to Java 16, you can achieve the same semantics with Project Lombok:
#Value(staticConstructor = "of")
public class Pair<K, V> {
K key;
V value;
}
or, with the following verbosity (which, unlike the class listed in the accepted answer, guards against NullPointerExceptions, and has a robust hashCode() implementation identical to that of Records1):
import java.util.Objects;
public class Pair<K, V> {
public final K key;
public final V value;
private Pair(K key, V value) {
this.key = key;
this.value = value;
}
public static <K, V> Pair<K, V> of(K key, V value) {
return new Pair<>(key, value);
}
public boolean equals(Object o) {
return o instanceof Pair && Objects.equals(key, ((Pair<?,?>)o).key) && Objects.equals(value, ((Pair<?,?>)o).value);
}
public int hashCode() {
return 31 * Objects.hashCode(key) + Objects.hashCode(value);
}
public String toString() {
return key + "=" + value;
}
}
1 Tested on OpenJDK 17

Map.Entry
These built-in classes are an option, too. Both implement the Map.Entry interface.
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry

Apache common lang3 has Pair class and few other libraries mentioned in this thread What is the equivalent of the C++ Pair<L,R> in Java?
Example matching the requirement from your original question:
List<Pair<String, Integer>> myPairs = new ArrayList<Pair<String, Integer>>();
myPairs.add(Pair.of("val1", 11));
myPairs.add(Pair.of("val2", 17));
//...
for(Pair<String, Integer> pair : myPairs) {
//following two lines are equivalent... whichever is easier for you...
System.out.println(pair.getLeft() + ": " + pair.getRight());
System.out.println(pair.getKey() + ": " + pair.getValue());
}

To anyone developing for Android, you can use android.util.Pair. :)

What about "Apache Commons Lang 3" Pair class and the relative subclasses ?
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
...
#SuppressWarnings("unchecked")
Pair<String, Integer>[] arr = new ImmutablePair[]{
ImmutablePair.of("A", 1),
ImmutablePair.of("B", 2)};
// both access the 'left' part
String key = arr[0].getKey();
String left = arr[0].getLeft();
// both access the 'right' part
Integer value = arr[0].getValue();
Integer right = arr[0].getRight();
ImmutablePair is a specific subclass that does not allow the values in the pair to be modified, but there are others implementations with different semantic. These are the Maven coordinates, if you need them.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>

You could write a generic Pair<A, B> class and use this in an array or list. Yes, you have to write a class, but you can reuse the same class for all types, so you only have to do it once.

Java 14+ edition
You can create a record which implements equals, hashCode, and toString out of the box. Interfaces like Comparable could also be implemented, if needed.
record Pair<A, B>(A first, B second) {}
Records are immutable.

Expanding on the other answers a generic immutable Pair should have a static method to avoid cluttering your code with the call to the constructor:
class Pair<L,R> {
final L left;
final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
static <L,R> Pair<L,R> of(L left, R right){
return new Pair<L,R>(left, right);
}
}
if you name the static method "of" or "pairOf" the code becomes fluent as you can write either:
list.add(Pair.of(x,y)); // my preference
list.add(pairOf(x,y)); // use with import static x.y.Pair.pairOf
its a real shame that the core java libraries are so sparse on such things that you have to use commons-lang or other 3rd parties to do such basic stuff. yet another reason to upgrade to scala...

The preferred solution as you've described it is a List of Pairs (i.e. List).
To accomplish this you would create a Pair class for use in your collection. This is a useful utility class to add to your code base.
The closest class in the Sun JDK providing functionality similar to a typical Pair class is AbstractMap.SimpleEntry. You could use this class rather than creating your own Pair class, though you would have to live with some awkward restrictions and I think most people would frown on this as not really the intended role of SimpleEntry. For example SimpleEntry has no "setKey()" method and no default constructor, so you may find it too limiting.
Bear in mind that Collections are designed to contain elements of a single type. Related utility interfaces such as Map are not actually Collections (i.e. Map does not implement the Collection interface). A Pair would not implement the Collection interface either but is obviously a useful class in building larger data structures.

I was going to ask if you would not want to just use a List<Pair<T, U>>? but then, of course, the JDK doesn't have a Pair<> class. But a quick Google found one on both Wikipedia, and forums.sun.com. Cheers

Spring has a Pair<S,T> type in the Data Utils package org.springframework.data.util
Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());

This is based on JavaHelp4u 's code.
Less verbose and shows how to do in one line and how to loop over things.
//======> Imports
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
//======> Single Entry
SimpleEntry<String, String> myEntry = new SimpleEntry<String, String>("ID", "Text");
System.out.println("key: " + myEntry.getKey() + " value:" + myEntry.getValue());
System.out.println();
//======> List of Entries
List<Entry<String,String>> pairList = new ArrayList<>();
//-- Specify manually
Entry<String,String> firstButton = new SimpleEntry<String, String>("Red ", "Way out");
pairList.add(firstButton);
//-- one liner:
pairList.add(new SimpleEntry<String,String>("Gray", "Alternate route")); //Ananomous add.
//-- Iterate over Entry array:
for (Entry<String, String> entr : pairList) {
System.out.println("Button: " + entr.getKey() + " Label: " + entr.getValue());
}

Apache Crunch also has a Pair class:
http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html

I mean, even though there is no Pair class in Java there is something pretty simmilar: Map.Entry
Map.Entry Documentation
This is (simplifying quite a bit) what HashMap , or actually any Map stores.
You can create an instance of Map store your values in it and get the entry set. You will end up with a Set<Map.Entry<K,V>> which effectively is what you want.
So:
public static void main(String []args)
{
HashMap<String, Integer> values = new HashMap<String,Integer>();
values.put("A", 235);//your custom data, the types may be different
//more data insertions....
Set<Map.Entry<String,Integer>> list = values.entrySet();//your list
//do as you may with it
}

just create a class like
class Tuples
{
int x;
int y;
}
then create List of this objects of Tuples
List<Tuples> list = new ArrayList<>();
so you can also implement other new data structures in the same way.

In project Reactor (io.projectreactor:reactor-core) there is advanced support for n-Tuples:
Tuple2<String, Integer> t = Tuples.of("string", 1)
There you can get t.getT1(), t.getT2(), ... Especially with Stream or Flux you can even map the tuple elements:
Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));

What about com.sun.tools.javac.util.Pair?

First Thing on my mind when talking about key/value pairs is the Properties Class where you can save and load items to a stream/file.

You can reuse existing Pair or any such class from "God knows how many libraries already provide such classes". And If you do not want anything from Hans Brende's answer on this question then I don't see any reason for not using 2D Array or List of Object Arrays/ArrayLists used as Pairs/Tuples. The reason for not using Array, you mentioned:
I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.
Even if you use Pair class from the Accepted Answer, you'll still have to cast the key and value objects. Since you want to store objects of all the types in there. In other words List<Pair> pairs is no different from List<Pair<? extends Object>> pairs which in turn is no different from Object[][2] or List<Object[]> or List<List<Object>>. Because if you write the following code:
List<Pair> pairs = new ArrayList<>();
// logic for populating pairs into list goes here
// then after sometime you need an element
Pair p = pairs.get(whateverIndex);
Object key = p.getKey(); // We don't know type of key, right?
Object value = p.getValue(); // We don't know the exact type here as well
// All sorts of type guessing statemntes go here
GuessedTypeOfKey finallyMyKey = (GuessedTypeOfKey) key;
GuessedTypeOfValue finallyMyValue = (GuessedTypeOfValue) value;
You still have to do the type casting. So I don't find any other reason to not use 2d Object array or List of Object Arrays/ArrayLists used as Pairs/Tuples . Following is code using List and arrays
List<Object[]> simplePairs = new ArrayList<>();
// Add values to pairs
simplePairs.add(new Object[]{1,"One"});
simplePairs.add(new Object[]{"Another Key of Different Type","Value"});
simplePairs.add(new Object[]{"Another Key of Different Type",new AnotherType("Another Value Type")});
// get values
Object[] pair = simplePairs.get(whateverIndex);
Object key = pair[0];
Object value = pair[1];

What you want is a List of some kind of object. I personally do not like using generic Pair classes. They have too many disadvantages:
They are not very expressive. They provide no contextual information besides their type arguments.
They prevent you from using primitive types
You can't apply any constraints on keys or values (besides their
type, of course).
They force you to carry the verbose type parameter declaration in many places (though this is somewhat mitigated by var and <>)
I prefer using ad-hoc classes. While a few years ago, this came with annoying boilerplate, this is no longer the case.
Here is a couple of alternatives that makes the declaration of the class really easy:
Java 14 and above: use a record class
record TypedCount(String type, int count) {}
There, that's it. You get toString() and equals() for free
Before Java 14: use lombok's #Value annotation
#Value
class TypedCount {
String type;
int count;
}
That's also it. Lombok automatically makes the fields private final, constructors, getters, toString() and equals().
While this requires you to add lombok as a dependency, this is only a compile-time dependency that generates the code for you. The lombok library does not need to be in your classpath at runtime.

Related

When initialzing a variable of an interface type, what happens to the non-interface methods belonging to the class in the initialization statement?

When .getClass() is called on myMap and myTreemap, "class java.util.LinkedHashMap" and "class java.util.TreeMap" are returned. Despite the matching return types, myMap can only use the methods in the map interface. I've heard that this eases programming by allowing programmers to change the implementation type easily. But - what good is changing the implementation type if I (seemingly) can only access the methods in the interface?
Also - myMap are myTreeMap are sorted according to their class type but again, what about the methods of the class type?
import java.util.*;
public class Freq {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<String, Integer>();
for (String a : args) {
Integer freq = m.get(a);
m.put(a, (freq == null) ? 1 : freq + 1);
}
System.out.println(m.size() + " distinct words:");
System.out.println(m);
System.out.println();
Map<String, Integer> myMap = new LinkedHashMap<String, Integer>(m);
System.out.println("map: " + myMap.getClass());
//output is "map: class java.util.LinkedHashMap"
//but, only the methods in the myMap interface can be accessed.
System.out.println(myMap.toString());
//output in order of appearance like a LinkedHashMap should.
TreeMap<String, Integer> myTreemap = new TreeMap<String, Integer>(m);
System.out.println("treemap: " + myTreemap.getClass());
//output is "treemap: class java.util.TreeMap"
//methods in the Map interface and myTreemap can be accessed.
System.out.println(myTreemap.toString());
//output in in alphabetical order like a treemap should.
}
}
I've heard that this eases programming by allowing programmers to change the implementation type easily.
Correct.
But - what good is changing the implementation type if I (seemingly) can only access the methods in the interface?
You can get e.g. Tree or Hash variants using the same API, the one published by the interface, while only changing the code at one place. If you were allowed to use the class's non-interface methods you wouldn't get that benefit: you would have to change all those calls too.
Also - myMap are myTreeMap are sorted according to their class type but again, what about the methods of the class type?
I don't understand the question. What about them?
Interfaces are a great feature. Think about it -- lets say you want to implement an algorithm that uses a hashMap. Previously in the code, the user chose a hash map implementation that was optimized in algorithms ran earlier. If you didn't have an interface,(rather, if the concept of interfaces didn't exist at all...or a set of function pointers didn't exist at all ), you would have to create the new algorithm that you want to implement for each implementation of the hash map. That's a lot of redundant code, and it's not very readable.
You don't really lose access to the underlying methods. However, if you want to access the underlying TreeMap and its methods...you will have to cast it to a tree map from a map.
#suppressedwarnings
TreeMap treeMap = null;
if(myMap instanceof TreeMap){
treeMap = (TreeMap)myMap;
}
if(treeMap == null){
return;
//If it wasn't the correct type, then it could not safely be cast.
}
//Now, do treeMap stuff
treeMap.treeMapOnlyMethod();
Using instanceof is usually an indicator of poor design -- instead, polymorphism should be used.
You will understand the point of interface much better if you look at an example with method parameters:
boolean validateMap(Map<String, Object> map) {
return map.get("x") != null && map.get("y") != null;
}
Observe that this method doesn't care which exactly map you passed in: it will work equally well with any type. That's the beauty of polymorphism.

What is the Best Way to Store some data in Java? (Array vs ArrayList)

So currently, I am extracting two different attributes from an XML file in java that (for my project) are related to each other and just printing them out to the console. However, I want to be able to store these in a way in which referencing one value will retrieve it's corresponding counterpart. For example:
Id: rId11 & Target: image3
Id: rId10 & Target: image2
Id: rId9 & Target: image1
With those 3 values, I'd want a way to store each line, but when I reference "rId" I could get it's corresponding "Target" value. I was thinking about using either an array or an arrayList, but I'm not really sure which would be better for my purposes or how exactly I would go about referencing only one value and getting the other. Could anyone offer me some advice? Thank you in advance.
If your keys are unique, use a Map.
Map<String, String> mak = new HashMap<String, String>();
map.put("rId11","image3");
map.put("rId10","image2");
map.put("rId9","image1");
Reference:
Java Tutorial > The Map
Interface
Otherwise, create a custom Object that holds key and value and create a List (or Set???) of these.
public class Entry {
private final String id;
private final String value;
public Entry(String id, String value) {
this.id = id; this.value = value;
}
public String getId() { return id; }
public String getValue() { return value; }
// also implement equals() and hashCode(), please
}
List<Entry> entries = new ArrayList<Entry>();
entries.add(new Entry("rId11","image3"));
Reference:
Java Tutorial > The List Interface
Java Tutorial > The Set Interface
Use a Map, with the Id ad the key and the Target as the value. Note that Map is an interface and thus defines behavior only. You will need to pick a specific implementation, such as HashMap.
I think a java.util.HashMap would be better suited for this requirement especially if sorting is not required.
// not sure what types these are but this would work better
Map<String, String> m = new HashMap<String, String>();
m.put("rId11", "image3");
String other = m.get("rId11");
If i understand correctly, you want to be able to do look for something like "rId10" and get the value "image2" (and only that).
If that is the case,I think the best (in terms of speed) and easiest solution will be a hash table (java.util.Hashtable) - be careful to use Java Generics as well (after Java 1.5). Check out http://en.wikipedia.org/wiki/Hash_table also.
You're being a bit ambiguous about what you want. If you want to lookup a value based on a given key, then store the pairs in a HashMap (faster) or Hashtable (slower but thread-safe).
Primitive arrays (and more advanced List-based collections such and ArrayList or Vector) don't work with name-value pairs out of the box. They are simply, well... lists. Primitive arrays can offer a bit more performance, since you avoid creating objects, but the more advanced List-type collections can be safer and more flexible.
Still, it sounds (?) like you want a Map type collection rather List type one.
UPDATE: By the way, if you use a Map then you can still work with a list of all your "rId" values. It will be a Set datatype actually, but that's just a special cousin of List that doesn't allow duplicates:
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("rId11","image3");
// ... additional put's for the other values
Set<String> myRids = myMap.keySet();
for(String rId : myRids) {
// do whatever you want with each rId one-by-one, etc
// You could also use "myRids.iterator()" to work with an Iterator instead
}
If the "keys" to your target values will be unique and only ever have one target mapped to them, then I would recommend using java.util.HashMap instead. You can retrieve any target value by passing in the key. Plus you can Iterate over HashMap like you could an ArrayList.
public class Item {
private String id;
private String target;
public Item(String id, String target) {
this.id = id;
this.target = target;
}
public String getId() {
return this.id;
}
public String getTarget() {
return this.target;
}
}
List<Item> items = new ArrayList<Item>();
// or
Map<String, Item> itemsIndexedById = new HashMap<String, Item>();
// depending on your use-case
Read the Java tutorial about collections.
ArrayList is useful if you need to add elements to it dynamically

Java Hashmap/Hashtable and numbering

the question is simple - I have to implement JTree TreeModel interface which requires that every object has a number. The tree will represent data that are kept in hashmap/hashtable. Keys in that hashmap are client objects and values are arrays of resources (or ArrayLists) so numbering is only a problem at the top level. What would be the easiest way to number keys in Hashmap/Hashtable?
public class IndexedMap<V> extends HashMap<Long, V> {
private AtomicLong index = new AtomicLong();
public void put(V value) {
put(index.getAndIncrement(), value);
}
}
IndexedMap<Object> objects = new IndexedMap<Object>();
objects.put("foo");
objects.put("bar");
// ...
But why don't you just use an ArrayList? It holds objects by an index, exactly what you need.
Sounds like the user-object keys need to be ordered - their "number" would be derived from their spot in the ordering.
Are the keys Comparable? If so, maybe use a TreeMap. If not, I suppose insertion order is your best bet (LinkedHashMap)

Casting to generic type in Java doesn't raise ClassCastException?

I have come across a strange behavior of Java that seems like a bug. Is it? Casting an Object to a generic type (say, K) does not throw a ClassCastException even if the object is not an instance of K. Here is an example:
import java.util.*;
public final class Test {
private static<K,V> void addToMap(Map<K,V> map, Object ... vals) {
for(int i = 0; i < vals.length; i += 2)
map.put((K)vals[i], (V)vals[i+1]); //Never throws ClassCastException!
}
public static void main(String[] args) {
Map<String,Integer> m = new HashMap<String,Integer>();
addToMap(m, "hello", "world"); //No exception
System.out.println(m.get("hello")); //Prints "world", which is NOT an Integer!!
}
}
Update: Thanks to cletus and Andrzej Doyle for your helpful answers. Since I can only accept one, I'm accepting Andrzej Doyle's answer because it led me to a solution that I think isn't too bad. I think it's a little better way of initializing a small Map in a one-liner.
/**
* Creates a map with given keys/values.
*
* #param keysVals Must be a list of alternating key, value, key, value, etc.
* #throws ClassCastException if provided keys/values are not the proper class.
* #throws IllegalArgumentException if keysVals has odd length (more keys than values).
*/
public static<K,V> Map<K,V> build(Class<K> keyClass, Class<V> valClass, Object ... keysVals)
{
if(keysVals.length % 2 != 0)
throw new IllegalArgumentException("Number of keys is greater than number of values.");
Map<K,V> map = new HashMap<K,V>();
for(int i = 0; i < keysVals.length; i += 2)
map.put(keyClass.cast(keysVals[i]), valClass.cast(keysVals[i+1]));
return map;
}
And then you call it like this:
Map<String,Number> m = MapBuilder.build(String.class, Number.class, "L", 11, "W", 17, "H", 0.001);
Java generics use type erasure, meaning those parameterized types aren't retained at runtime so this is perfectly legal:
List<String> list = new ArrayList<String>();
list.put("abcd");
List<Integer> list2 = (List<Integer>)list;
list2.add(3);
because the compiled bytecode looks more like this:
List list = new ArrayList();
list.put("abcd");
List list2 = list;
list2.add(3); // auto-boxed to new Integer(3)
Java generics are simply syntactic sugar on casting Objects.
As cletus says, erasure means that you can't check for this at runtime (and thanks to your casting you can't check this at compile time).
Bear in mind that generics are a compile-time only feature. A collection object does not have any generic parameters, only the references you create to that object. This is why you get a lot of warning about "unchecked cast" if you ever need to downcast a collection from a raw type or even Object - because there's no way for the compiler to verify that the object is of the correct generic type (as the object itself has no generic type).
Also, bear in mind what casting means - it's a way of telling the compiler "I know that you can't necessarily check that the types match, but trust me, I know they do". When you override type checking (incorrectly) and then end up with a type mismatch, who ya gonna blame? ;-)
It seems like your problem lies around the lack of heterogenous generic data structures. I would suggest that the type signature of your method should be more like private static<K,V> void addToMap(Map<K,V> map, List<Pair<K, V>> vals), but I'm not convinced that gets you anything really. A list of pairs basically is a map, so contructing the typesafe vals parameter in order to call the method would be as much work as just populating the map directly.
If you really, really want to keep your class roughly as it is but add runtime type-safety, perhaps the following will give you some ideas:
private static<K,V> void addToMap(Map<K,V> map, Object ... vals, Class<K> keyClass, Class<V> valueClass) {
for(int i = 0; i < vals.length; i += 2) {
if (!keyClass.isAssignableFrom(vals[i])) {
throw new ClassCastException("wrong key type: " + vals[i].getClass());
}
if (!valueClass.isAssignableFrom(vals[i+1])) {
throw new ClassCastException("wrong value type: " + vals[i+1].getClass());
}
map.put((K)vals[i], (V)vals[i+1]); //Never throws ClassCastException!
}
}
Java's generics are done using "type erasure", meaning that at runtime, the code doesn't know you have a Map<String, Integer> -- it just sees a Map. And since you're converting the stuff to Objects (by way of your addToMap function's param list), at compile time, the code "looks right". It doesn't try to run the stuff when compiling it.
If you care about the types at compile time, don't call them Object. :) Make your addToMap function look like
private static<K,V> void addToMap(Map<K, V> map, K key, V value) {
If you want to insert multiple items in the map, you'll want to make a class kinda like java.util's Map.Entry, and wrap your key/value pairs in instances of that class.
This is a fairly good explanation of what Generics do and don't do in Java:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html
It is very very different from C#!
I think you were trying to do something like this? Where there's compile time safety for the pairs that you're adding to the map:
addToMap(new HashMap<String, Integer>(), new Entry<String,Integer>("FOO", 2), new Entry<String, Integer>("BAR", 8));
public static<K,V> void addToMap(Map<K,V> map, Entry<K,V>... entries) {
for (Entry<K,V> entry: entries) {
map.put(entry.getKey(), entry.getValue());
}
}
public static class Entry<K,V> {
private K key;
private V value;
public Entry(K key,V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
Edit after comment:
Ahh, then perhaps all you're really looking for is this arcane syntax used to harass new hires who just switched to Java.
Map<String, Integer> map = new HashMap<String,Integer>() {{
put("Foo", 1);
put("Bar", 2);
}};
Java generics only apply during compile time and not run time.
The issue is the way you have implemented, java compiler does not get a chance to ensure type safety at compile time.
Since ur K,V never say they extend any particular class, during compile time java has no way to know it was supposed to be an integer.
If you change your code as following
private static void addToMap(Map map, Object ... vals)
it will give you a compile time error

Best way to create a hashmap of arraylist

I have one million rows of data in .txt format. the format is very simple. For each row:
user1,value1
user2,value2
user3,value3
user1,value4
...
You know what I mean. For each user, it could appear many times, or appear only once (you never know). I need to find out all the values for each user. Because user may appear randomly, I used Hashmap to do it. That is: HashMap(key: String, value: ArrayList). But to add data to the arrayList, I have to constantly use HashMap get(key) to get the arrayList, add value to it, then put it back to HashMap. I feel it is not that very efficient. Anybody knows a better way to do that?
You don't need to re-add the ArrayList back to your Map. If the ArrayList already exists then just add your value to it.
An improved implementation might look like:
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
while processing each line:
String user = user field from line
String value = value field from line
Collection<String> values = map.get(user);
if (values==null) {
values = new ArrayList<String>();
map.put(user, values)
}
values.add(value);
Follow-up April 2014 - I wrote the original answer back in 2009 when my knowledge of Google Guava was limited. In light of all that Google Guava does, I now recommend using its Multimap instead of reinvent it.
Multimap<String, String> values = HashMultimap.create();
values.put("user1", "value1");
values.put("user2", "value2");
values.put("user3", "value3");
values.put("user1", "value4");
System.out.println(values.get("user1"));
System.out.println(values.get("user2"));
System.out.println(values.get("user3"));
Outputs:
[value4, value1]
[value2]
[value3]
Use Multimap from Google Collections. It allows multiple values for the same key
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
Since Java 8 you can use map.computeIfAbsent
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-
Collection<String> values = map.computeIfAbsent(user, k -> new ArrayList<>());
values.add(value);
The ArrayList values in your HashMap are references. You don't need to "put it back to HashMap". You're operating on the object that already exists as a value in the HashMap.
If you don't want to import a library.
package util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* A simple implementation of a MultiMap. This implementation allows duplicate elements in the the
* values. (I know classes like this are out there but the ones available to me didn't work).
*/
public class MultiMap<K, V> extends HashMap<K, List<V>> {
/**
* Looks for a list that is mapped to the given key. If there is not one then a new one is created
* mapped and has the value added to it.
*
* #param key
* #param value
* #return true if the list has already been created, false if a new list is created.
*/
public boolean putOne(K key, V value) {
if (this.containsKey(key)) {
this.get(key).add(value);
return true;
} else {
List<V> values = new ArrayList<>();
values.add(value);
this.put(key, values);
return false;
}
}
}
i think what you want is the Multimap. You can get it from apache's commons collection, or google-collections.
http://commons.apache.org/collections/
http://code.google.com/p/google-collections/
"collection similar to a Map, but
which may associate multiple values
with a single key. If you call put(K,
V) twice, with the same key but
different values, the multimap
contains mappings from the key to both
values."
I Could not find any easy way. MultiMap is not always an option available. So I wrote something this.
public class Context<K, V> extends HashMap<K, V> {
public V addMulti(K paramK, V paramV) {
V value = get(paramK);
if (value == null) {
List<V> list = new ArrayList<V>();
list.add(paramV);
put(paramK, paramV);
} else if (value instanceof List<?>) {
((List<V>)value).add(paramV);
} else {
List<V> list = new ArrayList<V>();
list.add(value);
list.add(paramV);
put(paramK, (V) list);
}
return paramV;
}
}
it would be faster if you used a LinkedList instead of an ArrayList, as the ArrayList will need to resize when it nears capacity.
you will also want to appropriately estimate the capacity of the wrapping collection (HashMap or Multimap) you are creating to avoid repetitive rehashing.
As already mentioned, MultiMap is your best option.
Depending on your business requirements or constraints on the data file, you may want to consider doing a one-off sorting of it, to make it more optimised for loading.

Categories

Resources