Improve performance of mapping values - java

I have the following code. I would like to know if I can improve the performance of the following code.
class CarModel {
private long carKey;
private String carName;
private CarColor carColor;
private CarEngine carEngine;
}
class CarColor {
private long carKey;
private String carPrimaryColor;
private String carSecondaryColor;
}
class CarEngine {
private long carKey;
private String carEnginePartNumber;
private String carEngineTransmissionFluid;
}
I am getting values for CarModel class from database which has only carKey and carName.
Then I get values for CarColor and carEngine and perform some data transformation on those lists and then assign the values to the CarModel so that I can update tables on the database.
The mapping of values to the CarModel is done in the following way
private void addList(List<?> list, List<CarModel> carModelList) {
carModelList.stream().forEach(x -> {
for (Object object : list) {
if (object instanceof CarColor
&& ((CarColor) object).getCarKey() == x.getCarKey()) {
x.setCarColor(((CarColor) object));
break;
}
if (object instanceof CarEngine
&& ((CarEngine) object).getCarKey() == x.getCarKey()) {
x.setCarEngine(((CarEngine) object));
break;
}
}
});
}
The above code works. Is there any way I can improve the code in addList() method and make it run faster?
Any help would be greatly appreciated! Thanks you

Yes, by storing the CarColor and CarEngine objects in a Map data structure with their carKey as the key.
Then, rather than repeatedly checking the list, the necessary CarColor or CarEngine object from the Map may be retrieved to perform the mapping of values to the CarModel.
This would significantly lower the time complexity from O(n2) to O(n).
Something like:
void addList(List<?> list, List<CarModel> carModelList) {
Map<Long, CarColor> colorMap = new HashMap<>();
Map<Long, CarEngine> engineMap = new HashMap<>();
for (Object object : list) {
if (object instanceof CarColor) {
CarColor color = (CarColor) object;
colorMap.put(color.getCarKey(), color);
}
if (object instanceof CarEngine) {
CarEngine engine = (CarEngine) object;
engineMap.put(engine.getCarKey(), engine);
}
}
carModelList.forEach(x -> {
CarColor color = colorMap.get(x.getCarKey());
if (color != null) {
x.setCarColor(color);
}
CarEngine engine = engineMap.get(x.getCarKey());
if (engine != null) {
x.setCarEngine(engine);
}
});
}

For each element of carModelList you iterate the complete list once, meaning you'll have n * m inner loop iterations where n is the size of carModelList and m is the size of list.
Instead of looping over carModelList, it would be much faster to build a Map<Long,CarModel> (that you probably want to keep around in the long term, possibly instead of carModelList) and just lookup the CarModel object using the objects carKey, thus eliminating the outer loop entirely.

One way to improve the performance of this code is to use a Map instead of a List to store the information in the "list" parameter. The key of the map could be the "carKey" and the value could be either a "CarColor" or a "CarEngine". Then, you could use the "get" method of the map to retrieve the information for each "CarModel" in the "carModelList". This would eliminate the need to iterate through the entire list for each "CarModel".
Here's the updated code:
private void addList(Map<Integer, Object> map, List<CarModel> carModelList) {
carModelList.stream().forEach(x -> {
Object object = map.get(x.getCarKey());
if (object instanceof CarColor) {
x.setCarColor((CarColor) object);
} else if (object instanceof CarEngine) {
x.setCarEngine((CarEngine) object);
}
});
}

Related

Iterate over ConcurrentHashMap while deleting entries

I want to periodically iterate over a ConcurrentHashMap while removing entries, like this:
for (Iterator<Entry<Integer, Integer>> iter = map.entrySet().iterator(); iter.hasNext(); ) {
Entry<Integer, Integer> entry = iter.next();
// do something
iter.remove();
}
The problem is that another thread may be updating or modifying values while I'm iterating. If that happens, those updates can be lost forever, because my thread only sees stale values while iterating, but the remove() will delete the live entry.
After some consideration, I came up with this workaround:
map.forEach((key, value) -> {
// delete if value is up to date, otherwise leave for next round
if (map.remove(key, value)) {
// do something
}
});
One problem with this is that it won't catch modifications to mutable values that don't implement equals() (such as AtomicInteger). Is there a better way to safely remove with concurrent modifications?
Your workaround works but there is one potential scenario. If certain entries have constant updates map.remove(key,value) may never return true until updates are over.
If you use JDK8 here is my solution
for (Iterator<Entry<Integer, Integer>> iter = map.entrySet().iterator(); iter.hasNext(); ) {
Entry<Integer, Integer> entry = iter.next();
Map.compute(entry.getKey(), (k, v) -> f(v));
//do something for prevValue
}
....
private Integer prevValue;
private Integer f(Integer v){
prevValue = v;
return null;
}
compute() will apply f(v) to the value and in our case assign the value to the global variable and remove the entry.
According to Javadoc it is atomic.
Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). The entire method invocation is performed atomically. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this Map.
Your workaround is actually pretty good. There are other facilities on top of which you can build a somewhat similar solution (e.g. using computeIfPresent() and tombstone values), but they have their own caveats and I have used them in slightly different use-cases.
As for using a type that doesn't implement equals() for the map values, you can use your own wrapper on top of the corresponding type. That's the most straightforward way to inject custom semantics for object equality into the atomic replace/remove operations provided by ConcurrentMap.
Update
Here's a sketch that shows how you can build on top of the ConcurrentMap.remove(Object key, Object value) API:
Define a wrapper type on top of the mutable type you use for the values, also defining your custom equals() method building on top of the current mutable value.
In your BiConsumer (the lambda you're passing to forEach), create a deep copy of the value (which is of type your new wrapper type) and perform your logic determining whether the value needs to be removed on the copy.
If the value needs to be removed, call remove(myKey, myValueCopy).
If there have been some concurrent changes while you were calculating whether the value needs to be removed, remove(myKey, myValueCopy) will return false (barring ABA problems, which are a separate topic).
Here's some code illustrating this:
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
public class Playground {
private static class AtomicIntegerWrapper {
private final AtomicInteger value;
AtomicIntegerWrapper(int value) {
this.value = new AtomicInteger(value);
}
public void set(int value) {
this.value.set(value);
}
public int get() {
return this.value.get();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AtomicIntegerWrapper)) {
return false;
}
AtomicIntegerWrapper other = (AtomicIntegerWrapper) obj;
if (other.value.get() == this.value.get()) {
return true;
}
return false;
}
public static AtomicIntegerWrapper deepCopy(AtomicIntegerWrapper wrapper) {
int wrapped = wrapper.get();
return new AtomicIntegerWrapper(wrapped);
}
}
private static final ConcurrentMap<Integer, AtomicIntegerWrapper> MAP
= new ConcurrentHashMap<>();
private static final int NUM_THREADS = 3;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; ++i) {
MAP.put(i, new AtomicIntegerWrapper(1));
}
Thread.sleep(1);
for (int i = 0; i < NUM_THREADS; ++i) {
new Thread(() -> {
Random rnd = new Random();
while (!MAP.isEmpty()) {
MAP.forEach((key, value) -> {
AtomicIntegerWrapper elem = MAP.get(key);
if (elem == null) {
System.out.println("Oops...");
} else if (elem.get() == 1986) {
elem.set(1);
} else if ((rnd.nextInt() & 128) == 0) {
elem.set(1986);
}
});
}
}).start();
}
Thread.sleep(1);
new Thread(() -> {
Random rnd = new Random();
while (!MAP.isEmpty()) {
MAP.forEach((key, value) -> {
AtomicIntegerWrapper elem =
AtomicIntegerWrapper.deepCopy(MAP.get(key));
if (elem.get() == 1986) {
try {
Thread.sleep(10);
} catch (Exception e) {}
boolean replaced = MAP.remove(key, elem);
if (!replaced) {
System.out.println("Bailed out!");
} else {
System.out.println("Replaced!");
}
}
});
}
}).start();
}
}
You'll see printouts of "Bailed out!", intermixed with "Replaced!" (removal was successful, as there were no concurrent updates that you care about) and the calculation will stop at some point.
If you remove the custom equals() method and continue to use a copy, you'll see an endless stream of "Bailed out!", because the copy is never considered equal to the value in the map.
If you don't use a copy, you won't see "Bailed out!" printed out, and you'll hit the problem you're explaining - values are removed regardless of concurrent changes.
Let us consider what options you have.
Create your own Container-class with isUpdated() operation and use your own workaround.
If your map contains just a few elements and you are iterating over the map very frequently compared against put/delete operation. It could be a good choice to use CopyOnWriteArrayList
CopyOnWriteArrayList<Entry<Integer, Integer>> lookupArray = ...;
The other option is to implement your own CopyOnWriteMap
public class CopyOnWriteMap<K, V> implements Map<K, V>{
private volatile Map<K, V> currentMap;
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newOne = new HashMap<K, V>(this.currentMap);
V val = newOne.put(key, value);
this.currentMap = newOne; // atomic operation
return val;
}
}
public V remove(Object key) {
synchronized (this) {
Map<K, V> newOne = new HashMap<K, V>(this.currentMap);
V val = newOne.remove(key);
this.currentMap = newOne; // atomic operation
return val;
}
}
[...]
}
There is a negative side effect. If you are using copy-on-write Collections your updates will be never lost, but you can see some former deleted entry again.
Worst case: deleted entry will be restored every time if map get copied.

Sort and merge two Arraylist of user defined type Library class

Aim : I have to two sorted Arraylists of OSD Type(class part of an library inported). I need to merge them to create final sorted list(soerted by time in descending order) in the most optimized way.
Problem : I can't modify the user defined OSD class as its part
of an library and hence I cant edit and make it implements Comparable
class. I have already created the function which performs the task,
but it I want to optimize the code and make it less expensive.
I have come across many solutions that involve implementing Comparable,which I can't do.
Current working code that I want to optimize for better performance
private ArrayList<OSD> latestFistSortMergeOsds(
ArrayList<OSD> awbThingTypeOSDs,
ArrayList<OSD> LDDthingTypeOSDs) {
ArrayList<OSD> sortedOsds = new ArrayList<OSD>();
ArrayList<Long> timestampList = null;
HashMap<Long, OSD> awbThingTypeOSDsHmap = null;
if (awbThingTypeOSDs != null) {
awbThingTypeOSDsHmap = new HashMap<Long, OSD>();
for (OSD mOSD : awbThingTypeOSDs) {
awbThingTypeOSDsHmap.put(mOSD.getUpdatedOn()
.getTime(), mOSD);
}
Log.i("sorted", "hmap" + awbThingTypeOSDsHmap);
}
HashMap<Long, OSD> LDDthingTypeOSDsHmap = null;
if (LDDthingTypeOSDs != null) {
LDDthingTypeOSDsHmap = new HashMap<Long,OSD>();
for (OSD nOSD : LDDthingTypeOSDs) {
LDDthingTypeOSDsHmap.put(nOSD.getUpdatedOn()
.getTime(), nOSD);
}
}
// merge n sort timestamp
if (awbThingTypeOSDsHmap != null) {
timestampList = new ArrayList<Long>(awbThingTypeOSDsHmap.keySet());
if (LDDthingTypeOSDsHmap != null) {
ArrayList<Long> timestampListLDD = new ArrayList<Long>(
LDDthingTypeOSDsHmap.keySet());
timestampList.addAll(timestampListLDD);
}
Collections.sort(timestampList, Collections.reverseOrder());// descending
Log.i("sorted", "sorted keyList" + timestampList);
}
// merge sorted osds- latest first
// timestamplist is sorted in desc order
if (timestampList != null) {
for (Long timestampKey : timestampList) {
RareMediaCompanyOSD osd = null;
osd = awbThingTypeOSDsHmap.get(timestampKey);
if (osd == null) {
osd = LDDthingTypeOSDsHmap.get(timestampKey);
}
sortedOsds.add(osd);
}
}
return sortedOsds;
}
I have referred to the following links
Comparing Long values using Collections.sort(object)
Comparing Long values using Collections.sort(object)
How to sort ArrayList<Long> in Java in decreasing order?
Please suggest better usage of the Hashmaps or should I use some other DS instead of hashmap to optimize the code.
P.S : its not a duplicate of these questions as I cannot edit the class and implement comparable,as its a part of library.
Thanks!
I would suggest creating a holder class, that holds an OSD object, which in turns implements Comparable
public class OSDHolder implements Comparable<OSDHolder> {
private final OSD obj;
public OSDHolder(OSD obj) {
this.obj = obj;
}
public long getTiming() {
this.obj.getUpdatedOn().getTime();
}
#Override
public int compareTo(OSDHolder o) {
if (this.getTiming() > o.getTiming())
return 1;
else if (this.getTiming < o.getTiming())
return -1;
return 0;
}
}
Then you can simply add the contents of both lists to one list as OSDHolder type
ArrayList<OSDHolder> holders = new ArrayList<>();
for (OSD osd:awbThingTypeOSDs) {
holders.add(new OSDHolder(osd));
}
for (OSD osd:LDDthingTypeOSDs) {
holders.add(new OSDHolder(osd));
}
Then sort holders with either Collections.sort() or Arrays.sort()

Iterate over map and run method on each result - APEX

I'm looking to check the field level security of all of the fields on the Opportunity object.
So I'm getting the full map of fields and storing them in a static variable so I can access them from where ever I need.
private static Map<String, Schema.SObjectField> myMap;
private static void initMaps() {
myMap = Schema.SObjectType.Opportunity.fields.getMap();
}
I want to iterate over all of the fields on the object and check .isAccessible() on each.
I'm a little stuck as to how to iterate over the fields however, and also how to check .isAccessible() on eash.
//THIS BIT DOESN'T WORK...
private doCheckMap(myMap){
for (Id key : myMap.keySet()) {
if(false == Schema.SObjectType.???.fields.isAccessible()) {
System.debug('nope!');
}
}
}
Any suggestions or advice would be greatly appreciated.
Cheers.
You could iterate through yout map like this:
Map<String, MyType> myMap = new HashMap<String, MyType>();
Iterator<Map.Entry<String, MyType>> iter = myMap.entrySet().iterator();
while(iter.hasNext()){
MyType entry = iter.next().getValue();
for (Field f : entry.getClass().getFields()) {
if (f.isAccessible()) {
//...
}
}
}
Then again why do you even need a map? Couldn'you just use reflexion api getFields() - like this:
for (Field f : Opportunity.getClass().getFields()) {
if (f.isAccessible()) {
//...
}
}

In java, is there a way to search for a specific field of a class stored in a linked list?

I wrote a class that is to be stored in a linkedlist, with 3 fields in the class. One of these fields is a String, which I would like to search for in the linked list.
Example
LinkedList
Obj1
String name = "first";
int age = 2;
int size = 4;
Obj2
String name = "second";
int age = 3;
int size = 6;
Obj3
String name = "third";
int age = 5;
int size = 8;
If this is the linkedlist storing these three objects with the given fields, is there a way to search the linked list for the object with the name "second"?
You can search for an item in the list by iteration
// Iterate over each object within the list
for(YourClass obj : yourLinkedList) {
// Check if the object's name matches the criteria, in this case, the name
// of the object has to match "second"
if (obj.name.equals("second")) {
// If we are within this block, it means that we found the object that has
// its name set as "second".
return obj;
}
}
You could also make a method to make things more elegant
public YourClass findByName(String name) {
for(YourClass obj : yourLinkedList) {
if (obj.name.equals(name)) {
return obj;
}
}
return null;
}
And use it the following way
YourClass object = findByName("second");
The easiest way to do this would be to of course, iterate through each element in the collection, checking if it matched your filter condition, and selecting the matches found. However this gets tedious the more times you need to do it, and the more complex your filter condition is. I would recommend utilizing pre-existing libraries to get the task done efficiently. Here is an example using Google-Collections:
final List<SomeObj> listObjs = Arrays.asList(
new SomeObj("first", 2, 4), new SomeObj("second", 3, 6),
new SomeObj("third", 5, 8));
final Iterable<SomeObj> filtered = Iterables.filter(listObjs,
new Predicate<SomeObj>() {
#Override
public boolean apply(final SomeObj obj) {
return "second".equals(obj.getName());
}
});
for (final SomeObj obj : filtered) {
System.out.println(obj);
}
The code shown would select all objects in the list with a name property of "second". Obviously, the predicate doesn't have to be an anonymous inner class - if you needed to reuse it you would just break it out to a standalone class.
Here's another way to implement a Comparator (just in case it helps).
I find it's easier to understand if you implement the Comparator explicitly:
class PersonAgeComparator implements Comparator<Person> {
#Override
public int compare(Person p1, Person person2) {
return p1.getAge().compareTo(p2.getAge());
}
}
You might use the above like this:
Comparator ageComparator = new PersonAgeComparator();
List<Person> personList = // populate list somehow
Person fourYearOld = new Person();
fourYearOld.setAge(4);
for (Person p : personList) {
if (ageComparator.compare(fourYearOld, p) == 0) {
System.out.println(p.getName() + " is 4 years old");
}
}
This doesn't make much sense for this simple example.
It would be ideal if you had several complicated ways to compare people (by height, by adjusted income, by how many states they've lived in, etc...).
Take a look at the java.util.Comprator interface. You can write a method that iterates over a List and uses a comparator to find the one you are after.
Something like (not compiled):
for(final T value : list)
{
if(comparator.compare(value, desired) == 0)
{
// match
}
}
In your comparator you have it perform whatever comparison you want.
Here is a working example:
public class JavaApplication4
{
public static void main(String[] args)
{
final List<Data> list;
final List<Data> a;
final List<Data> b;
list = new ArrayList<Data>();
list.add(new Data("Foo", 1));
list.add(new Data("Bar", 10));
list.add(new Data("Car", 10));
a = find(list,
new Data("Bar", 0),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.name.compareTo(o2.name));
}
});
b = find(list,
new Data(null, 10),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.count - o2.count);
}
});
System.out.println(a.size());
System.out.println(b.size());
}
private static List<Data> find(final List<Data> list,
final Data desired,
final Comparator<Data> comprator)
{
final List<Data> results;
results = new ArrayList(list.size());
for(final Data data : list)
{
if(comprator.compare(desired, data) == 0)
{
results.add(data);
}
}
return (results);
}
private static class Data
{
private final String name;
private final int count;
Data(final String nm,
final int c)
{
name = nm;
count = c;
}
}
}
And here is a generic version of the find method. Using this method you would never have to write the find method again, using a method that embeds the logic for matching in the iteration code means that you would have to re-write the iteration logic for each new set of matching logic.
private static <T> List<T> find(final List<T> list,
final T desired,
final Comparator<T> comprator)
{
final List<T> results;
results = new ArrayList(list.size());
for(final T value : list)
{
if(comprator.compare(desired, value) == 0)
{
results.add(value);
}
}
return (results);
}
You can go through it and get it done or there's another way.
You need to override the equals method in your class (and the hashcode method as well).
After you override the equals to your desire, in this case to compare the names, create a new object with the same name and call the remove(Object o) method of the LinkedList and get the object.
You should note that with this approach you objects equality will be defined by name and that the entry will be removed from the LinkedList

how to combine object item and sum up qty - JAVA

i having an object with below information
TransHdr: id, order_num
TransItem: hdr_id, product_code, refnum, qty (child record)
transHdr.id=transItem.hdr_id
if let say 3 record can be found in TransItem,
parkA,112,10
parkA,112,6
parkB,113,10
i would like group it base on refnum, means that my result will be
parkA,112,16
parkB,113,10
i need a method that will loop the object (item level) and need to return transHdr object to other function. anyway to do this?
for (java.util.Iterator<ITransItem> groupTransItems = TransHdr.getTransItems().iterator();
groupTransItems.hasNext();) {
ITransItem _TransItem = groupTransItems.next();
if (null!=_TransItem.getRefNum()){
<question here..how do i group and sum up my item and become only 1 record?>
}
}
return newGroupingTransHdr;
}
Create a new Map with refnum as key and qty as value.
Map<String,Integer> qtyMap=new HashMap<String,Integer>();
while iterating, try
String refNum=transItem.getRefNum();
// Mark for removal ? if this is not the first item in the list with the refnum
boolean remove=true;
Integer currentQty=qtyMap.get(refNum);
if(currentQty==null){
currentQty=0;
// this doesnt exist already in the map, this is the first item with this reference
// number in the list, so you should keep this without removing
remove=false;
}
currentQty=currentQty+transItem.getQty();
qtyMap.put(refNum,currentQty);
// if the remove is true then remove this item from the list.
if(remove){
groupTransItems.remove();
}
This will sum up the qty for refnum's in the map and once your iteration is over, the map will have the sums of quantities for each refnum. You will have to iterate the list once more to set the current qty to each item from the map [EDIT] :- Added the iterating time removal.
Similar to the solution suggested in this post. You can have a Map with ref_num as key and TransItem as value.
TransHdr transHdr; // Externally given
Map<String, ITransItem> collapsedItems = new HashMap<String, ITransItem>();
List<ITransItem> items = transHdr.getItems();
transHdr.setItems(new ArrayList<ItransItem>());
for (ITransItem item : items) {
String ref_num = item.getRefNum();
ITransItem collapsedItem = collapsedItems.get(ref_num);
if (collapsedItem == null) {
collapsedItems.put(ref_num, item);
} else {
int qnt = item.getQnt();
collapsedItem.setQnt(collapsedItem.getQunt() + qnt);
}
}
transHdr.setItems(new ArrayList<ITransItem>(collapsedItems.values()));
Another way to accomplish what you want to do is to embed the logic in an add method on your TransHdr class.
pulic class TransHdr {
private String id;
private int orderNumber;
private Map<String, ITransItem> items;
public TransHdr(String id, int orderNumber) {
this.id = id;
this.orderNumber = orderNumber;
this.items = new HashMap<String, ITransItem>();
}
public void addItem(ITransItem item) {
String ref = item.getRefNum();
ITransItem currentItem = items.get(ref);
if (currentItem == null) {
items.put(ref, item);
} else {
int qnt = item.getQnt();
currentItem.setQnt(currentItem.getQnt() + qnt);
}
}
public Set<ITransItem> getItems() {
return items.values();
}
}
As you can see, there's multiple ways of doing this. The appropriate solution depends on what your requirements and use cases are.

Categories

Resources