I have the following list:
List<ArrayList> list;
list.get(i) contains the ArrayList object with the following values {p_name=set1, number=777002}.
I have to create a
Map<key,value>
where the key contains the p_name, and values are the numbers.
How to do it easily and fast as there can be hundreds of entries in the initial list and each number can be present in multiple p_name entries.
Update: Here is my current solution
List<Row> list; //here is my data
Map<String,String> map = new TreeMap<String,String>();
for (Row l : list) {
if (l.hasValues()) {
Map<String, String> values = l.getResult(); // internal method of Row interface that returns a map
String key = values.get( "number");
map.put(key, values.get( "p_name" ));
}
}
The method works, but maybe it could be done better?
PS : There is an obvious error in my design. I wonder if you find it :)
Sine the key can have more then one values, what you are looking for is a MultiMap. Multimap
Or a simple map in the form
Map<Key,ArrayList<Values>>
There is no "fast" way here to me. You still need to iterate through all the elements and check all the values.
And actually hundreds to Java is not much at all
Related
I'm new to java stream API.
I have 2 lists, and if both their internal object ID matches wants to put some attributes to MAP.
Below is the implementation.
List<LookupMstEntity> examTypeDetails; //This list contains values init.
List<MarksMstEntity> marksDetailList; //This list contains values init.
//FYI above entities have lombok setter, getter, equals & hashcode.
Map<Long, Integer> marksDetailMap = new HashMap<>();
//need below implementation to changed using java 8.
for (LookupMstEntity examType : examTypeDetails) {
for (MarksMstEntity marks : marksDetailList) {
if (examType.getLookupId() == marks.getExamTypeId())
marksDetailMap.put(examType.getLookupId(), marks.getMarks());
}
}
Creating a set of lookupIds Set<Long> ids helps you to throw away duplicate values and to get rid of unnecessary checks.
Then you can filter marksDetailList accordingly with examTypeId values:
filter(m -> ids.contains(m.getExamTypeId()))
HashSet contains() method has constant time complexity O(1).
Try this:
Set<Long> ids = examTypeDetails.stream().map(LookupMstEntity::getLookupId)
.collect(Collectors.toCollection(HashSet::new));
Map<Long, Integer> marksDetailMap = marksDetailList.stream().filter(m -> ids.contains(m.getExamTypeId()))
.collect(Collectors.toMap(MarksMstEntity::getExamTypeId, MarksMstEntity::getMarks));
As long as you are looking for these with equal ID, it doesn't matter which ID you use then. I suggest you to start streaming the marksDetailList first since you need its getMarks(). The filtering method searches if there is a match in IDs. If so, collect the required key-values to the map.
Map<Long, Integer> marksDetailMap = marksDetailList.stream() // List<MarksMstEntity>
.filter(mark -> examTypeDetails.stream() // filtered those where ...
.map(LookupMstEntity::getLookupId) // ... the lookupId
.anyMatch(id -> id == mark.getExamTypeId())) // ... is present in the list
.collect(Collectors.toMap( // collected to Map ...
MarksMstEntity::getExamTypeId, // ... with ID as a key
MarksMstEntity::getMarks)); // ... and marks as a value
The .map(..).anyMatch(..) can be shrink into one:
.anyMatch(exam -> exam.getLookupId() == mark.getExamTypeId())
As stated in the comments, I'd rather go for the for-each iteration as you have already used for sake of brevity.
An observation:
First, your resultant map indicates that there can only be one match for ID types (otherwise you would have duplicate keys and the value would need to be a List or some other way of merging duplicate keys, not an Integer. So when you find the first one and insert it in the map, break out of the inner loop.
for (LookupMstEntity examType : examTypeDetails) {
for (MarksMstEntity marks : marksDetailList) {
if (examType.getLookupId() == marks.getExamTypeId()) {
marksDetailMap.put(examType.getLookupId(),
marks.getMarks());
// no need to keep on searching for this ID
break;
}
}
}
Also if your two classes were related by a parent class or a shared interface that had access to to the id, and the two classes were considered equal based on that id, then you could do something similar to this.
for (LookupMstEntity examType : examTypeDetails) {
int index = marksDetailList.indexOf(examType);
if (index > 0) {
marksDetailMap.put(examType.getLookupId(),
marksDetaiList.get(index).getMarks());
}
}
Of course the burden of locating the index is still there but it is now under the hood and you are relieved of that responsibility.
You can do it with O(N) time complexity using HashMap, first convert two lists into Map<Integer, LookupMstEntity> and Map<Integer, MarksMstEntity> with id as key
Map<Integer, LookupMstEntity> examTypes = examTypeDetails.stream()
.collect(Collectors.toMap(LookupMstEntity::getLookupId,
Function.identity()) //make sure you don't have any duplicate LookupMstEntity objects with same id
Map<Integer, MarksMstEntity> marks = marksDetailList.stream()
.collect(Collectors.toMap(MarksMstEntity::getExamTypeId,
Function.identity()) // make sure there are no duplicates
And then stream the examTypes map and then collect into map if MarksMstEntity exists with same id in marks map
Map<Integer, Integer> result = examTypes.entrySet()
.stream()
.map(entry->new AbstractMap.SimpleEntry<Integer, MarksMstEntity>(entry.getKey(), marks.get(entry.getKey())))
.filter(entry->entry.getValue()!=null)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
I have a HashMap with key of type Double and my custom object as value.
It looks like this:
private static Map<Double, Incident> incidentHash = new HashMap<>();
The Incident object has following attributes: String date, String address, String incidentType.
Now I have a String date that I get from the user as input and I want to check if there exists any incident in the HashMap with that user inputted date. There can be many Incidents in the HashMap with the given date but as long as there's at least one Incident with the given date, I can do *
something.
I can just iterate over all the values in the HashMap and check if a given date exists but I was wondering if there is any better and more efficient way possible without modifying the data structure.
Given your HashMap, NO, there is not another way of doing so without iterating that HashMap.
As for changing the structure, you could do as Map<String, List<Incident>> that way you would have a date as key and a List of incidents for that date, given your requirement: There can be many Incidents in the HashMap with the given date.
So this would be a O(1)
//considering that the key is added when you have at least one incident
if (yourHash.get("yourDateStringWhatEverTheFormatIs") != null)
You can use streams API (from Java8) as shown in the below code with inline comments:
String userInput="10-APR-2017";
Optional<Map.Entry<Double, Incident>> matchedEntry =
incidentHash.entrySet().stream().
//filter with the condition to match
filter(element -> element.getValue().getDate().equals(userInput)).findAny();
//if the entry is found, do your logic
matchedEntry.ifPresent(value -> {
//do something here
});
If you are looking for something prior to JDK1.8, you can refer the below code:
String userInput="10-APR-2017";
Set<Map.Entry<Double, Incident>> entries = incidentHash.entrySet();
Map.Entry<Double, Incident> matchedEntry = null;
for(Iterator<Map.Entry<Double, Incident>> iterator = entries.iterator();
iterator.hasNext();) {
Map.Entry<Double, Incident> temp = iterator.next();
if(temp.getValue().getDate().equals(userInput)) {
matchedEntry = temp;
break;
}
}
You can use a TreeMap with your custom Comparator. In your Comparator compare the values of dates.
You would have to iterate through the map until you find a data that matches. Since you only need to know if any occurrences exist you can simply exit the loop when you find a match instead of iterating the rest of the map.
You can only keep a second Hash/TreeMap that matches the attribute to the object, so you can also check this attibute qickly. But you have to curate one such map for each attribute you want to access quickly. This makes it a bit more complex and use more memory, but can be much much faster.
If this is not an option the stream API referenced in other answers is a nice and tidy way to iterate over all objects to search for an attribute.
private static Map<Double, Incident> incidentHash = new HashMap<>();
private static Map<String, List<Incident>> incidentsPerDayMap = new HashMap<>();
Given that you don't want to iterate the Map and currently it's the only way to get the required value, I would recommend recomment another Map that contains Date as key and List<Incident> as value. It can be a TreeMap, e.g.:
Map<Date, List<Incident>> incidents = new TreeMap<>();
You can put the entry in this Map whenever an entry is added into the original Map, e.g.:
Incident incident = ;// incident object
Date date; //Date
incidents.computeIfAbsent(date, t -> new ArrayList<>()).add(incident);
Once the user inputs the Date, you can get all the incidents belonging to this date just by incidents.get(). Although that will give you a list and you still need to iterate over it, it will contain a lot less elements and get method in TreeMap will guarantee you log n complexity as it is sorted. So, your search operation will be much more efficient.
I currently have a map which stores the following information:
Map<String,String> animals= new HashMap<String,String>();
animals.put("cat","50");
animals.put("bat","38");
animals.put("dog","19");
animals.put("cat","31");
animals.put("cat","34");
animals.put("bat","1");
animals.put("dog","34");
animals.put("cat","55");
I want to create a new map with total for unique items in the above map. So in the above sample, count for cat would be 170, count for bat would be 39 and so on.
I have tried using Set to find unique animal entries in the map, however, I am unable to get the total count for each unique entry
First, don't use String for arithmetic, use int or double (or BigInteger/BigDecimal, but that's probably overkill here). I'd suggest making your map a Map<String, Integer>.
Second, Map.put() will overwrite the previous value if the given key is already present in the map, so as #Guy points out your map actually only contains {cat:55, dog:34, bat:1}. You need to get the previous value somehow in order to preserve it.
The classic way (pre-Java-8) is like so:
public static void putOrUpdate(Map<String, Integer> map, String key, int value) {
Integer previous = map.get(key);
if (previous != null) {
map.put(key, previous + value);
} else {
map.put(key, value);
}
}
Java 8 adds a number of useful methods to Map to make this pattern easier, like Map.merge() which does the put-or-update for you:
map.merge(key, value, (p, v) -> p + v);
You may also find that a multiset is a better data structure to use as it handles incrementing/decrementing for you; Guava provides a nice implementation.
As Guy said. Now you have one bat, one dog and one cat. Another 'put's will override your past values. For definition. Map stores key-value pairs where each key in map is unique. If you have to do it by map you can sum it just in time. For example, if you want to add another value for cat and you want to update it you can do it in this way:
animals.put("cat", animals.get("cat") + yourNewValue);
Your value for cat will be updated. This is for example where our numbers are float/int/long, not string as you have. If you have to do it by strings you can use in this case:
animals.put("cat", Integer.toString(Integer.parseInt(animals.get("cat")) + yourNewValue));
However, it's ugly. I'd recommend create
Map<String, Integer> animals = new HashMap<String, Integer>();
I'm relatively new to Java and I have a question about what type of data structure would be best for my case. I have a set of data which are essentially key-value pairs, however each value may correspond to multiple keys and each key may correspond to multiple values. A simplified example would be:
Red-Apple
Green-Apple
Red-Strawberry
Green-Grapes
Purple-Grapes
Considering the above example, I need to be able to return what color apples I have and/or what red fruits I have. The actual data will generated dynamically based upon an input file where each set will be anywhere from 100-100,000 values and each value may correspond to hundreds of values in the other set.
What would be the most efficient way to store and parse this data? I would prefer a solution as native to java as possible rather than something such as an external database.
This question is related, but I'm not sure how to apply the solution in my case given that I would need to assign multiple values to each key in both directions.
As you can't have duplicate keys in a Map, you can rather create a Map<Key, List<Value>>, or if you can, use Guava's Multimap.
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("Red", "Apple");
multimap.put("Red", "Strawberry");
System.out.println(multimap.get("Red")); // Prints - [Apple, Strawberry]
But the problem is you can't ask for the keys of a given object, I'll keep looking and make and edit if I find something else, hope it helps.
Still, you can make the reverse yourself by iterating the map and finding the keys for the object.
I suggest you use Guava's Table structure. Use color as your row keys and fruit as your column key or the other way round. Specifically, HashBasedTable is well suited for your case.
As per your use case, you wouldn't need to store anything for the values. However, these Tables don't allow null values. You could use a dummy Boolean or any other statistical useful value, i.e. date and time of insertion, user, number of color/fruit pairs, etc.
Table has the methods you need, such as column() and row(). Bear in mind that the docs say that these structures are optimized for row access. This might be OK for you if you plan to access by one key more than by the other.
You can create your own custom data structure
public class MultiValueHashMap<K, V> {
private HashMap<K, ArrayList<V>> multivalueHashMap = new HashMap<K, ArrayList<V>>();
public static void main(String[] args) {
MultiValueHashMap<String, String> multivaluemap = new MultiValueHashMap<String, String>();
multivaluemap.put("Red", "Apple");
multivaluemap.put("Green", "Apple");
multivaluemap.put("Red", "Strawberry");
multivaluemap.put("Green", "Grapes");
multivaluemap.put("Purple", "Grapes");
for(String k : multivaluemap.keySet()){
System.out.println(k + " : " + multivaluemap.get(k).toString());
}
}
public void put(K key, V value){
if (multivalueHashMap.containsKey(key)){
ArrayList<V> values = multivalueHashMap.get(key);
values.add(value);
}else{
ArrayList<V> values = new ArrayList<V>();
values.add(value);
multivalueHashMap.put(key, values);
}
}
public Set<K> keySet(){
return multivalueHashMap.keySet();
}
public ArrayList<V> get(K key){
return multivalueHashMap.get(key);
}
}
The output should be
Red : [Apple, Strawberry]
Purple : [Grapes]
Green : [Apple, Grapes]
I have a table consisting 10 columns in database. One of the columns have alphaneumaric values which was set as a key for the hashmap which will have all the values as objects for each row in the hashmap.
example : A10 (key) , rowobj(Class). Now in that same column another value is present as A10. So now if we are trying to load the values to hashmap from the tableas the key name is same will there be multiple values enrolled to that same key name?
Also can we combine the values of 2 columns to create a unique key for the hash map? How to do that?
Thanks
Two options:
create a wrapper object that contains all values as fields, set them, and then map.put(id, rowObject)
use Multimap (guava)
Can't you use primary key of the table as the key of your HashMap ?
You could use a HashMap where the key is still A10 but now the value is a List.
So you could have multiple values for the same key. You only have to pay attention in the insertion that new List is created when the first element is inserted. May be something like (in pseudocode):
HashMap<String, List> myMap = new HashMap<String,List>;
for (elements to insert){
if (!myMap.containsKey(element.key()))
ArrayList myList = new ArrayList();
myList.add(element);
myMap.put(element.key(), myList);
}else{
ArrayList myList = myMap.get(element.key());
myList.add(element);
myMap.put(element.key(), myList);
}
}
If you mean that a Key x should map to a list vals (which are the values that x represents), then that is easily doable like this (didnt check the syntax, so dont expect it to compile):
//assuming that the keys are of type int and values are of type String
Map<int,List<String>> myMap = new HashMap()<int, new ArrayList()<String>>;