Best way to create a hashmap of arraylist - java

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.

Related

How to make a key have multiple values in a Hashtable? [duplicate]

This question already has answers here:
HashMap with multiple values under the same key
(21 answers)
Closed 6 years ago.
I have 100 entries and I have to have to hash these into a hashtable of a limited size.
I know how to work with the first entry, ht.put(k,v) does the trick.
But as soon as I want to add another value to it, the old one gets overwritten. I don't want to do that, I want to append it in a linkedlist or arraylist.
Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>(211);
ht.put(1, 40);
ht.put (1, 60);
System.out.println(ht.get(1));
// output is 60
How to make it both 40 and 60?
You can have List as value type like:
Hashtable<Integer,List<Integer>> ht = new Hashtable<Integer,List<Integer>>(211);
And your put operation would look like:
public static void put(Hashtable<Integer,List<Integer>> ht, int key, int value) {
List<Integer> list = ht.get(key);
if (list == null) {
list = new ArrayList<Integer>();
ht.put(key, list);
}
list.add(value);
}
[UPDATE1]
If you want you can make your one extension of Hashtable like:
public class MyHashtable extends Hashtable<Integer,List<Integer>> {
public MyHashtable(...) { // add params if needed
super(...);
}
// with additional method:
public static void putOne(int key, int value) {
List<Integer> list = this.get(key);
if (list == null) {
list = new ArrayList<Integer>();
this.put(key, list);
}
list.add(value);
}
}
You need linear probing http://www.sanfoundry.com/java-program-implement-hash-tables-linear-probing/
It's not possible to store more than one value in a cell of a hash table
When trying to map a new key to an already occupied cell this is called a collision.
There are a few algorithm schemes to try and work around collision, one is Linear probing - which finds the next most appropriate free space for the key to be stored
The data structure you are looking for is called Multi Map. By definition it has different interface than a map, because it allows multiple values associated with the same key.
There's no standard library implementation for this data structure yet. But you can find good ones in some open source libraries:
Guava
Apache Commons Collections
Multimap (https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Multimap.html) should help if you are allowed to use it.
Alternatively, you could use Map<Integer, List<Integer>>.
You are using same key (1), which is not what you wanted, unless you wanted to add more values to the same key, in that case have hashtable of list of arrays HashMap<Integer,List<Integer>> integerArrayMap.
In Hashtable, the Key MUST be unique, as you are NOT using unique keys, the same value is being replaced. so try to put the values with different keys.
ht.put(1, 40);
ht.put (2, 60);
I suggest you to refer the Hashtable api here:
https://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html

Finding the key whose value is the lowest value in a hashmap

I'm trying to come up with an efficient way to return a key in my HashMap that has the lowest value in datastructure. Is there a quick and efficient way to do this besides looping through the entire HashMap?
For example, if I have a hashmap that looks like this:
1: 200
3: 400
5: 1
I want to return the key, 5.
No, you have to loop over all the keys in a HashMap to find the smallest. If this is an important operation, you're better off using a SortedMap, for instance TreeMap, which keeps its elements in sorted order, and then you can simply call firstKey() to find the lowest key.
As others have mentioned HashMap itself does not provide this.
So your options are to either compute it on-demand or pre-compute.
To compute it on-demand, you would iterate the HashMap.entrySet()
Depending on the size of the map, frequency of its change and frequency of requiring the key-with-lowest-value, pre-computing (caching) may be more efficient. Something as follows:
class HashMapWithLowestValueCached<K, V extends Comparable> extends HashMap<K, V> {
V lowestValue;
K lowestValueKey;
void put(K k, V v) {
if (v.compareTo(lowestValue) < 0) {
lowestValue = v;
lowestValueKey = k;
}
super.put(k, v);
}
K lowestValueKey () { return lowestValueKey; }
}
No, there is no way of doing this. You need to iterate over all the elements in the HashMap to find the one with the lowest value.
The reason why we have different kinds of storage is that they support different kinds of operations with different efficiency. HashMap is not designed to retrieve elements efficienctly based on their value. The kind of storage class you need for this will depend on what other operations you need to be able to do quickly. Assuming that you probably also want to be able to retrieve items quickly based on their key, the following might work:
Write a wrapper around your HashMap that keeps track of all the elements being added to it, and remembers which oneis the smallest. This is really only useful if retriving the smalls is the only way you need to access by value.
Store all your data twice - once in a HashMap and once in a data structure that sorts by value - for example, a SortedMap with key and value reversed.
If you find you don't need to retrieve by key, just reverse key and value.
No, there is no quick and efficient way of doing that - you need to loop through the entire hash map. The reason for it is that the keys and values in hash maps do not observe any particular order.
No, because otherwise there would exist a sorting algorithm in O(n log n) (probabilistic, though): add all elements to the hash map, than extract the lowest one by one.
//create hashmap
HashMap<Integer, String> yourHashmap = new HashMap<>();
//add your values here
yourHashmap.put(1,"200");
yourHashmap.put(3,"400");
yourHashmap.put(5,"1");
//then create empty arraylist
ArrayList<Integer> listDuplicates = new ArrayList<Integer>();
//filing the empty arraylist with all id's from duplicateHashmap
for (Map.Entry<Integer, String> entry : yourHashmap.entrySet()) {
listDuplicates.add(entry.getKey());
}
//Ordering the numbers
Collections.sort(listDuplicates);
for (Integer num : listDuplicates) {
int id = num; //entry
String number2 = duplicateHashmap.get(num);//value
System.out.println("lowest value = "+id+" : "+number2);
//breaking here because we've found the lowest value...
break;
}

How to get element position from Java Map

I have this Java Map:
Can you tell me how I can get the 6-th element of the Map?
private static final Map<String, Users> cache = new HashMap<>();
is this possible? Or I have to use another Java collection?
Though a bit late to answer. But the option is to use LinkedHashMap: this map preserves the order according to insertion of elements, as everyone has suggested. However, As a warning, it has a constructor LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) which will create a linked hash map whose order of iteration is the order in which its entries were last accessed. Don't use this constructor for this case.
However, if I needed such functionality, i would extend it and implement my necessary function to re-use them in OOP way.
class MyLinkedMap<K, V> extends LinkedHashMap<K, V>
{
public V getValue(int i)
{
Map.Entry<K, V>entry = this.getEntry(i);
if(entry == null) return null;
return entry.getValue();
}
public Map.Entry<K, V> getEntry(int i)
{
// check if negetive index provided
Set<Map.Entry<K,V>>entries = entrySet();
int j = 0;
for(Map.Entry<K, V>entry : entries)
if(j++ == i)return entry;
return null;
}
}
Now i can instantiate it and can get a entry and value either way i want:
MyLinkedMap<String, Integer>map = new MyLinkedMap<>();
map.put("a first", 1);
map.put("a second", 2);
map.put("a third", 3);
System.out.println(map.getValue(2));
System.out.println(map.getEntry(1));
Output:
3
a second=2
HashMap doesn't grantee the order. If you concern about order you should use LinkedHashMap
Map<String, Users> orderedMap=new LinkedHashMap<>();
Now when you put an element it will keep the order what you put.
If you want to get 6th element, now you can do it since you have your elements in order.
orderedMap.values().toArray()[5]// will give you 6th value in the map.
Example
Map<String, String> orderedMap=new LinkedHashMap<>();
orderedMap.put("a","a");
orderedMap.put("b","b");
System.out.println(orderedMap.values().toArray()[1]); // you will get b(value)
System.out.println(orderedMap.keySet().toArray()[1]); // you will get b(key)
}
A HashMap does not maintain the order of the elements inserted in it. You can used a LinkedHashMap instead which maintains the order of the elements inserted in it.
Though you need to note that even a LinkedHashMap has no such method which would give the element at a particular index. You will have to manually iterate through the entries and extract the element at the 6th iteration.
With guava's Iterables
Iterables.get(map.entrySet(),6);
The HashMap has no defined ordering of keys.It's Unordered.
You can use LinkedHashMap which will store your keys in order of insertion.You can retrieve them by calling keySet().
HashMaps do not preserve ordering:
LinkedHashMap which guarantees a predictable iteration order.
Example
public class Users
{
private String Id;
public String getId()
{
return Id;
}
public void setId(String id)
{
Id = id;
}
}
Users user;
LinkedHashMap<String,Users> linkedHashMap = new LinkedHashMap<String,Users>();
for (int i = 0; i < 3; i++)
{
user = new Users();
user.setId("value"+i);
linkedHashMap.put("key"+i,user);
}
/* Get by position */
int pos = 1;
Users value = (new ArrayList<Users>(linkedHashMap.values())).get(pos);
System.out.println(value.getId());
According to documentation, HashMap is a Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
That's why it is not wise to use this kind of Collection.
UPDATE:
Based on #Prateek implementation of LinkedHashMap I would suggest something like:
LinkedHashMap<String,User> linkedHashMap = new LinkedHashMap<String,User>();
// or LinkedHashMap<String,User> linkedHashMap = new LinkedHashMap<>(); //for java 7+
linkedHashMap.put("1",userObj1);
linkedHashMap.put("2",userObj2);
linkedHashMap.put("3",userObj3);
/* Get by position */
int pos = 1; // Your position
User tmp= (new ArrayList<User>(linkedHashMap.values())).get(pos);
System.out.println(tmp.getName());
A HashMap doesn't have a position. You can iterate through its KeySet or EntrySet, and pick the nth element, but it's not really the same as a position. A LinkedHashMap does have a position, since it has a predictable iteration order.
You need to use a LinkedHashMap in order to be able to tell the order of the inserted elements. HashMap is not capable of doing so.
There is no Order in HashMap. You can obtain the list of may keys using map.keySet() but there's no guarantee the key set will be in the order which you add it in. Use LinkedHashMap instead of HashMap It will always return keys in same order (as insertion)
Correct!!
you will have to use other collection for getting values on index(position).
You can use ArrayList
If the ordering is to mean anything significant, you could consider using a SortedMap.
Concrete implementation: TreeMap
Use LinkedHashMap instead of HashMap It will return keys in same order (as insertion) when calling keySet().
For mare detail about LinkedHashMap see this
For example to get the element from specific index
Create a new list from your values and get the value based on index.
LinkedHashMap<String, List<String>> hMap;
List<List<String>> l = new ArrayList<List<String>>(hMap.values());
l.get(6);

Is it possible to get element from HashMap by its position?

How to retrieve an element from HashMap by its position, is it possible at all?
Use a LinkedHashMap and when you need to retrieve by position, convert the values into an ArrayList.
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<String,String>();
/* Populate */
linkedHashMap.put("key0","value0");
linkedHashMap.put("key1","value1");
linkedHashMap.put("key2","value2");
/* Get by position */
int pos = 1;
String value = (new ArrayList<String>(linkedHashMap.values())).get(pos);
HashMaps do not preserve ordering:
This class makes no guarantees as to
the order of the map; in particular,
it does not guarantee that the order
will remain constant over time.
Take a look at LinkedHashMap, which guarantees a predictable iteration order.
If you want to maintain the order in which you added the elements to the map, use LinkedHashMap as opposed to just HashMap.
Here is an approach that will allow you to get a value by its index in the map:
public Object getElementByIndex(LinkedHashMap map,int index){
return map.get( (map.keySet().toArray())[ index ] );
}
If you, for some reason, have to stick with the hashMap, you can convert the keySet to an array and index the keys in the array to get the values in the map like so:
Object[] keys = map.keySet().toArray();
You can then access the map like:
map.get(keys[i]);
Use 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.
Use LinkedHashMap and use this function.
private LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
Define like this and.
private Entry getEntry(int id){
Iterator iterator = map.entrySet().iterator();
int n = 0;
while(iterator.hasNext()){
Entry entry = (Entry) iterator.next();
if(n == id){
return entry;
}
n ++;
}
return null;
}
The function can return the selected entry.
By default, java LinkedHasMap does not support for getting value by position. So I suggest go with customized IndexedLinkedHashMap
public class IndexedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private ArrayList<K> keysList = new ArrayList<>();
public void add(K key, V val) {
super.put(key, val);
keysList.add(key);
}
public void update(K key, V val) {
super.put(key, val);
}
public void removeItemByKey(K key) {
super.remove(key);
keysList.remove(key);
}
public void removeItemByIndex(int index) {
super.remove(keysList.get(index));
keysList.remove(index);
}
public V getItemByIndex(int i) {
return (V) super.get(keysList.get(i));
}
public int getIndexByKey(K key) {
return keysList.indexOf(key);
}
}
Then you can use this customized LinkedHasMap as
IndexedLinkedHashMap<String,UserModel> indexedLinkedHashMap=new IndexedLinkedHashMap<>();
TO add Values
indexedLinkedHashMap.add("key1",UserModel);
To getValue by index
indexedLinkedHashMap.getItemByIndex(position);
I'm assuming by 'position' you're referring to the order in which you've inserted the elements into the HashMap. In that case you want to be using a LinkedHashMap. The LinkedHashMap doesn't offer an accessor method however; you will need to write one like
public Object getElementAt(LinkedHashMap map, int index) {
for (Map.Entry entry : map.entrySet()) {
if (index-- == 0) {
return entry.value();
}
}
return null;
}
Another working approach is transforming map values into an array and then retrieve element at index. Test run of 100 000 element by index searches in LinkedHashMap of 100 000 objects using following approaches led to following results:
//My answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.values().toArray(new Particle[map.values().size()])[index];
} //68 965 ms
//Syd Lambert's answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.get( (map.keySet().toArray())[ index ] );
} //80 700 ms
All in all retrieving element by index from LinkedHashMap seems to be pretty heavy operation.
HashMap - and the underlying data structure - hash tables, do not have a notion of position. Unlike a LinkedList or Vector, the input key is transformed to a 'bucket' where the value is stored. These buckets are not ordered in a way that makes sense outside the HashMap interface and as such, the items you put into the HashMap are not in order in the sense that you would expect with the other data structures
HashMap has no concept of position so there is no way to get an object by position. Objects in Maps are set and get by keys.
HashMaps don't allow access by position, it only knows about the hash code and and it can retrieve the value if it can calculate the hash code of the key. TreeMaps have a notion of ordering. Linkedhas maps preserve the order in which they entered the map.
you can use below code to get key :
String [] keys = (String[]) item.keySet().toArray(new String[0]);
and get object or list that insert in HashMap with key of this item like this :
item.get(keys[position]);
You can try to implement something like that, look at:
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("juan", 2);
map.put("pedro", 3);
map.put("pablo", 5);
map.put("iphoncio",9)
List<String> indexes = new ArrayList<String>(map.keySet()); // <== Parse
System.out.println(indexes.indexOf("juan")); // ==> 0
System.out.println(indexes.indexOf("iphoncio")); // ==> 3

A Java collection of value pairs? (tuples?)

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.

Categories

Resources