Avoiding repetition (for loop) - java

I'm making a program that use two text files (two tables), and perform basic relational algebra (Union, difference, intersection,and join) on them. I'm using a Hashmaps, to save the values (keys/values) each time, but I wonder how can I use one main "for loop" instead of 4 for each operation.
This is my code:
for (Map.Entry<Integer, String> htEntries : map.entrySet()) {
if(map2.containsKey(htEntries.getKey()) && map2.get(htEntries.getKey()).equals(htEntries.getValue())){
inter.put( htEntries.getKey(), htEntries.getValue());
}
}
for (Map.Entry<Integer, String> joinEntries : map.entrySet()) {
if(map2.containsKey(joinEntries.getKey())){
join.put( joinEntries.getKey(), joinEntries.getValue());
}
}
for (Map.Entry<Integer, String> diffEntries : map.entrySet()) {
if(!map2.containsKey(diffEntries.getKey())){
diff.put( diffEntries.getKey(), diffEntries.getValue());
}
}
for (Map.Entry<Integer, String> diffEntries2 : map2.entrySet()) {
if(!map.containsKey(diffEntries2.getKey())){
diff2.put( diffEntries2.getKey(), diffEntries2.getValue());
}
}

I think you must use a least 2 for loops, you can do this:
for (Map.Entry<Integer, String> htEntries : map.entrySet()) {
if(map2.containsKey(htEntries.getKey()) {
join.put( htEntries.getKey(), htEntries.getValue());
if (map2.get(htEntries.getKey()).equals(htEntries.getValue())) {
inter.put(htEntries.getKey(), htEntries.getValue());
}
} else {
diff.put( htEntries.getKey(), htEntries.getValue());
}
}
for (Map.Entry<Integer, String> diffEntries2 : map2.entrySet()) {
if(!map.containsKey(diffEntries2.getKey())){
diff2.put(diffEntries2.getKey(), diffEntries2.getValue());
}
}

You can still do set/value pairs with sets:
Set<SetEntry> setA = new HashSet<>();
setA.add(new SetEntry("a", 1));
setA.add(new SetEntry("b", 2));
setA.add(new SetEntry("c", 2));
setA.add(new SetEntry("d", 1));
Set<SetEntry> setB = new HashSet<>();
setB.add(new SetEntry("a", 1));
setB.add(new SetEntry("b", 2));
setB.add(new SetEntry("e", 1));
setB.add(new SetEntry("f", 2));
Set<SetEntry> union = new HashSet<>(setA);
union.addAll(setB);
System.out.println("Union: " + union);
Set<SetEntry> intersection = new HashSet<>(setA);
intersection.retainAll(setB);
System.out.println("Intersection: " + intersection);
Set<SetEntry> difference = new HashSet<>(setA);
difference.removeAll(setB);
System.out.println("Difference: " + difference);
Here is the output:
Union: [a->1, b->2, c->2, d->1, e->1, f->2]
Intersection: [a->1, b->2]
Difference: [c->2, d->1]
Here is a base SetEntry implementation:
private class SetEntry {
private final String key;
private final int value;
public SetEntry(String key, int value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public int getValue() {
return value;
}
// Just use the key for equality
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SetEntry setEntry = (SetEntry) o;
return key.equals(setEntry.key);
}
#Override
public int hashCode() {
return key.hashCode();
}
#Override
public String toString() {
return key+"->"+value;
}

Related

Count occurrence HashMap

I'm trying to count the number of diseases a day by using hashmap:
public static main(String[] args){
Disease cholera=new Disease("cholera");
Disease dengue=new Disease("dengue");
List<Diagnosis> diagnoses = Arrays.asList(
new Diagnosis(cholera, 0), // registered cholera on day 0
new Diagnosis(cholera, 0),
new Diagnosis(cholera, 1),
new Diagnosis(cholera, 1),
new Diagnosis(cholera, 2),
new Diagnosis(cholera, 2)
);
printFreq(diagnosis);
}
public static void printFreq(List<Diagnosis> diagnoses) {
Map<Diagnosis, Integer> hm = new HashMap();
for (Diagnosis x : diagnoses) {
if (!hm.containsKey(x)) {
hm.put(x, 1);
} else {
hm.put(x, hm.get(x) + 1);
}
}
But if I call printFreq(diagnoses) I get:{{cholera, 0}=1, {cholera, 1}=1, {cholera, 2}=1, {cholera, 0}=1, {dengue, 0}=1, {cholera, 1}=1, {cholera, 2}=1}. How can I fix this to {{cholera,0}=2,{dengue0}=1,{cholera,1}=2,{cholera,2}=2}}
I'm not allowed to change the Diagnosis or Disease class but this is how they look like:
public class Disease {
private final String name;
public Disease(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Disease disease = (Disease) o;
return name.equals(disease.name);
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public String toString() {
return name;
}
Diagnosis:
public class Diagnosis {
private final Disease disease;
private final int day;
public Diagnosis(Disease disease, int day) {
this.disease = disease;
this.day = day;
}
public Disease getDisease() {
return disease;
}
public int getDay() {
return day;
}
#Override
public String toString() {
return "{" + disease + ", " + day + "}";
}
You can add a wrapper class to make life easier. In this code I have a wrapper class DiagnosisMetric that wraps the Diagnosis class.
Following is the new implementation of the printFreq function.
public static void printFreq(List<Diagnosis> diagnoses) {
Map<DiagnosisMetric, Long> collect = diagnoses.stream().
collect(Collectors.groupingBy(DiagnosisMetric::new, counting()));
System.out.println(collect);
}
And, following is the wrapper class. Notice that I have implemented equals and hashCode as per the requirement.
public static class DiagnosisMetric {
private Diagnosis diagnosis;
public DiagnosisMetric(Diagnosis s) {
this.diagnosis = s;
}
public Diagnosis getDiagnosis() {
return diagnosis;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DiagnosisMetric that = (DiagnosisMetric) o;
return diagnosis.getDisease().getName().
equals(that.getDiagnosis().getDisease().getName())
&& diagnosis.getDay() == that.getDiagnosis().getDay();
}
#Override
public int hashCode() {
return Objects.hash(diagnosis.getDay(),
diagnosis.getDisease().getName());
}
#Override
public String toString() {
String disease = diagnosis.getDisease().getName();
int day = diagnosis.getDay();
return "{" + disease + ", " + day + "}";
}
}
An ad-hoc solution which does not require storing the intermediate map with Diagnosis key (which cannot be used as a map key without properly implemented hashCode and equals as mentioned earlier) is like this:
use a raw list containing diagnosis.day and disease.name as a key wrapper in Collectors.groupingBy
calculate the frequencies (e.g. using Collectors.summingInt)
use Supplier<Map> to provide a tree map sorted by the list contents
print the stats
diagnoses.stream()
.collect(Collectors.groupingBy(
d -> Arrays.asList(d.getDay(), d.getDisease().getName()),
() -> new TreeMap<List<?>, Integer> (Comparator
.comparingInt((List k) -> (Integer) k.get(0))
.thenComparing((List k) -> (String) k.get(1))
),
Collectors.summingInt(d -> 1)
)) // Map<List, Integer> created here
.forEach((k, v) -> System.out.println(k + " = " + v));
Output:
[0, cholera] = 2
[0, dengue] = 1
[1, cholera] = 2
[2, cholera] = 2
To resolve this what we need to do is to override the hashCode and equals method for the Diagnosis class, like this:
#Override
public boolean equals(Object obj) {
Diagnosis diagnosis = (Diagnosis) obj;
return this.day == diagnosis.day && this.disease == diagnosis.disease;
}
#Override
public int hashCode() {
char[] charArr = this.disease.toString().toCharArray();
int sum = 0;
for (int i = 0; i < charArr.length; i++) {
sum += charArr[i];
}
return sum;
}
Also you need to override equals method of the Disease class, here is code for that:
#Override
public boolean equals(Object obj) {
Disease disease = (Disease) obj;
return this.disease.equalsIgnoreCase(disease.toString());
}

How to return List<E> from Collection<V> where E is contained inside V?

I have a map which returns below data when Map.values() is called which returns Collection<V>
[
Cache.CachedObject(inserted=1617483447407, value=Record(id=10, type=5, timestamp=2021-04-03T08:37:51.312Z)),
Cache.CachedObject(inserted=1617483446133, value=Record(id=11, type=6, timestamp=2021-04-03T08:37:51.312Z)),
Cache.CachedObject(inserted=1617483445030, value=Record(id=8, type=4, timestamp=2021-04-03T08:37:51.312Z))
]
How do I return List<Record> from Collection<V> ?
Code of Cache class is given below
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.api.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
public class Cache<K, V> {
private long timeToLive = 20000L;
private HashMap<K, V> cacheMap = new HashMap();
public Cache() {
if (this.timeToLive > 0L) {
Thread t = new Thread(() -> {
while(true) {
this.cleanup();
}
});
t.setDaemon(true);
t.start();
}
}
public void put(K key, V value) {
synchronized(this.cacheMap) {
this.cacheMap.put(key, new Cache.CachedObject(value));
}
}
public V get(K key) {
synchronized(this.cacheMap) {
Cache<K, V>.CachedObject c = (Cache.CachedObject)this.cacheMap.get(key);
return c == null ? null : c.value;
}
}
public void remove(K key) {
synchronized(this.cacheMap) {
this.cacheMap.remove(key);
}
}
public int size() {
synchronized(this.cacheMap) {
return this.cacheMap.size();
}
}
public void clear() {
synchronized(this.cacheMap) {
this.cacheMap.clear();
}
}
public Collection<V> values() {
synchronized(this.cacheMap) {
return this.cacheMap.values();
}
}
public void cleanup() {
long now = System.currentTimeMillis();
ArrayList deleteKey;
synchronized(this.cacheMap) {
Iterator<Entry<K, V>> itr = this.cacheMap.entrySet().iterator();
deleteKey = new ArrayList(this.cacheMap.size() / 2 + 1);
while(itr.hasNext()) {
Entry<K, V> entry = (Entry)itr.next();
K key = entry.getKey();
V cached = entry.getValue();
if (cached != null && now > ((Cache.CachedObject)cached).inserted + this.timeToLive) {
deleteKey.add(key);
}
}
}
for(Iterator var4 = deleteKey.iterator(); var4.hasNext(); Thread.yield()) {
K key = var4.next();
synchronized(this.cacheMap) {
this.cacheMap.remove(key);
}
}
}
public String toString() {
return "Cache(timeToLive=" + this.timeToLive + ", cacheMap=" + this.cacheMap + ")";
}
private class CachedObject {
public long inserted = System.currentTimeMillis();
public V value;
protected CachedObject(V value) {
this.value = value;
}
public String toString() {
return "Cache.CachedObject(inserted=" + this.getInserted() + ", value=" + this.getValue() + ")";
}
public long getInserted() {
return this.inserted;
}
public V getValue() {
return this.value;
}
}
}
Update
To resolve multiple compilation and design issues in the posted code of Cache /CachedObject, the following fixes need to be applied (but they are not final and further improvements are possible):
Make inner class CachedObject generic
Replace HashMap with ConcurrentHashMap (so that synchronized blocks could be removed) and fix the type of value in this map to be CachedObject<V>
Refactor cleanUp method
An example implementation
public class Cache<K, V> {
private long timeToLive = 20000L;
private Map<K, CachedObject<V>> cacheMap = new ConcurrentHashMap<>();
public Cache() {
if (this.timeToLive > 0L) {
Thread t = new Thread(() -> {
while(true) {
this.cleanup();
}
});
t.setDaemon(true);
t.start();
}
}
public void put(K key, V value) {
this.cacheMap.put(key, new CachedObject(value));
}
public V get(K key) {
CachedObject<V> c = this.cacheMap.get(key);
return c == null ? null : c.value;
}
public void remove(K key) {
this.cacheMap.remove(key);
}
public int size() {
return this.cacheMap.size();
}
public void clear() {
this.cacheMap.clear();
}
public Collection<V> values() {
return this.cacheMap.values().stream()
.map(CachedObject::getValue).collect(Collectors.toList());
}
public void cleanup() {
if (!this.cacheMap.isEmpty()) {
long now = System.currentTimeMillis();
this.cacheMap.entrySet().removeIf(e -> null == e.getValue() || now > e.getValue().inserted + this.timeToLive);
}
Thread.yield();
}
public String toString() {
return "Cache(timeToLive=" + this.timeToLive + ", cacheMap=" + this.cacheMap + ")";
}
private class CachedObject<V> {
public long inserted = System.currentTimeMillis();
public V value;
protected CachedObject(V value) {
this.value = value;
}
public String toString() {
return "Cache.CachedObject(inserted=" + this.getInserted() + ", value=" + this.getValue() + ")";
}
public long getInserted() {
return this.inserted;
}
public V getValue() {
return this.value;
}
}
}
With this implementation, the method Cache::values() provides an appropriate collection of V-type elements which are copied into a list, so it will be sufficient just to cast to List:
Cache<String, Record> cache = new Cache<>();
cache.put("#1", new Record(1));
cache.put("#2", new Record(2));
cache.put("#3", new Record(3));
System.out.println(cache);
List<Record> records = (List<Record>) cache.values();
System.out.println(records);
System.out.println(records);
Thread.sleep(2_100L);
List<Record> noRecords = (List) cache.values();
System.out.println(noRecords);
Output
Cache(timeToLive=2000, cacheMap={#3=Cache.CachedObject(inserted=1617530470001, value=Record{id=3}), #1=Cache.CachedObject(inserted=1617530470001, value=Record{id=1}), #2=Cache.CachedObject(inserted=1617530470001, value=Record{id=2})})
[Record{id=3}, Record{id=1}, Record{id=2}]
[]
If you don't mind using Eclipse Collections (which is a great library by the way, an extra dependency however...) , you might want to use the following
List<String> valuesList = Lists.mutable
.ofAll(values)
.collect(CachedObject::getValue);
I solved it. Decided to implement library Cache<K, V> class with minor change to values method.
public Collection<V> values() {
synchronized (cacheMap) {
return cacheMap
.values()
.stream()
.map(cache -> ((CachedObject) cache).getValue())
.collect(Collectors.toList());
}
}
Can someone please conform if its the right way

Sort Apache Commons MultiValuedMap by Key

I would like to know how to sort Apache Commons MultiValuedMap by Key. The below is the key class used.
public class VssKey implements Comparable<VssKey> {
private String funCode;
private String varntCode;
private String itemNb;
public VssKey(SummaryDataOracle summaryDataOracle) {
this.funCode = summaryDataOracle.getFuncCode();
this.varntCode = summaryDataOracle.getVariantCd();
this.itemNb = summaryDataOracle.getItemNB();
}
public String getFunCode() {
return funCode;
}
public void setFunCode(String funCode) {
this.funCode = funCode;
}
public String getVarntCode() {
return varntCode;
}
public void setVarntCode(String varntCode) {
this.varntCode = varntCode;
}
public String getItemNb() {
return itemNb;
}
public void setItemNb(String itemNb) {
this.itemNb = itemNb;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((funCode == null) ? 0 : funCode.hashCode());
result = prime * result + ((itemNb == null) ? 0 : itemNb.hashCode());
result = prime * result + ((varntCode == null) ? 0 : varntCode.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VssKey other = (VssKey)obj;
if (funCode == null) {
if (other.funCode != null)
return false;
} else if (!funCode.equals(other.funCode))
return false;
if (itemNb == null) {
if (other.itemNb != null)
return false;
} else if (!itemNb.equals(other.itemNb))
return false;
if (varntCode == null) {
if (other.varntCode != null)
return false;
} else if (!varntCode.equals(other.varntCode))
return false;
return true;
}
#Override
public String toString() {
return String.format("VssKey [funCode=%s, varntCode=%s, itemNb=%s]", funCode, varntCode, itemNb);
}
#Override
public int compareTo(VssKey o) {
int k1 = Integer.parseInt(this.varntCode);
int k2 = Integer.parseInt(o.getVarntCode());
return k2 - k1;
}
}
The below map is constructed by iterating SummerDataOracle values. The values are pushed into the map by VssKey object as shown below.
MultiValuedMap<VssKey, String> partNumberVarientMap = new ArrayListValuedHashMap<>();
for (SummaryDataOracle summaryDataOracle : summeryDataOracleList) {
VssKey key = new VssKey(summaryDataOracle);
String varntText = null;
if (!StringUtils.isEmpty(summaryDataOracle.getVariantSmText())) {
varntText = summaryDataOracle.getVariantSmText().trim();
}
partNumberVarientMap.put(key, varntText);
}
I need to achieve the order in the Map.
Thanks
HashMap/MultivaluedHashMap cannot be sorted directly.
Better, to get its key and sort them and parse map in sorted order.
Map<String, List<String>> map = new MultivaluedHashMap<>();
map.put("b", new ArrayList<>());
map.put("a", new ArrayList<>());
List<String> keylist = new ArrayList<>(map.keySet());
Collections.sort(keylist);
for(String key : keylist) {
System.out.println(key + " : " + map.get(key));
}
By design, you can't sort a HashMap. If you need to keep a specific order in your map, it is recommended to use a Map implementation like TreeMap.
What you can do if you want to iterate over a HashMap in a specific order, is getting the keys and sort them. Then you can iterate over the keys and lookup your values accordingly:
HashMap<String, List<String>> map = new HashMap<>();
for(String key : new TreeSet<String>(map.keySet())){
map.get(key);
}

How to do iterative sorting in string arraylist?

I have an arraylist with below string elements -->
['STM-1000-H', 'STM-2000-E', 'STM-4000-H', 'STM-200-H', 'SFP-1000-A',
'SFP-300-H', 'SAFPX-1000-H', 'SAFPX-2000-A', 'STM-1000-H-L1', 'STM-1000-H-L1+VA+GH', 'STM-1000-H-L2']
I want an arraylist with grouping like ...
display STM- 400, 500, 600, 100, 2000, 4000 (in that order), followed by SPX- 1000, 2000, 4000 (in that order, then followed by the SAFPX- 1000. Also, I have to list each by the L, L1, L2, M, M1, M2, M3, H, H! and VH (in that Order).
Can you please assist with this, Array list is string elements only.
You should create a custom comparator and then use that to sort the arraylist.
For example (this does some of the sorting you asked for but is not checked for edge-cases, that is up to you to fix). Note: Java 8 is used
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("STM-1000-H");
list.add("STM-2000-E");
list.add("SAFPX-1000-H-L2");
list.add("SAFPX-1000-H-L1");
list.add("STM-1000-H-L1");
list.add("SFP-1000-B");
list.add("SFP-1000-A");
HashMap hm = new HashMap();
hm.put("STM", 1);
hm.put("SFP", 2);
hm.put("SAFPX", 3);
list = sort(list, hm);
for (String s : list) {
System.out.println(s);
}
}
public static ArrayList<String> sort(ArrayList<String> list, HashMap<String, Integer> hashmap) {
Comparator<String> comparator = (string1, string2) ->
{
String[] tokens1 = string1.split("-");
String[] tokens2 = string2.split("-");
if (hashmap.get(tokens1[0]) > hashmap.get(tokens2[0]))
return 1;
else if (hashmap.get(tokens1[0]) < hashmap.get(tokens2[0]))
return -1;
if (Integer.parseInt(tokens1[1]) > Integer.parseInt(tokens2[1]))
return 1;
else if (Integer.parseInt(tokens1[1]) < Integer.parseInt(tokens2[1]))
return -1;
if (tokens1.length > 2 && tokens2.length > 2) {
int res = tokens1[2].compareTo(tokens2[2]);
if(res != 0)
return res;
}
if (tokens1.length > 3 && tokens2.length > 3) {
int res = tokens1[3].compareTo(tokens2[3]);
if(res != 0)
return res;
}
return 0;
};
Collections.sort(list, comparator);
return list;
}
Outputs:
STM-1000-H
STM-1000-H-L1
STM-2000-E
SFP-1000-A
SFP-1000-B
SAFPX-1000-H-L1
SAFPX-1000-H-L2
Stream.of("STM-1000-H",
"STM-2000-E",
"STM-4000-H",
"STM-200-H",
"SFP-1000-A",
"SFP-300-H",
"SAFPX-1000-H",
"SAFPX-2000-A",
"STM-1000-H-L1",
"STM-1000-H-L1+VA+GH",
"STM-1000-H-L2")
.collect(Collectors.groupingBy(s -> s.substring(0, s.indexOf('-'))))
.entrySet()
.stream()
.sorted((e1, e2) -> -e1.getKey().compareTo(e2.getKey()))
.forEach(e -> {
System.out.println(e.getKey() + ":");
e.getValue().stream().sorted().forEach(System.out::println);
});
--
STM:
STM-1000-H
STM-1000-H-L1
STM-1000-H-L1+VA+GH
STM-1000-H-L2
STM-200-H
STM-2000-E
STM-4000-H
SFP:
SFP-1000-A
SFP-300-H
SAFPX:
SAFPX-1000-H
SAFPX-2000-A
package com.skadakov.examples.devrev;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
/**
*
* #author s.kadakov
*/
public class DevRev {
private final String series;
private final int model;
private final String version;
private final String revision;
public DevRev(String firmware) {
String[] ss = firmware.split("-");
if (ss.length < 3) {
throw new IllegalArgumentException(firmware);
}
this.series = ss[0];
this.model = Integer.parseInt(ss[1]);
this.version = ss[2];
this.revision = ss.length == 3 ? null : ss[3];
}
public String getSeries() {
return series;
}
public int getModel() {
return model;
}
public String getVersion() {
return version;
}
public String getRevision() {
return revision;
}
#Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.series);
hash = 97 * hash + this.model;
hash = 97 * hash + Objects.hashCode(this.version);
hash = 97 * hash + Objects.hashCode(this.revision);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DevRev other = (DevRev) obj;
if (this.model != other.model) {
return false;
}
if (!Objects.equals(this.series, other.series)) {
return false;
}
if (!Objects.equals(this.version, other.version)) {
return false;
}
return Objects.equals(this.revision, other.revision);
}
#Override
public String toString() {
return series + "-" + model + "-" + version + (revision != null ? "-" + revision : "");
}
public static void main(String[] args) {
String[] firmare = {
"STM-1000-H",
"STM-2000-E",
"STM-4000-H",
"STM-200-H",
"SFP-1000-A",
"SFP-300-H",
"SAFPX-1000-H",
"SAFPX-2000-A",
"STM-1000-H-L1",
"STM-1000-H-L1+VA+GH",
"STM-1000-H-L2"};
Map<String, List<DevRev>> revs = new TreeMap<>();
for (String f : firmare) {
DevRev dr = new DevRev(f);
List<DevRev> sdevs = revs.get(dr.getSeries());
if (sdevs == null) {
sdevs = new ArrayList<>();
revs.put(dr.getSeries(), sdevs);
}
sdevs.add(dr);
}
for (Map.Entry<String, List<DevRev>> entry : revs.entrySet()) {
String series = entry.getKey();
List<DevRev> devices = entry.getValue();
System.out.println(series);
devices.sort(new Comparator<DevRev>() {
#Override
public int compare(DevRev o1, DevRev o2) {
return new Integer(o1.getModel()).compareTo(o2.getModel());
}
});
for (DevRev dr : devices) {
System.out.println("-> " + dr);
}
}
}
}

Iterate and compare values in same map to find largest value and remove other from same map

I have
Map<Key,Integer> myMap= new HashMap<Key,Integer>();
suppose I have elements in map like,
myMap.put(k1,5);
myMap.put(k2,20);
myMap.put(k3,10);
myMap.put(k4,15);
What code I should try to iterate and compare values in this map, and I want map element with largest value, also map should contain only that element.
The simple solution would be to use concurrent hashmap. Try this..
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Largest {
public static void main(String[] args) {
Map<Integer, Integer> myMap = new HashMap<Integer, Integer>();
myMap.put(1, 5);
myMap.put(2, 20);
myMap.put(3, 10);
myMap.put(4, 15);
myMap.put(5, 20);
System.out.println(myMap);
Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<Integer, Integer>(myMap);
Integer maxKey = concurrentMap.keySet().iterator().next();
Integer max = myMap.get(maxKey);
for(Integer key : concurrentMap.keySet()){
Integer currValue = concurrentMap.get(key);
if(max > currValue){
concurrentMap.remove(key);
} else if(max < currValue) {
concurrentMap.remove(maxKey);
max = concurrentMap.get(key);
maxKey = key;
}
}
System.out.println(concurrentMap);
}
}
I would use a wrapper class:
public class MyMapWrapper<K, V extends Comparable> {
private V maximum;
private K maxKey;
private final Map<K, V> map = new HashMap<>();
private final Comparator<V> comp;
public MyMapWrapper() {
this(new Comparator<V>() {
public int compareTo(V v1, V v2) {
return (v1 == null) ? ((v2 == null) ? 0 : -1) : v1.compareTo(v2);
}
});
}
public MyMapWrapper(Comparator<V> comp) {
if (comp == null) {
//Comparator should not be null
throw new IllegalArgumentException("Comparator cannot be null!");
}
this.comp = comp;
}
public V getMaximum() {
return this.maximum;
}
public K getMaximumKey() {
return this.maxKey;
}
public int put(K key, V value) {
if (value == null) {
//You don't want null comparables, can either throw an exception or ignore
throw new IllegalArgumentException("No null values allowed!");
}
if (this.maximum == null || this.comp.compareTo(this.maximum, value) > 0) {
this.maximum = value;
this.maxKey = key;
}
return this.map.put(key, value);
}
public int remove(K key) {
V back = this.map.remove(key);
if (this.maximum.equals(back)) {
K newKey = null;
V newMax = null;
//search map for new maximum
for (Map.Entry<K, V> ent : this.map.entrySet()) {
if (this.comp.compareTo(ent.getValue(), newMax) > 0) {
newMax = ent.getValue();
newKey = ent.getKey();
}
}
this.maximum = newMax;
this.maxKey = newKey;
}
return back;
}
public V get(K key) {
return this.map.get(key);
}
}
If other specific methods are needed you can do so, but this will only require a single "map" to be used:
MyMapWrapper<String, Integer> example = new MyMapWrapper<>();
example.put("test1", 14);
example.put("test2", 3450);
example.put("test4", -142);
System.out.println(String.format("Max key: %s, Max value: %d", example.getMaximumKey(), example.getMaximum());
//Max key: "test2", Max value: 3450
This also allows for custom comparators for values:
MyMapWrapper<String, Integer> example = new MyMapWrapper<>(new Comparator<Integer>() {
public int compareTo(Integer v1, Integer v2) {
return (v2 == null) ? ((v1 == null) ? 0 : -1) : v2.compareTo(v1);
}
});
example.put("test1", 14);
example.put("test2", 3450);
example.put("test4", -142);
System.out.println(String.format("Max key: %s, Max value: %d", example.getMaximumKey(), example.getMaximum());
//Max key: "test4", Max value: -142
if you have java 8 you can do it like this:
CODE
public class MapFiltering {
public static void main(String[] args) {
Random rnd = new Random();
Map<String, Integer> myMap = new HashMap<>();
int i = 1;
for (int j = 0; j < 10; j++) {
myMap.put("k" + j, rnd.nextInt(100));
}
System.out.println("BEFORE");
printMap(myMap);
filterMap(myMap);
System.out.println("AFTER");
printMap(myMap);
}
private static void printMap(Map<String, Integer> map) {
map.entrySet().stream().forEach((e) -> {
System.out.println("K=" + e.getKey() + " V=" + e.getValue());
});
}
private static void filterMap(Map<String, Integer> map) {
Map.Entry<String, Integer> get
= map.entrySet()
.stream()
.max((Comparator<? super Map.Entry<String, Integer>>) new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}).get();
map.clear();
map.put(get.getKey(), get.getValue());
}
}
EXAMPLE OUTPUT
BEFORE
K=k0 V=66
K=k1 V=5
K=k2 V=34
K=k3 V=54
K=k4 V=52
K=k5 V=69
K=k6 V=34
K=k7 V=62
K=k8 V=73
K=k9 V=28
AFTER
K=k8 V=73
If you just want to grab the max Entry in the Map use Collections.max with your defined Comparator:
Map<Key, Integer> myMap= new HashMap<Key,Integer>();
myMap.put(k1,5);
myMap.put(k2,20);
myMap.put(k3,10);
myMap.put(k4,15);
Entry<Key, Integer> maxEntry = Collections.max(myMap.entrySet(),
new Comparator<Entry<Key, Integer>>(){
#Override public int compare(Entry<Key, Integer> e1, Entry<Key, Integer> e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
// outputs k2=20
System.out.print(maxEntry);
If you really want to have a 1 entry map in the end, you can clear the old one away and add maxEntry:
myMap.clear();
myMap.put(maxEntry.key(), maxEntry.value());

Categories

Resources