I have a map that should contain relations from an integer a to an integer b. Integer b should be in a Set. Relations from integer a to integer b can be added using the add method. To create such a relation, I have to create a new Set (to contain b) everytime the add method is called. How should I do this? I think I know how to do this with arrays since they support names containing variables, but sets don't.
public class intRelImplementation extends intRel {
protected final Map<Integer, Set<Integer>> connection;
public intRelImplementation (final int n) {
super(n);
connection = new HashMap<>();
}
#Override
public void add(int a, int b) {
// I have to create a new Set everytime the Add method is called.
// The Set should contain the Integer b, and this set should then be
// placed into the Map: Map<a, Set<b>>.
Set<Integer> setInMap = new HashSet<>(); //not correct obviously
Set setInMap2 = new HashSet(setInMap);
}
Suppose you call add(4,5) and then add(4,6).
If the result is that your map now contains 4 -> {5,6} (that is, the key 4 links to a set containing 5 and 6), then what you are making is a multi-valued map.
A way to add into a multi-valued map is something like this:
public void add(int a, int b) {
Set<Integer> values = connection.get(a);
if (values==null) {
values = new HashSet<Integer>();
connection.put(a, values);
}
values.add(b);
}
That is, get the set associated with the key a.
If there isn't one, create one and add it to the map.
Add your value b to the set.
Just add a new Set, if there isn't already a mapping for a in the Map and then add the value to the Set in the Map (whether it was just added or previously in the map):
connection.computeIfAbsent(a, k -> new HashSet<Integer>()).add(b);
I think this is what you're looking for
public class intRelImplementation extends intRel {
protected final Map<Integer, Set<Integer>> connection;
public intRelImplementation (final int n) {
super(n);
connection = new HashMap<>();
}
#Override
public void add(int key, int val) {
if(!connection.containsKey(key)){
connection.put(key, new HashSet<>());
}
connection.get(key).add(val);
}
...
}
!connection.containsKey(key) will check if the HashMap contains the key mapping. If it doesnt, it will add an mapped entry for {key, HashSet} where HashSet is an empty HashSet<Integer>
connection.get(key) will return the HashSet<Integer> associated to the key in the HashMap.
.add(val) will now add the value to the HashSet<Integer>
This guarantees that a hashset is created if the key doesnt exist, and then it adds the value to the set owned by the key
Related
I am having a problem since I have a list of products which contains several types, however, I want to show the general data but it was repeated in the list, now I can make them only exist once but the problem is that when I want to set the result when the element already existed in my Hash, it doesn't do the set, I don't know if it has to do with the Shallow Copy of Java, I tried Cloneable but it didn't succeed.
#RequiresApi(api = Build.VERSION_CODES.N)
public List<ProductsAlmacenInfo> getProductsTotal() {
HashMap<Long, ProductsAlmacenInfo> map = new HashMap<Long, ProductsAlmacenInfo>();
for (ProductsAlmacenInfo x: products) {
ProductsAlmacenInfo temp = x;
if(map.containsKey(x.getId_product().toString()) && (x.getStock_type().contentEquals("VTA") ||
x.getStock_type().contentEquals("VDO") || x.getStock_type().contentEquals("CHG"))) {
temp.setQty(Math.abs(x.getQty()+1));
map.replace(temp.getId_product(), temp);
}
else if (!map.containsKey(x.getId_product().toString())) {
temp.setQty(Math.abs(x.getQty()));
map.put(x.getId_product(), temp);
}
}
List<ProductsAlmacenInfo> productsTemp = new ArrayList<ProductsAlmacenInfo>(map.values());
if(productsTemp.isEmpty()) return products;
return productsTemp;
}[enter image description here][1]
To insert a new entry or combine with existing entry, use the merge() method:
HashMap<Long, ProductsAlmacenInfo> map = new HashMap<>();
for (ProductsAlmacenInfo x : products) {
x.setQty(Math.abs(x.getQty())); // Why?
map.merge(x.getId_product(), x, (a, b) -> {
ProductsAlmacenInfo temp = b.clone(); // or use copy-constructor
// or don't copy if not needed
temp.setQty(a.getQty() + b.getQty());
return temp;
});
}
All properties other than id_product and qty will have the values from the last object seen for that id_product. If you want values from the first seen object, clone a instead of b.
I am having a hard time understanding the right syntax to sort Maps which values aren't simply one type, but can be nested again.
I'll try to come up with a fitting example here:
Let's make a random class for that first:
class NestedFoo{
int valA;
int valB;
String textA;
public NestedFoo(int a, int b, String t){
this.valA = a;
this.valB = b;
this.textA = t;
}
}
Alright, that is our class.
Here comes the list:
HashMap<Integer, ArrayList<NestedFoo>> sortmePlz = new HashMap<>();
Let's create 3 entries to start with, that should show sorting works already.
ArrayList<NestedFoo> l1 = new ArrayList<>();
n1 = new NestedFoo(3,2,"a");
n2 = new NestedFoo(2,2,"a");
n3 = new NestedFoo(1,4,"c");
l1.add(n1);
l1.add(n2);
l1.add(n3);
ArrayList<NestedFoo> l2 = new ArrayList<>();
n1 = new NestedFoo(3,2,"a");
n2 = new NestedFoo(2,2,"a");
n3 = new NestedFoo(2,2,"b");
n4 = new NestedFoo(1,4,"c");
l2.add(n1);
l2.add(n2);
l2.add(n3);
l2.add(n4);
ArrayList<NestedFoo> l3 = new ArrayList<>();
n1 = new NestedFoo(3,2,"a");
n2 = new NestedFoo(2,3,"b");
n3 = new NestedFoo(2,2,"b");
n4 = new NestedFoo(5,4,"c");
l3.add(n1);
l3.add(n2);
l3.add(n3);
l3.add(n4);
Sweet, now put them in our Map.
sortmePlz.put(5,l1);
sortmePlz.put(2,l2);
sortmePlz.put(1,l3);
What I want now, is to sort the Entire Map first by its Keys, so the order should be l3 l2 l1.
Then, I want the lists inside each key to be sorted by the following Order:
intA,intB,text (all ascending)
I have no idea how to do this. Especially not since Java 8 with all those lambdas, I tried to read on the subject but feel overwhelmed by the code there.
Thanks in advance!
I hope the code has no syntatical errors, I made it up on the go
You can use TreeSet instead of regular HashMap and your values will be automatically sorted by key:
Map<Integer, ArrayList<NestedFoo>> sortmePlz = new TreeMap<>();
Second step I'm a little confused.
to be sorted by the following Order: intA,intB,text (all ascending)
I suppose you want to sort the list by comparing first the intA values, then if they are equal compare by intB and so on. If I understand you correctly you can use Comparator with comparing and thenComparing.
sortmePlz.values().forEach(list -> list
.sort(Comparator.comparing(NestedFoo::getValA)
.thenComparing(NestedFoo::getValB)
.thenComparing(NestedFoo::getTextA)));
I'm sure there are way of doing it with lambda but it is not actually required. See answer from Schidu Luca for a lambda like solution.
Keep reading if you want an 'old school solution'.
You cannot sort a map. It does not make sense because there is no notion of order in a map. Now, there are some map objects that store the key in a sorted way (like the TreeMap).
You can order a list. In your case, makes the class NestedFoo comparable (https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html). Then you can invoke the method Collections.sort (https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#sort-java.util.List-) on your lists.
Use TreeMap instead of HashMap, it solves the 1st problem: ordering entries by key.
After getting the needed list from the Map, you can sort the ArrayList by valA, valB, text:
l1.sort(
Comparator.comparing(NestedFoo::getValA).thenComparing(NestedFoo::getValB).thenComparing(NestedFoo::getTextA)
);
And change your NestedFoo class definition like this:
class NestedFoo {
int valA;
int valB;
String textA;
public NestedFoo(int a, int b, String t) {
this.valA = a;
this.valB = b;
this.textA = t;
}
public int getValA() {
return valA;
}
public void setValA(int valA) {
this.valA = valA;
}
public int getValB() {
return valB;
}
public void setValB(int valB) {
this.valB = valB;
}
public String getTextA() {
return textA;
}
public void setTextA(String textA) {
this.textA = textA;
}
}
When using treemap for sorting keep in mind that treemap uses compareTo instead of equals for sorting and to find duplicity. compareTo should be incosistent with equals and hashcode when implemented for any object which will be used as key. You can look for a detailed example on this link https://codingninjaonline.com/2017/09/29/unexpected-results-for-treemap-with-inconsistent-compareto-and-equals/
I am trying to build a many to one key value pair in java. Till now all I have manged is this
public class KeyStore {
int i=1;
Map<Integer,String> map1=new HashMap<Integer,String>();
Map<String,List<Integer>> map2=new HashMap<String,List<Integer>>();
public synchronized int put(String blobString) {
if(map1.containsValue(blobString)){
int r=blobString.hashCode()+i*blobString.hashCode();
i++;
map1.put(r, blobString);
List<Integer> j=map2.get(blobString);
List<Integer> k=j;
map2.remove(blobString);
k.add(r);
map2.put(blobString, k);
return r;
}
else{
map1.put(blobString.hashCode(),blobString);
List<Integer> x=new ArrayList<Integer>();
x.add(blobString.hashCode());
map2.put(blobString,x);
return blobString.hashCode();
}
}
public synchronized String get(int objectId) {
return map1.get(objectId);
}
What this does is if i put
ks.put("abc")
ks.put("abc")
Here ks is an instant of the class containing the above methods.
it results in
{1916062554=abc, 958031277=abc}
But what I want is
191602554,958031277=abc
and if i use get() on either of these keys it should output the value abc. Also delete() should delete the most recent key and not harm the other keys.
I thought of using
Map<ArrayList<Integer>,String> keystore=new HashMap<ArrayListInteger>,String>();
but I dont know how to implement the put method i.e how to insert a key in a map of lists. Need help with this.
EDIT 1
I am able to make the get and put methods work. Struggling with the delete method. Wrote it some what like this
Map<Integer,String> map1=new HashMap<Integer,String>();
Map<String,List<Integer>> map2=new HashMap<String,List<Integer>>();
public synchronized void delete(int objectId) {
map1.remove(objectId);
Iterator<Entry<String, List<Integer>>> it = map2.entrySet().iterator();
loop1: while (it.hasNext()) {
#SuppressWarnings("rawtypes")
Map.Entry pairs = (Map.Entry)it.next();
#SuppressWarnings("unchecked")
List<Integer> z=(List<Integer>) pairs.getValue();
if(z.contains(objectId)){
//System.out.println(z.size());
String key=(String) pairs.getKey();
System.out.println(z+" "+key);
if(z.size()==1){
map2.remove(key);
break loop1;
}
else{
z.remove(objectId);
map2.remove(key);
map2.put(key, z);
break loop1;
}
}
}
}
Basically map1 contains the mappings
123=>abc,456=>abc
and map2 contains
abc=>[123,456]
I am getting an arrayindexoutofbound exception. What I am trying in the delete method is to iterate across each blob String and then check in the list of values associated with the blobstring whetehr the required objectID is present. if it is then I remove that object id from the list and append the new mapping. Any help?
EDIT 2
The updated and working get and put methods are given above.
The Map JavaDoc says:
A map cannot contain duplicate keys; each key can map to at most one value.
But you can get around this by making the value a list of strings:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
<P>{#code java MultiValueHashMap}</P>
**/
public class MultiValueHashMap {
public static final void main(String[] ignored) {
Map<Integer,List<String>> mapOfIntStrs = new HashMap<Integer,List<String>>();
//Add elements
addStringToMap(mapOfIntStrs, 1, "one");
addStringToMap(mapOfIntStrs, 1, "two");
addStringToMap(mapOfIntStrs, 1, "three");
addStringToMap(mapOfIntStrs, 2, "four");
addStringToMap(mapOfIntStrs, 2, "five");
//Output
Set<Integer> keyNumSet = mapOfIntStrs.keySet();
Iterator<Integer> keyNumItr = keyNumSet.iterator();
while(keyNumItr.hasNext()) {
Integer keyNum = keyNumItr.next();
List<String> strList = mapOfIntStrs.get(keyNum);
System.out.println(keyNum);
for(String s : strList) {
System.out.println(" " + s);
}
}
}
private static final void addStringToMap(Map<Integer,List<String>> mapTo_addTo, int keyNum, String value) {
if(mapTo_addTo.containsKey(keyNum)) {
mapTo_addTo.get(keyNum).add(value);
} else {
List<String> strList = new ArrayList<String>();
strList.add(value);
mapTo_addTo.put(keyNum, strList);
}
}
}
Output:
[C:\java_code\]java MultiValueHashMap
1
one
two
three
2
four
five
Regarding multiple keys per value, you could certainly do this, although I'm not sure it's recommended. According to the HashMap API:
The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.
And the Hashtable API:
To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.
So while this would work with ArrayList<Integer> keys, for anything with customized keys, containing non-standard classes, unless you are correctly implementing hashCode() for those objects, the HashMap may not function properly.
It seems like you need a couple of data structures as fields in your class:
stringMap: Map<Integer,String>
{1916062554=abc, 958031277=abc}, etc.
because for get you want to look up items by key
keys: Map<String,List<Integer>>
{ "abc" = {1916062554, 958031277}
because for delete you want to know the keys for a given item, in order.
To add to the Map:
public void put(String item) {
List<Integer> list = getOrCreateList(item,keys);
int key = calculateKey(item,list);
list.add(key);
stringMap.put(key,item);
}
private static List<Integer> getOrCreateList(String item, Map<String,List<Integer>> map) {
List<Integer> list = map.get(item);
if(list == null) {
list = new ArrayList<Integer>();
map.put(item,list);
}
return list;
}
To get from the map is easy:
public String get(int key) {
return stringMap.get(key);
}
To delete from the map -- if I understand your requirements correctly -- you need to find the most recent key in the list corresponding to the key provided...
public void delete(int key) {
String item = stringMap.get(key);
if(item == null) {
// ... deal with
}
List<Integer> keys = keys.get(item);
// lazily using methods which don't exist in the Java API
// but which illustrate the point.
keys.removeLast();
if(keys.isEmpty()) {
stringMap.remove(key);
list.remove(item);
}
}
I have an ArrayList:
ArrayList<Integer> example = new ArrayList<Integer>();
example.add(1);
example.add(1);
example.add(2);
example.add(3);
example.add(3);
So I want to make others three ArrayLists containing in which one the same values (where there is just one value the ArrayList would have just the one).
Is that possible?
Here's an approach to filtering out the elements, implemented: using a generic Map (in a generic class) to encapsulate the values.
The key is the object we want, and the value is determined as follows:
If the key never existed, we have a list with at most one element, which is the same as the key;
If the key has existed prior, we have a list with at least one element, which is the same as the key.
Here's how it's laid out. You instantiate it with the type of object you want to split.
public class UniqueSplitter<T> {
public Map<T, List<T>> filterOutElements(final List<?> theCandidateList) {
final Map<T, List<T>> candidateMap = new HashMap<>();
for(Object element : theCandidateList) {
if(candidateMap.containsKey(element)) {
candidateMap.get(element).add((T) element);
} else {
final List<T> elementList = new ArrayList<>();
elementList.add((T) element);
candidateMap.put((T)element, elementList);
}
}
return candidateMap;
}
}
I'm trying to put multiple values in one arraylist key, but instead I get an error:
Class:
public class BestellenWindow extends javax.swing.JFrame {
private ArrayList<String> bestelling = new ArrayList<String>();
public BestellenWindow() {
initComponents();
}
Action performed:
private void BestellenbuttonActionPerformed(java.awt.event.ActionEvent evt)
{
bestelling.add(Barcodetext.getText(), Aantaltext.getText());
System.out.println(bestelling.get(0));
}
error:
no suitable method found for add(java.lang.String,java.lang.String)
method java.util.ArrayList.add(int,java.lang.String) is not applicable
(actual argument java.lang.String cannot be converted to int by method invocation conversion)
method java.util.ArrayList.add(java.lang.String) is not applicable
An ArrayList is just a list. It doesn't have a "key". If you want to store objects by key, use an implementation of interface Map (for example HashMap) instead of a List.
But, a normal Map can store only one value per key. If you want to store multiple values, you can use a Map<K, List<V>> (where K is the key type and V the value type), or you could use for example Multimap from Google Guava.
But there's also another, perhaps better solution. Create a new class to hold the barcode and aantal, and store instances of that class in your ArrayList. For example:
public class Bestelling {
private String barcode;
private int aantal;
public Bestelling(String barcode, int aantal) {
this.barcode = barcode;
this.aantal = aantal;
}
public String getBarcode() {
return barcode;
}
public int getAantal() {
return aantal;
}
}
// Later:
Bestelling b = new Bestelling(Barcodetext.getText(),
Integer.parseInt(Aantaltext.getText()));
bestelling.add(b);
In Java there are two elements.
1. List Interface : This does not hold any key. It has the collection of the values. You can add one by one by add(value) method.
2. Map : This holds one key for a set of value.
List<String> userName = new ArrayList<String>();
userName.add("Jesper");
userName.add("Mafue");
Map<Long,String> userMap = new HashMap<Long,String>();
userMap.put(1l,"Jesper");
userMap.put(2l,"Mafue");
From map you can retrieve the values by providing key.
You are trying to invoke the method
list.add(int index, String value)
that it is used to insert an element at a specific position.
If this is what you actually want to do then I guess that Barcodetext should contain a string that is a number, so you need to convert it to an int with
Integer.parseInt(Barcodetext.getText())
If instead you want to add multiple values just call the method twice:
bestelling.add(Barcodetext.getText());
bestelling.add(Aantaltext.getText());
Use either:
bestelling.add(...);
bestelling.add(...);
...
or
bestelling.addAll(Arrays.asList(..., ...));