List of entries, how to add a new Entry? - java

In Java, I am implementing this:
List<Entry<String, Integer>> listObjects = new ArrayList<Entry<String, Integer>>();
but how can I add a new Entry?
as it does not work with: listObjects.add(new Entry<"abc", 1>());
thanks in advance.

I know it's a pretty older thread but you can do it as follows:
listObjects.add(new java.util.AbstractMap.SimpleEntry<String, Integer>("abc", 1));
It might help someone like me, who was trying to do this recently!
I hope it helps :-)

Do you mean Map.Entry? That is an interface (so you can't instantiate without an implementation class, you can learn about interfaces in the Java Tutorial). Entry instances are usually only created by Map implementations and only exposed through Map.entrySet()
Of course, since it's an interface you could add your own implementation, something like this:
public class MyEntry<K, V> implements Entry<K, V> {
private final K key;
private V value;
public MyEntry(final K key) {
this.key = key;
}
public MyEntry(final K key, final V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(final V value) {
final V oldValue = this.value;
this.value = value;
return oldValue;
}
}
That way you could do listObjects.add(new MyEntry<String,Integer>("abc", 1))
But that doesn't really make sense outside of a map context.

Entry is a parametized class and you need to create instances of Entry with a constructor (typical way).
Without knowing the implementation of Entry: this could already work (at least it shows how it usually works):
// create a list
List<Entry<String, Integer>> listObjects =
new ArrayList<Entry<String, Integer>>()
// create an instance of Entry
Entry<String, Integer> entry = new Entry<String, Integer>("abc", 1);
// add the instance of Entry to the list
listObjects.add(entry);
With Map.Entry it is somewhat different (OP just mentioned, that Entry is Map.Entry in Fact. We can't create Map.Entry instances, we usually get them from an existing map:
Map<String, Integer> map = getMapFromSomewhere();
List<Map.Entry<String, Integer>> listObjects =
new ArrayList<Map.Entry<String, Integer>>();
for (Map.Entry<String, Integer> entry:map.entrySet())
listObjects.add(entry);

One way to use a ready made implementation of Entry would be Guava's Immutable Entry
Something like this listObjects.add(Maps.immutableEntry("abc",1));

Perhaps listObjects.add(new Entry<String, Integer>("abc", 1));? The generics specify the type (String, Integer), not the values (abc, 1).
Edit - Note that it was later added that the Entry is in fact Map.Entry, so you need to create an implementation of Map.Entry that you later instantiate, exactly as the chosen answer explains.

Related

How to get a specific Map.Entry<K, V> for a specific key?

I'm implementing a NavigableMap-implementing LinkedHashMap in Java. (There don't seem to be many (any?) reasons why LinkedHashMap doesn't implement NavigableMap already, but I digress...)
I've written lowerKey(), lowerEntry(), higherKey(), and higherEntry() by iterating the entrySet(). I don't see any way in these cases to avoid iterating the entire entrySet().
For floorKey(), floorEntry(), ceilingKey(), and ceilingEntry(), in the case that the key exists, I'd like to avoid the expense of iterating the entrySet(), considering that I can already get the value with plain-old get().
Is there a way to get the Map.Entry for a particular key, rather than just the value? Thanks.
You have the key, and you can get the value associated with the key using get, now all you gotta do is to make a Map.Entry, and we can do that with the Map.entry factory method:
var value = theBackingLinkedHashMap.get(key);
if (value == null) {
return null;
}
return Map.entry(key, value);
The entry returned by entry does have two caveats that you should be aware of:
does not allow null keys, so your NavigableLinkedHashMap would need to not allow null keys either
is immutable, so you cannot call setValue.
But other than that, it will work as if you got the Map.Entry from inside the backing LinkedHashMap, and it does fulfil the contract of ceilingEntry, floorEntry etc, since they just ask for a "a key-value mapping", and doesn't require that it has to have the mutability as the map itself or anything like that. For example, this is ceilingEntry:
Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
I would use TreeSet to keep the keys in the NavigableMap class. See example below:
import java.util.*;
public class Main{
public static void main(String[] args) {
NavMap<Integer, String> map = new NavMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.put(4, "four");
map.put(10, "ten");
System.out.println(map.lowerKey(3)); //2
System.out.println(map.higherKey(3)); //4
System.out.println(map.higherEntry(3)); // 4:four
System.out.println(map.ceilingKey(7)); //10
System.out.println(map.floorKey(7)); //4
System.out.println(map.floorEntry(10)); // 4:four
}
}
class NavMap<K extends Comparable,V extends Comparable> extends LinkedHashMap<K,V>{
private TreeSet<K> keys = new TreeSet<>();
public Set<K> keySet(){ return keys; }
public K lowerKey (K k){ return keys.lower(k); }
public K higherKey (K k){ return keys.higher(k); }
public K floorKey (K k){ return keys.floor(k); }
public K ceilingKey(K k){ return keys.ceiling(k);}
public Map.Entry<K,V> lowerEntry(K k) { return newEntry(lowerKey(k), get(lowerKey(k)));}
public Map.Entry<K,V> higherEntry(K k) { return newEntry(higherKey(k), get(higherKey(k)));}
public Map.Entry<K,V> floorEntry(K k) { return newEntry(floorKey(k), get(floorKey(k)));}
public Map.Entry<K,V> ceilingEntry(K k) { return newEntry(ceilingKey(k),get(ceilingKey(k)));}
private Map.Entry<K,V> newEntry(K k, V v) { return new AbstractMap.SimpleEntry<>(k,v);}
public V put(K key, V value){
keys.add(key);
return super.put(key, value);
}
}
Output:
2
4
4=four
10
4
10=ten
#Sweeper's comment on his answer got me thinking. The solution I came up with was to maintain a Map from Key to Entry inside my class. That way I have a way of having O(1) access to the entrySet. Like so:
Map<K, Map.Entry<K, V>> entryMap = new HashMap<>();
for(final currentEntry : entrySet())
{
entryMap.put(currentEntry.getKey(), currentEntry);
}
I just need to update this Map every time an operation runs which changes the keySet. I only need to update the one entry in this Map that would be affected. That sounds like it wouldn't be very expensive.
You could do something like this.
Map<String,Integer> map = Map.of("Foo", 123, "Bar", 234);
Function<String, Entry<String,Integer>> getEntry =
getEntryFnc(map);
System.out.println(getEntry.apply("Foo"));
System.out.println(getEntry.apply("Bar"));
System.out.println(getEntry.apply("Baz"));
prints
Foo=123
Bar=234
Baz=null
Returns a lambda which builds an entry using the supplied key and map.
public static <K,V> Function<K, Entry<K,V>> getEntryFnc(Map<K,V> map) {
return key->
new AbstractMap.SimpleEntry<>(key, map.get(key));
};
}

Why LinkedCaseInsensitiveMap uses both LinkedHashMap and HashMap?

I'm looking at the structure of LinkedCaseInsensitiveMap (spring framework 5.0.5.RELEASE). I'm curious why LinkedCaseInsensitiveMap uses both LinkedHashMap and HashMap, and why not just use LinkedHashMap like this?
private final LinkedHashMap<String, V> targetMap;
public V get(Object key) {
if (key instanceof String) {
return this.targetMap.get(convertKey((String) key));
}
return null;
}
private final LinkedHashMap<String, V> targetMap;
private final HashMap<String, String> caseInsensitiveKeys;
In this case targetMap contains real-case string to your object, and caseInsensitiveKeys contains mapping your key in lower case to your real-case key.
It allows to show you real-case keys when you are doing for-each iteration, but at the same time it allows you to have case insensitivity.
So let say, following code:
LinkedCaseInsensitiveMap<Object> map = new LinkedCaseInsensitiveMap<>();
map.put("MyCustomObject", new Object());
will put "MyCustomObject" -> new Object() in targetMap, and "mycustomobject" -> "MyCustomObject" in caseInsensitiveKeys. And now if you try to print all objects from your map it will print it as you added and not changed keys. You can't archive it without second map.

N Level Map, Need to place map inside map

Need a dynamic data structure, which may be similar to a MAP(Java.util.Map), wchich is capable of storing, String and Object. And that Object may again need to store another map, which can store, String and Object.
I suspect that the requester is looking for something like the below:
class MultilevelMap<K,V> extends HashMap<List<K>,V> {
#SafeVarargs
public final put(V value, K keys...) {
put(makeKey(keys), value);
}
#SafeVarargs
public final V get(K keys...) {
return get(makeKey(keys));
}
// The remainder of this class is left as a tedious exercise for the reader
private List<K> makeKey(K[] keys) {
List<K> key = new ArrayList<K>(keys.size);
for(K k: keys) {
key.add(k);
}
return key;
}
}
A Trie, as far as I understand it is similar, but opposite. It presents an interface of Map<S,V>, but internally is implemented as a variable-depth Map<K,Map<K, ... V>> where K are consecutive affixes of S, such that if you concatenate all of the Ks between the top of the tree and V, you get the S you used as the key. The above presents an interface of (very approximately) Map<K,K, ... , V>, but internally is Map<List<K>, V>.
You can nest maps (and other containers) to arbitrary depth. This puts a Map in another Map in another ... to a total depth of 10:
private Map<String, Object> nest(int levelsLeft, Map<String, Object> parent) {
if (levelsLeft > 0) {
parent.put("key" + levelsLeft,
nest(levelsLeft - 1, new HashMap<String, Object>()));
}
return parent;
}
// from somewhere else
Map<String, Object> nested = nest(10, new Map<String, Object>());
((Map<String, Object>)nested.get("key10")).get("key9"); // goes all the way down to "key1"
Note that the price for declaring a Map<String, Object> is that, whenever you access something via get(), you need to cast it to whatever it actually is to be able to use it as something more specific than an Object.
Sounds like you either need a Multimap or a Trie.

a hashmap with multiple keys to value?

I have a question about hashmaps with multiple keys to value. Let's say I have (key / value )
1/a, 1/b, 1/3, 2/aa, 2/bb, 2/cc.
Would this work?
If it does, could I have a way to loop through it and display all values for only either key 1 or 2?
You can use a map with lists as values, e.g.:
HashMap<Integer, List<String>> myMap = new HashMap<Integer, List<String>>();
java.util.HashMap does not allow you to map multiple values to a single key. You want to use one of Guava's Multimap's. Read through the interface to determine which implemented version is suitable for you.
A simple MultiMap would look something like this skeleton:
public class MultiMap<K,V>
{
private Map<K,List<V>> map = new HashMap<K,List<V>>();
public MultiMap()
{
// Define constructors
}
public void put(K key, V value)
{
List<V> list = map.get(key);
if (list == null)
{
list = new ArrayList<V>();
map.put(key, list);
}
list.add(value);
}
public List<V> get(K key)
{
return map.get(key);
}
public int getCount(K key)
{
return map.containsKey(key) ? map.get(key).size() : 0;
}
}
It cannot directly implement Map<K,V> because put can't return the replaced element (you never replace). A full elaboration would define an interface MultiMap<K,V> and an implementation class, I've omitted that for brevity, as well as other methods you might want, such as V remove(K key) and V get(K key, int index)... and anything else you can think of that might be useful :-)
Maps will handle multiple keys to one value since only the keys need be unique:
Map(key, value)
However one key to multiple values requires s multimap of a map strict of :
Map(key, list(values))
Also, whatever you use as a key really should implement a good hadhCode() function if you decide to use a HashMap and/or HashSet
Edit: had to use() instead of <> because my mobile or sof's mobile site editor clobbered the <> symbols....odd

Two way collections in Java

I have a list of objects. The objects are given an ID and stored in a Hashtable. If I need an object with particular ID, I simply say:
ht.get(ID);
However, sometimes I need to get the ID for a given object:
ht.get(Object);
My first idea is to use two different HashTables; one for ID -> Object mapping and the other for Object -> ID mapping.
Does this sound like a good enough solution?
If you cannot use external collections (as you seem to not want to use given one of your comments) you could write a simple class to do what you want (which, yes, is essentially your first thought), along the lines of (I didn't compile this, and it is just a first thought so could be a bad idea, etc ...):
EDIT: now there are two versions, one that allows for duplicate values and one that does not. The ones that does not will remove the key if the value is overwritten.
This version does not allow duplicate values:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, K> valueKey;
{
keyValue = new HashMap<K, V>();
valueKey = new HashMap<V, K>();
}
// this makes sure that if you do not have duplicate values.
public void put(final K key, final V value)
{
if(keyValue.containsValue(value))
{
keyValue.remove(valueKey.get(value));
}
keyValue.put(key, value);
valueKey.put(value, key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public K getKeyForValue(final V value)
{
return (valueKey.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
This version allows duplicated values and gives you back a list of all of the keys that have a given value:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, List<K>> valueKeys;
{
keyValue = new HashMap<K, V>();
valueKeys = new HashMap<V, List<K>>();
}
public void put(final K key, final V value)
{
List<K> values;
keyValue.put(key, value);
values = valueKeys.get(value);
if(values == null)
{
values = new ArrayList<K>();
valueKeys.put(value, values);
}
values.add(key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public List<K> getKeyForValue(final V value)
{
return (valueKeys.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
Hiding the two maps in a class is a good idea, because of you find a better way later all you need to do is replace the innards of the class and the rest of your code is left untouched.
If using an external library is OK, you should check BiMap on google collections:
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/BiMap.html
What you are looking for is a bidirectional map. You can find it in the commons collections in the classes implementing the BidiMap interface or the Google Guava.
What you are looking for is a Bi-directional Map.
Try Apache Collections BidiMap.
http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/BidiMap.html
Not that I know of immediatley but you can build one ... How about having a single collection of your objects and several lookup structures (hashmaps or trees) that don't store the objects themselves (for memory saving reasons) but the index into your single collection? This way you use the appropriate lookup structure you need (Id -> object or vice versa) get back an integer value that you can index into your original collection. This way you can do more than a bidirectional lookup in case you need to do so in the future.

Categories