Related
I need perform an hashmap iteration using Java 8 streams. I need to iterate over an hashmap. Check whether a particular key ("new") does not have null or empty values, copy that value to a variable (String val1) of type string. Then again check for another key for ex:"old" and then copy that value to a variable (String val2) of type string and call the main method where i need to send these 2 values (val1, val2). This has to be done with in hashmap iteration. Can you please help me on this.
The code:
map1.entrySet()
.stream()
.filter(s -> {
if (s.getKey().contains("abc") && !s.getValue().equals("") && s.getValue()!=null) {
String val1 = s.getValue;
if (s.getKey().contains("bb")) {
String val2 = s.getValue(); //call the function
callFunction(val1,val2);
}
}
else {
}
});
Need to be done using Java 8
for(Map.Entry e : map1.entrySet()) {
if(e.containsKey("new")&& !e.getValue().equals("")){
String val1 = (String) e.getValue();
if(e.containsKey("old")&& !e.getValue().equals("")){
String val2 = (String) e.getValue();
//call the function-- This is boolean
if(validateMethod(val1, val2)){ // if true
Map<String, String> map2 = new HashMap<>();
map2.putAll(e);
}
}
}
}
You need to look for particular keys : new and old so you don't need to iterate over the entries of the map, because if the keys exist they will be unique.
get the value of the specific keys, if they don't exist, keep en empty String
do your stuff with these values
Map<String, String> map1 = ...;
String v1 = map1.getOrDefault("new", "");
String v2 = map1.getOrDefault("old", "");
Map<String, String> map2 = new HashMap<>();
if(!v1.isEmpty() && !v2.isEmpty() && validateMethod(v1, v2)){
// do your stuff
}
You might put the check for isEmpty in your validateMethod rather than in an if
Try it with this:
yourMap.entrySet().stream()
From this point, you can manage. Stream consists of Entry<Key,Value> so you can check whatever you want to.
I have a structure like below ,
Key: active-csr-HA-profile & Value: {sapd-outside=[outside], sapd-extra4=[extra4], sapd-extra3=[extra3], sapd-inside=[inside]}
Key = sapd-outside, Value = [outside]
Key = sapd-extra4, Value = [extra4]
Key = sapd-extra3, Value = [extra3]
Key = sapd-inside, Value = [inside]
Key: standby-csr-HA-profile & Value: {sapd-outside=[outside], sapd-extra4=[extra4], sapd-extra3=[extra3], sapd-inside=[inside]}
Key = sapd-outside, Value = [outside]
Key = sapd-extra4, Value = [extra4]
Key = sapd-extra3, Value = [extra3]
Key = sapd-inside, Value = [inside]
the above if of format Hashtable<String, Map<String, Set<String>>>
I want to compare if sapd-outside of active-csr-HA-profile is same as one of the keys of standby-csr-HA-profile. So compare each key of active-csr-HA-profile to each key of standby-csr-HA-profile.
I looked some similar questions but what i am working out is not solving the purpose.
As already mentioned in the comments, the Hashtable is considered obsolete. Its replacement is HashMap. If you wish make HashMap synchronized the same way the Hashtable does, use the Collections::synchronizedMap decorator on it.
The structure of your Hashtable looks a bit unclear. I guess the following structure matches your one the best and I base my solution on it.
Hashtable<String, Map<String, Set<String>>> map = new Hashtable<>();
Map<String, Set<String>> activeCsrHAProfile = new HashMap<>();
activeCsrHAProfile.put("sapd-outside", new HashSet<>(Arrays.asList("outside")));
activeCsrHAProfile.put("sapd-extra4", new HashSet<>(Arrays.asList("extra4")));
activeCsrHAProfile.put("sapd-extra3", new HashSet<>(Arrays.asList("extra3")));
activeCsrHAProfile.put("sapd-inside", new HashSet<>(Arrays.asList("inside")));
Map<String, Set<String>> standbyCsrHAProfile = new HashMap<>();
standbyCsrHAProfile.put("sapd-outside", new HashSet<>(Arrays.asList("outside")));
standbyCsrHAProfile.put("sapd-extra4", new HashSet<>(Arrays.asList("extra4")));
standbyCsrHAProfile.put("sapd-extra3", new HashSet<>(Arrays.asList("extra3")));
standbyCsrHAProfile.put("sapd-inside", new HashSet<>(Arrays.asList("inside")));
map.put("active-csr-HA-profile", activeCsrHAProfile);
map.put("standby-csr-HA-profile", standbyCsrHAProfile);
In case my structure differs a bit from yours, there would be no problem to amend the solution in order to match your structure - the principle is the same.
Set<String> sapdOutsideOfActiveCsrHAProfile = map.get("active-csr-HA-profile")
.get("sapd-outside");
map.get("standby-csr-HA-profile").entrySet()
.stream()
.filter(i -> i.getValue().containsAll(sapdOutsideOfActiveCsrHAProfile))
.forEach(e -> System.out.println("Found at: " +
"key=" + e.getKey() + ", value=" + e.getValue()));
.filter(i -> i.getValue().containsAll(..) filters those entris which values Set<String> contains all of the required Strings.
.forEach(..) gives a consumer performing an action over all the matching results.
In case you need the boolean representing whether the match has occurred or not, do:
boolean matches = map.get(..).entrySet().stream().filter(..).findFirst().isPresent();
As mentioned in the comments, HashTable is a debatable choice. Regardless of the implementation you choose, you could create your own class to manage the messy stuff:
public class CustomMap extends Hashtable<String, Map<String, Set<String>>> {
public CustomMap() {
super();
}
public boolean compareEntries(String key1, String key2) {
if (!this.containsKey(key1) || !this.containsKey(key2) || this.get(key1).size() != this.get(key2).size())
return false;
for (String innerKey : this.get(key1).keySet()) {
if (!this.get(key2).containsKey(innerKey)) {
return false;
}
final Set<String> setA = this.get(key1).get(innerKey);
final Set<String> setB = this.get(key2).get(innerKey);
if (!setA.containsAll(setB) || !setB.containsAll(setA)) {
return false;
}
}
return true;
}
}
I took the assumption there could be more entries in your table and you'd want to compare specific entries.
You can iterate through a map with its entry set:
Hashtable<String, Map<String, Set<String>>> table = new Hashtable();
for (Map.Entry<String, Map<String, Set<String>>> entry : table.entrySet()) {
String key = entry.getKey();
Map<String, Set<String>> map = entry.getValue();
for (Map.Entry<String, Set<String>> mapEntry : map.entrySet()) {
String mapKey = mapEntry.getKey();
Set<String> set = mapEntry.getValue();
for (String text : set) {
// ...
}
}
}
Nesting sets inside maps inside maps makes the code hard to read though, you might want to use specialized objects instead.
As others have said, in most cases HashMap is preferrable compared to an Hashtable.
I am trying to implement a logic where I have a POJO class which has 7 attributes.
I have added these POJO classes into the map depends upon the value of the attributes.
Below is the implementation
Map<String,List<PriceClass>> map = new HashMap();
for (PriceClass price : prices) {
if (price.getAttribute1() !=null) {
if (map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
} else if(price.getAttribute2()!=null) {
if (map.get("attribute12") !=null) {
map.get("attribute2").add(price);
} else {
map.set("attibute2",Collections.singletonList(price))
}
} else if (price.getAttribute3() !=null) {
.
.
.
} else if (price.getAttribute7() !=null) {
//update the map
}
}
My question is rather than writing these many if loops are there any generalize implementations I can try here.
You may use
Map<String,List<PriceClass>> map = new HashMap<>();
for(PriceClass price: prices) {
HashMap<String,Object> options = new HashMap<>();
options.put("attibute1", price.getAttribute1());
options.put("attibute2", price.getAttribute2());
options.put("attibute3", price.getAttribute3());
options.put("attibute4", price.getAttribute4());
options.put("attibute5", price.getAttribute5());
options.put("attibute6", price.getAttribute6());
options.put("attibute7", price.getAttribute7());
options.values().removeIf(Objects::isNull);
options.keySet().forEach(attr -> map.computeIfAbsent(attr, x -> new ArrayList<>())
.add(price));
}
or generalizing the process:
Prepare a unmodifiable map once
static final Map<String, Function<PriceClass,Object>> ATTR;
static {
Map<String, Function<PriceClass,Object>> a = new HashMap<>();
a.put("attibute1", PriceClass::getAttribute1);
a.put("attibute2", PriceClass::getAttribute2);
a.put("attibute3", PriceClass::getAttribute3);
a.put("attibute4", PriceClass::getAttribute4);
a.put("attibute5", PriceClass::getAttribute5);
a.put("attibute6", PriceClass::getAttribute6);
a.put("attibute7", PriceClass::getAttribute7);
ATTR = Collections.unmodifiableMap(a);
}
and use either
Map<String,List<PriceClass>> map = new HashMap<>();
for(PriceClass price: prices) {
HashMap<String,Object> options = new HashMap<>();
ATTR.forEach((attr,func) -> options.put(attr, func.apply(price)));
options.values().removeIf(Objects::isNull);
options.keySet().forEach(attr -> map.computeIfAbsent(attr, x -> new ArrayList<>())
.add(price));
}
or
Map<String,List<PriceClass>> map = prices.stream()
.flatMap(price -> ATTR.entrySet().stream()
.filter(e -> e.getValue().apply(price) != null)
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), price)))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
A likely optimal solution would be similar to one I have suggested earlier today.
Use the Map<String, Optional<?>> to store the Optional values of the checked attributes with a key of the future output map key.
Map<String, Optional<?>> options = new HashMap<>();
options.put("attribute1", Optional.ofNullable(price.getAttribute1()));
// ...
options.put("attribute3", Optional.ofNullable(price.getAttribute2()));
// ...
Using the iteration of the indices would let you perform the update of a map.
Map<String,List<Price>> map = new HashMap();
for (int i=1; i<7; i++) { // attributes 1..7
String attribute = "attribute" + i; // attribute1...attribute7
options.get(attribute).ifPresent(any -> // for non-nulls
map.put( // put to the map
attribute, // attribute as key remains
Optional.ofNullable(map.get(attribute)) // gets the existing list
.orElse(new ArrayList<>()) // or creates empty
.add(price))); // adds the current Price
}
Moreover, I bet your intention was a bit different. There is no method Map::set
map.set("attibute1",Collections.singletonList(price))
Didn't you mean to put a List<Price> with one item to the very same key instead?
map.put("attibute1", Collections.singletonList(price))
For this reason you can use the way I posted above.
What about using e.g. Enum to define 7 different objects each of them is responsible for concrete attribute:
// this is client code, looks pretty easy
Map<String, List<PriceClass>> map = new HashMap<>();
for (PriceClass price : prices)
PriceAttribute.add(map, price);
// all logic is hidden within special Enum
enum PriceAttribute {
ATTRIBUTE1("attribute1", PriceClass::getAttribute1),
ATTRIBUTE2("attribute2", PriceClass::getAttribute2),
ATTRIBUTE3("attribute3", PriceClass::getAttribute3),
ATTRIBUTE4("attribute4", PriceClass::getAttribute4),
ATTRIBUTE5("attribute5", PriceClass::getAttribute5),
ATTRIBUTE6("attribute6", PriceClass::getAttribute6),
ATTRIBUTE7("attribute7", PriceClass::getAttribute7);
private final String key;
private final Function<PriceClass, ?> get;
PriceAttribute(String key, Function<PriceClass, ?> get) {
this.key = key;
this.get = get;
}
public static void add(Map<String, List<PriceClass>> map, PriceClass price) {
for (PriceAttribute attribute : values()) {
if (attribute.get.apply(price) != null) {
map.computeIfAbsent(attribute.key, key -> new ArrayList<>()).add(price);
break;
}
}
}
}
Following is repetitive code:
if(map.get("attribute1") !=null)
{
map.get("attribute1").add(price);
}
else
{
map.set("attibute1",Collections.singletonList(price))
}
It could be refactored into a method, and called from the parent method which could make this look a bit cleaner.
Additionally you can also try
prices.removeAll(Collections.singleton(null)) and then run loop through it, to avoid one "If" condition.
Following code snippet can be added as a method.
if(map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
to
private static void addPrice(String attributeName, Price price){
if(map.get(attributeName) !=null) {
map.get(attributeName).add(price);
} else {
map.set(attributeName,Collections.singletonList(price))
}
}
Also, map should be created static to be used in this case.
That wasn't be strict answer to the question but I want to improve your code.
You call get twice. Instead of this:
if(map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
Use this:
final List<PriceClass> attribute1 = map.get("attribute1");
if (attribute1 != null) {
attribute1.add(price);
} else {
map.set("attibute1", Collections.singletonList(price))
}
Second you use Collections.singletonList which create immutable list so if you try to add something to it (and you do it) you'll get exception. You should use
new ArrayList<PriceClass>(Arrays.asList(price)) or
Stream.of(price).collect(toList())
If this is the real code you have, so create an array of attribute
Attribute[] attributesOf(Price price){
Attribute[] a = new Attribute[7];
a[0] = price.getAttribute1();
a[1] = price.getAttribute2();
...
a[6] = price.getAttribute7();
}
when you have more attributes, just modify this method. Then your code can be refactor to
for(PriceClass price : prices){
Attribute[] attributes = attributesOf(price);
for(int i=0;i<attributes.length;i++){
String key = "attribute" + (i+1);
if(attributes[i] != null){
map.get(key).add(price);
}
else{
map.set(key, Collections.singletonList(price));
}
}
}
But if you code are different, like price.getGrossAmount(), price.getNetAmount(), price.getTax() you have to find the general contract to define the type of array.
Nevertheless, you have to understand that good data structure will make your code simple and perform well. Maybe you don't need to define attribute in the price class. Maybe you don't need the price class, but you can use BigDecimal as the price value instead.
I don't know your context well, I don't know what are you doing. I think you might have a better answer from someone else if you show the real code as well as its context.
Acc. to your question, it seems like if attibute1 is present in a PriceClass object, it will go to the attibute1 key. This means if all objects in prices list has attibute1, the whole list will go the attibute1 key.
With the above assumption, here is a java-8 solution containing streams.
public static void main(String[] args){
Map<String,List<PriceClass>> map = new HashMap<>();
List<PriceClass> prices =new ArrayList<>();
prices.add(new PriceClass(1,2,3,4,5,6,7));
prices.add(new PriceClass(null,12,13,14,15,16,17));
map = prices.stream()
.map(priceClass -> Arrays
.stream(PriceClass.class.getDeclaredFields())
.map(field -> getFieldValue(field, priceClass))
.filter(Objects::nonNull)
.findFirst()
.orElse(null)
)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey,
Collectors.mapping(AbstractMap.SimpleEntry::getValue,
Collectors.toList())));
System.out.println(map);
}
private static AbstractMap.SimpleEntry<String,PriceClass> getFieldValue(Field field, PriceClass priceClass){
Optional<Integer> value = Optional.empty();
try {
value = Optional.ofNullable((Integer)field.get(priceClass));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (value.isPresent()) {
return new AbstractMap.SimpleEntry<>(field.getName(), priceClass);
}
return null;
}
Output:
{attribute1=[PriceClass{attribute1=1, attribute2=2, attribute3=3, attribute4=4, attribute5=5, attribute6=6, attribute7=7}],
attribute2=[PriceClass{attribute1=null, attribute2=12, attribute3=13, attribute4=14, attribute5=15, attribute6=16, attribute7=17}]}
I am having a Map of type HashMap.
I am trying to iterate over the map and for every entry, for which the boolean flag is set to true, I am trying to print the corresponding key value.
I am able to achieve this. However, instead of printing the String "key" values, it prints String objects. I tried casting it, using the .toString() function. Nothing solved it.
Any help would be greatly appreciated.
Thanks,S.
You want to iterate over the Map's entrySet:
Set< Map.Entry<String, Boolean> > es = map.entrySet();
That's a set, so you can iterate over it:
for( Map.Entry<String, Boolean> v : es ) {
if( v.getValue() ) { // auto-unboxing
System.out.println(v.getKey());
}
}
Simplifying:
for( Map.Entry<String, Boolean> v : map.entrySet() ) {
if( v.getValue() ) {
System.out.println(v.getKey());
}
}
Your followup suggests that the values in your Map are not of type String and are not of a type that has overridden toString, which is why, when you call toString, you get a value like "com.f5.lunaui.emproto.reports.Device_Sri#334003".
In Device_Sri, you should override the toString method to return the String you want:
#Override
public String toString() {
return "em_device90-36";
}
Of course, you'll probably want to calculate the value "em_device90-36" from the fields of the Device_Sri class.
You probably want something like this:
for(String key : map.keySet()){
if(map.get(key)){
System.out.println(key);
}
}
This should work:
Map<String, Boolean> myMap = new HashMap<String, Boolean>();
myMap.put("one", true);
myMap.put("second", false);
for (String key : myMap.keySet()) {
if (myMap.get(key)) {
System.out.println(key + " --> " + myMap.get(key));
}
}
To add to the other answers, you'll get a null pointer exception if the value is null.
if (e.getValue()) { ... }
This is because the value is a Boolean and will be unboxed to a boolean. It's equivalent to e.getValue().booleanValue().
If you want to guard against the value being null, then use
if (Boolean.TRUE.equals(e.getValue()) { ... }
private Map<String, Boolean> deviceChecked = new HashMap<String, Boolean>();
deviceChecked = checkMap;
Set entry = deviceChecked.entrySet();
Iterator i = entry.iterator();
while(i.hasNext()){
Map.Entry ent = (Map.Entry)i.next();
if(ent.getValue()){
result = result + (String)ent.getKey() + ", ";
}
}
System.out.println("result is " +result);
return result;
I am trying to print the key only if the corresopnding boolean value is true. Something like the code above. In this, the result value does not contain the string and it turn prints the object.
I should get the value to be printed as "em-device90-36". But, instead I get this printed
com.f5.lunaui.emproto.reports.Device_Sri#334003
I have a HashMap as below (assuming it has 10,0000 elements)
HashMap<String,String> hm = new HashMap<String,String>();
hm.put("John","1");
hm.put("Alex","2");
hm.put("Mike","3");
hm.put("Justin","4");
hm.put("Code","5");
==========================
Expected Output
==========================
Key = John",Value = "1"
Key = Alex",Value = "2"
Key = Mike",Value = "3"
Key = Justin",Value = "4"
Key = Code",Value = "5"
===========================
I need Java code to prevent Addition of Duplicate <Key,Value> Pairs in HashMap such
that below conditions are staisfied.
1> hm.put("John","1"); is not accepted/added again in the Map
2> hm.put("John","2"); is not accepted/added again in the Map
Hope its clear.
Java code provided will be appreciated.(generic solution needed since i can add any duplicate to the existing map)
You can wrap HashMap in a class, which delegates put, get, and other methods you use from HashMap. This method is wasteful but safe, since it doesn't depend on the internal implementation of HashMap, AbstractMap. The code below illustrates put, get delegating:
public class Table {
protected java.util.HashMap<String, Integer> map =
new java.util.HashMap<String, Integer>();
public Integer get(String key) { return map.get(key); }
public Integer put(String key, Integer value) {
if (map.containsKey(key)) {
// implement the logic you need here.
// You might want to return `value` to indicate
// that no changes applied
return value;
} else {
return map.put(key, value);
}
}
// other methods goes here
}
Another option is to make a class which extends HashMap, and depend on its internal implementation. Java 1.6 sources shows that put is called only in putAll in HashMap, so you can simply override put method:
public class Table extends java.util.HashMap<String, Integer> {
public Integer put(String key, Integer value) {
if (containsKey(key)) {
// implement the logic you need here.
// You might want to return `value` to indicate
// that no changes applied
return value;
} else {
return super.put(key, value);
}
}
}
Another option is similar to the first, and can make an utility method in your class which contains the HashMap instance and call that method wherever you need put something to your map:
public final Integer putToMap(String key, String value) {
if(this.map.containsKey(key)) {
return value;
} else {
return this.map.put(key, value);
}
}
This is an "inline" equivalent of checking manually.
I note that you clarify the question by suggesting you might have "100000000 elements". You still won't have duplicates in the HashMap, because, as two other posters have pointed out, you can't get duplicate keys in a Map. I'm still not sure we understand the question, though, as it's not at all clear how you expected to generate the block titled "Output", or what you intend to do with it.
This may be old question but I thought to share my experience with this. As others pointed out you can't have the same element in a HashMap. By default HashMap will not allow this but there are some cases that you could end up with two or more elements are almost alike that you do not accept but HashMap will. For example, the following code defines a HashMap that takes an array of integers as a key then add :
HashMap<int[], Integer> map1 = new HashMap<>();
int[] arr = new int[]{1,2,3};
map1.put(arr, 4);
map1.put(arr, 4);
map1.put(arr, 4);
At this point, the HashMap did not allow dublicating the key and map1.size() will return 1. However, if you added elements without creating the array first things will be different:
HashMap<int[], Integer> map2 = new HashMap<>();
map2.put(new int[]{4,5,6}, 6);
map2.put(new int[]{4,5,6}, 6);
map2.put(new int[]{4,5,6}, 6);
This way, the HashMap will add all the three new elements so the map2.size() will return 3 and not 1 as expected.
The explanation is that with the first map I created the object arr once and tried to add the same object 3 times which HashMap does not allow by default so only the last usage will be considered. With the second map, however, evey time I recreate a new object on the stack. The three objects created are different and separated thought the three of them have the same data but they are different. That's why HashMap allowed them as different keys.
Bottom line, you don't need to prevent HashMap from adding dublicated keys because it won't by design. However, you have to watch out how you define these keys because the fault may be on your side.
List<String> keys = new ArrayList<String>(); (1000000)
List<String> values = new ArrayList<String>(); (1000000)
Map<String, String> map = new HashMap<String, String>();
int i =0;
for(String key : keys){
String returnedValue = map.put(key, values.get(i));
if(returnedValue!=null){
map.put(key, returnedValue);
system.out.println("Duplicate key trying to be entered with new value so reverting the duplicate key ="+key+"new Value"+values.get(i));
}
}
Unfortunately, it is the way that Map works.
The easiest workaround is to remove all pre existed keys and their values by calling hm.remove() first! like this:
for (String name : names) {
hm.remove(name);
hm.put(name,uri.getQueryParameter(name));
}
And if you don't use a for loop just call it like this:
hm.remove("John");
hm.put("John","1");
hm.remove("Alex");
hm.put("Alex","2");
hm.remove("Mike");
hm.put("Mike","3");
And so on ...
see even if u write same key values multiple times you will just have unique set of pairs. Check that by either iterating or by doing hm.size();
if(hm.put("John","1") != null)
{
// "John" was already a key in the map. The sole value for this key is now "1".
}
List<Object> yourElements = new ... // 10000000
for(Object O : yourElements) {
if(myMap.get(O.key)==null) {
myMap.put(O.key,O);
}
}