A KeyValuePair in Java [duplicate] - java

This question already has answers here:
A Java collection of value pairs? (tuples?)
(22 answers)
Closed 2 years ago.
I'm looking for a KeyValuePair class in Java.
Since java.util heavily uses interfaces there is no concrete implementation provided, only the Map.Entry interface.
Is there some canonical implementation I can import?
It is one of those "plumbers programming" classes I hate to implement 100x times.

The class AbstractMap.SimpleEntry is generic and can be useful.

Android programmers could use BasicNameValuePair
Update:
BasicNameValuePair is now deprecated (API 22).
Use Pair instead.
Example usage:
Pair<Integer, String> simplePair = new Pair<>(42, "Second");
Integer first = simplePair.first; // 42
String second = simplePair.second; // "Second"

The Pair class from Commons Lang might help:
Pair<String, String> keyValue = new ImmutablePair("key", "value");
Of course, you would need to include commons-lang.

Use of javafx.util.Pair is sufficient for most simple Key-Value pairings of any two types that can be instantiated.
Pair<Integer, String> myPair = new Pair<>(7, "Seven");
Integer key = myPair.getKey();
String value = myPair.getValue();

import java.util.Map;
public class KeyValue<K, V> implements Map.Entry<K, V>
{
private K key;
private V value;
public KeyValue(K key, V value)
{
this.key = key;
this.value = value;
}
public K getKey()
{
return this.key;
}
public V getValue()
{
return this.value;
}
public K setKey(K key)
{
return this.key = key;
}
public V setValue(V value)
{
return this.value = value;
}
}

I like to use
Properties
Example:
Properties props = new Properties();
props.setProperty("displayName", "Jim Wilson"); // (key, value)
String name = props.getProperty("displayName"); // => Jim Wilson
String acctNum = props.getProperty("accountNumber"); // => null
String nextPosition = props.getProperty("position", "1"); // => 1
If you are familiar with a hash table you will be pretty familiar with this already

You can create your custom KeyValuePair class easily
public class Key<K, V>{
K key;
V value;
public Key() {
}
public Key(K key, V value) {
this.key = key;
this.value = value;
}
public void setValue(V value) {
this.value = value;
}
public V getValue() {
return value;
}
public void setKey(K key) {
this.key = key;
}
public K getKey() {
return key;
}
}

My favorite is
HashMap<Type1, Type2>
All you have to do is specify the datatype for the key for Type1 and the datatype for the value for Type2. It's the most common key-value object I've seen in Java.
https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html

Hashtable<String, Object>
It is better than java.util.Properties which is by fact an extension of Hashtable<Object, Object>.

I've published a NameValuePair class in GlobalMentor's core library, available in Maven. This is an ongoing project with a long history, so please submit any request for changes or improvements.

Related

Generic in Java with Interface [duplicate]

This question already has answers here:
Instantiating interfaces in Java
(15 answers)
Closed 4 years ago.
I was reading the tutorial from https://docs.oracle.com/javase/tutorial/java/generics/types.html.
Code:
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
But above code, i can't grasp.
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello",
"world");
It seems like creating an object of Interface, but as per my understanding of OOP in c++, it's impossible to create an object of Abstract Class(My understanding is that Interface is somehow like Abstract Class). Needed a more specific answer "How is it possible to create such object?"
Edited
As i was suggested to view answer at Instantiating interfaces in Java.
What's the differences between instantiation of object in following two cases:
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
OrderPair<String, Integer> p2 = new OrderedPair<String, Integer>("Even", 8);
You're actually not instantiating an interface. You are instantiating a class which implements the interface, then saving the object to a variable of the interface's type.
Instantiating the interface would be something like this, which you cannot do.
Pair<String, Integer> p1 = new Pair<>("Even", 8);
However, you can make it an anonymous class.
Pair<String, String> p2 = new Pair<String, String>() {
#Override
public String getKey() {
return "hello";
}
#Override
public String getValue() {
return "world";
}
};

Java implementing Map<String, String> class and use of iterator

Can anyone please explain me the meaning of implementing Map class and how should i create an iterator? Iv'e googled this in the past hour and i don't really understand how should implement such interface.
Thanks in advance for any information about it.
One site that may be of interest to you - http://www.sergiy.ca/how-to-iterate-over-a-map-in-java/ and example from there at hand:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Yes it is a bit hard-to-grasp, but please thoroughly study these:
Implementing a simple Map-Entry
public class GenericEntry<KeyType , ValueType> {
private final KeyType key;
private ValueType value;
public MyEntry(KeyType key, ValueType value) {
this.key = key;
this.value = value;
}
public KeyType getKey() {
return key;
}
public ValueType getValue() {
return value;
}
public void setValue(ValueType value) {
this.value = value;
}
}
Source: How to implement Map(and other data struct.) in plain Java
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Map.html

Bidirectional Map

Can you suggest a kind of map or similar data structure where we can get both the value and key from each other at equal ease. That is to say, that each may be used to find other.
Java doesn't have a bidirectional map in its standard library.
Use for example BiMap<K, V> from Google Guava .
If you feel it pain importing some third party library.
How about this simple class.
public class BiMap<K,V> {
HashMap<K,V> map = new HashMap<K, V>();
HashMap<V,K> inversedMap = new HashMap<V, K>();
void put(K k, V v) {
map.put(k, v);
inversedMap.put(v, k);
}
V get(K k) {
return map.get(k);
}
K getKey(V v) {
return inversedMap.get(v);
}
}
Make sure K and V class has proper hashCode implementation.
The most common solution is using two maps. You can easily encapsulate them in a class with a friendly interface by extending AbstractMap. (Update: This is how Guava's HashBiMap is implemented: two maps)
Creating a new data structure using nothing but arrays and custom classes has few advantages. The map implementations are lightweight wrappers of a data structure that indexes the keys. Since you need two indexes you might as well use two complete maps.
Also try Apache Commons Collections 4 BidiMap Package.
Google Guava contains a BiMap (BiDirectional Map).
well for the average usecase where you need a Dictionary like that, I see nothing wrong with a KISS solution, just put'ting the key and value vice versa, saving the overhead of a second Map or even library only for that purpose:
myMap.put("apple", "Apfel");
myMap.put("Apfel", "apple");
Based on this answer in this QA and its comments I wrote following. [Will be tested]
Bidirectional Map
import java.util.HashMap;
public class BidirectionalMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
public HashMap<V, K> inversedMap = new HashMap<V, K>();
public K getKey(V value) {
return inversedMap.get(value);
}
#Override
public int size() {
return this.size();
}
#Override
public boolean isEmpty() {
return this.size() > 0;
}
#Override
public V remove(Object key) {
V val=super.remove(key);
inversedMap.remove(val);
return val;
}
#Override
public V get(Object key) {
return super.get(key);
}
#Override
public V put(K key, V value) {
inversedMap.put(value, key);
return super.put(key, value);
}
}
You can define an enum and define helper method to get key. Performance is way too far better compared to BidiMap.
E.g
public enum Fruit {
APPLE("_apple");
private final String value;
Fruit(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
public static String getKey(String value){
Fruit fruits[] = Fruit.values();
for(Fruit fruit : fruits){
if(value.equals(fruit.value)){
return fruit.name();
}
}
return null; }
}
Based on this tutorial I suggest the following as answer:
public class IdToNames {
public static void main(String[] args){
BidiMap<String, Integer> map = new DualHashBidiMap<>();
map.put("NameA", 100);
map.put("NameB", 200);
System.out.println(map.size()); //2 as expected
System.out.println(map.get("NameA")); //100 as expected
System.out.println(map.getKey(100)); //"NameA" as expected
}
}
Note the problem of duplicated keys and/or values described in this question here

Is there a basic id / value object in Java?

I've created an "Attribut" class which is just a wrapper for a key/value single item. I know that Maps and HashMaps are designed for lists of this kind of items so I feel like i reinvented the wheel...
Is there some Class which fulfill this purpose ?
Regards
( My code to be clear about what i'm looking for )
public class Attribut {
private int id;
private String value;
#Override
public String toString() {
return value;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
You can reuse Map.Entry<K, V>:
http://docs.oracle.com/javase/6/docs/api/java/util/Map.Entry.html
In your case it'd be Map.Entry<Integer, String>.
HashMap !
example :
Map<Integer,String> attribut = new HashMap<Integer, String>();
attribut.put(1, "hi");
String value = attribut.get(1);
you can iterate :
for (Integer key : attribut.keySet()) {
value = attribut.get(key);
}
EDIT :
OK, just for a Pair !
public class Pair<K, V> {
private final K element0;
private final V element1;
public static <K, V> Pair<K, V> createPair(K key, V value) {
return new Pair<K, V>(key, value);
}
public Pair(K element0, V element1) {
this.element0 = element0;
this.element1 = element1;
}
public K getElement0() {
return element0;
}
public V getElement1() {
return element1;
}
}
usage :
Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();
Immutable, only a pair !
You can use AbstractMap.SimpleEntry. There is also a SimpleImmutableEntry.
However, I believe that it is not wrong designing your own type. There is a plethora of examples in the JDK itself where something like this (tuple) has been done:
java.awt.Dimension
java.awt.Point
I believe that it's a good thing, since you're code is more easily readable and you gain additional type safety.
You're not "reinventing the wheel", you just specifying your requirements. You want a class that constitutes a mutable int/String pair, and so your code is OK.
Your problem is that Java syntax is overly verbose. It would be nice to simply define it as something like
class IdValuePair(id: int, value: String)
but that's something for other languages.
You could use [Collections.singletonMap()](http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#singletonMap(K, V)).

MultiKeyMap get method

I want to use MultiKeyMap from Apache Collection, because I need a HashMap with two keys and a value.
To put elements I do this:
private MultiKeyMap multiKey = new MultiKeyMap();
multiKey.put("key1.1", "key2.1", "value1");
And for get element I do this:
String s = multiKey.get("key1.1");
But the String s cames null... If I pass the two keys, like that:
String s = multiKey.get("key1.1", "key2.1");
The String s cames with values value1...
How can I extend the MultiKeyMap to get the right value when I pass only one of the two keys?
If you need only one key to get a value you have a plain old HashMap.
private Map<String, String> map = new HashMap<>();
map.put("key1.1", "value1");
map.put("key2.1", "value1");
And for get element you can do this:
String s = map.get("key1.1"); // s == "value1"
MultiKeyMap is required when both keys must be provided.
If you specify a value with two keys, you are going to need both keys to get it back. The hash function is not designed to return all the possible values that are associated with only one of the two keys. You may need to find a different data structure to do this.
MultiKeyMap is about using tuples as keys, not about matching one value to more than one key. Use a normal map and just put your value twice, with different keys.
Some more caution is needed when removing values. When you remove a value for the first key, do you want to automatically remove other keys with the same value? If so, you need either to loop over all keys and remove those with same value by hand, which could be inefficient, or keep some kind of reverse map to quickly find keys for specific value.
I don't know exact solution to your problem. But I suggest you to implement it like:
Map<K2, K1> m2;
Map<K1, V> m1;
And see: How to implement a Map with multiple keys?
It seems that you just do not need MultiKeyMap. You need regular map. Using it you can associate the same value with as many keys as you want.
Map<String, String> map = new HashMap<String, String>();
Object value = .....
map.put("key1", value);
map.put("key2", value);
..................
if(map.get("key1") == map.get("key2")) {
System.out.println("the same value stored under 2 different keys!");
}
You just can't since it's not the way a MultiKeyMap works. Put the value with separate keys and than try getting it with each key at a time.
Instead of that you can use table data stature from guava.
I would suggest to create a separate class for multiple keys:
public class Test {
Map<Shape, Book> test1 = new HashMap<>();
Book book = new Book("A");
test1.put(Shape, book);
private class Shape {
String id1;
String id2;
public Shape(String id1, String id2) {
this.id1 = id1;
this.id2 = id2;
}
#Override
public boolean equals(Object o) {//}
#Override
public int hashCode() {//}
}
}
Here is a simple MultiKeyMap implementation that worked for me.
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class MultiMap<K, V> implements Map<K, V>
{
private class MultiMapEntery implements java.util.Map.Entry<K, V>
{
private final K key;
private V value;
public MultiMapEntery(K key, V value)
{
this.key = key;
this.value = value;
}
#Override
public K getKey()
{
return key;
}
#Override
public V getValue()
{
return value;
}
#Override
public V setValue(V value)
{
V oldValue = this.value;
this.value = value;
return oldValue;
}
};
private final Map<K, String> keyMap = new HashMap<K, String>();
private final Map<String, Set<K>> inverseKeyMap = new HashMap<String, Set<K>>();
private final Map<String, V> valueMap = new HashMap<String, V>();
#Override
public void clear()
{
keyMap.clear();
inverseKeyMap.clear();
valueMap.clear();
}
#Override
public boolean containsKey(Object key)
{
return keyMap.containsKey(key);
}
#Override
public boolean containsValue(Object value)
{
return valueMap.containsValue(value);
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet()
{
Set<java.util.Map.Entry<K, V>> entries = new HashSet<>();
for(K key : keyMap.keySet())
{
V value = valueMap.get(key);
entries.add(new MultiMapEntery(key, value));
}
return entries;
}
#Override
public V get(Object key)
{
return valueMap.get(keyMap.get(key));
}
#Override
public boolean isEmpty()
{
return valueMap.isEmpty();
}
#Override
public Set<K> keySet()
{
return keyMap.keySet();
}
#Override
public V put(K key, V value)
{
String id = keyMap.get(key);
if(id == null)
{
id = UUID.randomUUID().toString();
}
keyMap.put(key, id);
Set<K> keys = inverseKeyMap.get(id);
if(keys == null)
{
keys = new HashSet<>();
}
keys.add(key);
inverseKeyMap.put(id, keys);
valueMap.put(id, value);
return value;
}
public V put(Set<K> keys, V value)
{
String id = null;
for(K key : keys)
{
id = keyMap.get(key);
if(id != null) // one of the keys already exists
{
break;
}
}
if(id == null)
{
id = UUID.randomUUID().toString();
}
for(K key : keys)
{
keyMap.put(key, id);
}
inverseKeyMap.put(id, keys);
valueMap.put(id, value);
return value;
}
#Override
public void putAll(Map<? extends K, ? extends V> map)
{
for(java.util.Map.Entry<? extends K, ? extends V> entry : map.entrySet())
{
put(entry.getKey(), entry.getValue());
}
}
#Override
public V remove(Object key)
{
String id = keyMap.get(key);
keyMap.remove(key);
Set<K> keys = inverseKeyMap.get(id);
keys.remove(key);
V value = valueMap.get(id);
if(keys.size() == 0) // it was the last key, now remove the value
{
valueMap.remove(id);
}
return value;
}
#Override
public int size()
{
return valueMap.size();
}
#Override
public Collection<V> values()
{
return valueMap.values();
}
public static void main(String[] args)
{
MultiMap<String, String> m = new MultiMap<>();
m.put("a", "v1");
Set<String> s = new HashSet<>();
s.add("b");
s.add("c");
s.add("d");
m.put(s, "v2");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
m.remove("a");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
s.add("a");
m.put(s, "v3");
System.out.println("size:" + m.size());
System.out.println("keys:" + m.keySet());
System.out.println("values:" + m.values().toString());
System.out.println("a:" + m.get("a"));
System.out.println("b:" + m.get("b"));
System.out.println("c:" + m.get("c"));
System.out.println("d:" + m.get("d"));
}
}
A little late, but you probably mean to get every result from the map, that matches the first element only, even though it contains multiple results, ignoring the second key (wildcard effect). Apache's MultiKeyMap is not suitable for this.
You could solve this by creating your own filter functionality using the MultiKey of MultiKeyMap. First, filter out only the relevant MultiKeys (which you get from yourMultiKeyMap.keySet() ) . The following method takes those multiKeys, and the first keys you want to filter on:
private Set<MultiKey<? extends String>> filterMultiKeys(Set<MultiKey<? extends String>> multiKeys, final String... keys) {
final List<String> givenKeys = Arrays.asList(keys);
return multiKeys.stream().filter(multiKey -> {
final Object[] actualKeys = multiKey.getKeys();
if (actualKeys.length < givenKeys.size()) {
// Lesser keys, so never a match
return false;
}
final List<Object> trimmedKeys = Arrays.asList(actualKeys).subList(0, givenKeys.size());
return trimmedKeys.equals(givenKeys);
}).collect(Collectors.toSet());
}
Then, use the resulting MultiKeys to get the results:
final Set<String> results = filteredKeys.stream().map(multiKey -> yourMultiKeyMap.get(multiKey)).collect(Collectors.toSet());
For bonus points, one could extend or decorate MultiKeyMap and create MyMultiKeyMap , having a method like match(keys...) using the filter functionality.

Categories

Resources