Effective way to Join ArrayList - java

I am looking for an effective way to create a list/map etc out of the below two lists, which I can use to get both current and past status of a rule.
List<Boolean> rulesCurrentStatus = new ArrayList<Boolean>(); // 3 Rules: false/true meaning if the rule passed of failed
rulesCurrentStatus.add(false);
rulesCurrentStatus.add(true);
rulesCurrentStatus.add(false);
List<Boolean> rulesPreviousStatus = new ArrayList<Boolean>(); // Previous state of the above 3 rules.
rulesPreviousStatus.add(true);
rulesPreviousStatus.add(true);
rulesPreviousStatus.add(false);

You can use Map with key of String type and value of Boolean type. You can differentiate between current and previous value using the key. e.g. Store all current values with key something similar C#1,C#2 and store previous values with key something similar P#1, P#2 etc.

if I understood you right, you want to get the status history of a rule.
then maybe this could help:
Map<Rule( or ruleName as String), List<Boolean>>
the key in that map is the rule object or e.g. a String indicate which rule. the value is a list (ArrayList for example), stores the status history. for example:
{"rule1":[True, False,True] //1st,2nd,3rd(current) status
"rule2":[True,False]
...
}
thus if you want to get the whole status-history of a rule by
List<Boolean> history = map.get("someRule")
then you could add new status, or get certain status by playing with the List.
if you only need pre and current, you could declare the List with initial capacity.

Related

Create a collection by associating two id's of two different object

I have two list of objects.
members: List,
membersHistory: List
Both of these objects have an "id" field. I want to create a list by joining both of these lists by making sure the "id" field of one is associated to the other object that have the same id. Both, object have different data but they are for specific member. Just need to pair them somehow to create a collection.
I started out something like below. But, I think I need to map them by "id" first before zipping them. Thank you!
members.zip(membersHistory).mapIndexed {_, pair ->
val (member, memberHistory) = pair
}
Here are a couple of ways to create a list of pairs of the items.
For each item in the first list, find a corresponding item in the second list with the same idea and pair them if found. mapNotNull will cause it to skip items that have no match in the second list.
val combination: List<Pair<Member, MemberHistory>> = members
.mapNotNull { member -> memberHistories.firstOrNull { it.id == member.id }?.let { member to it } }
To do this in O(n), you can create a map with the IDs as keys from one of the sources lists using associateBy.
val memberHistoryById = memberHistories.associateBy { it.id }
val combination = members.mapNotNull { member -> memberHistoryById[member.id]?.let { member to it } }
Presumably, though it hasn't been stated explicitly in the question, the id is the ID of a Member, so there will be no two objects in the members list with the same id value.
For quick lookup of Member by ID, I'd recommend creating 2 maps:
// Examples in Java
Map<Integer, Member> memberById = members.stream()
.collect(Collectors.toMap(Member::getId, Function.identity()));
Map<Integer, List<History>> memberHistoryById = membersHistory.stream()
.collect(Collectors.groupingBy(History::getId));
Those are both good to keep around, but if you want the Member and the History together, you can then create a combined map, keyed by the Member object. Assuming the natural order of Member is not the ID, we need a custom key, which we can do with TreeMap.
Map<Member, List<History>> historyByMember = memberHistoryById.entrySet().stream()
.collect(Collectors.toMap(e -> memberById.get(e.getKey()),
Map.Entry::getValue,
(a,b) -> a/*this is never called*/,
() -> new TreeMap(Comparator.comparingInt(Member::getId)));
The Answer by Andreas is a good one, using a map to associate a member object with its matching history object.
Write a class
Alternatively, you could create a class to bind the two objects together.
In Java 16 and later, a record might do. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString. A record can be declared locally or separately.
record MemberWithHistory ( Member member , History history ) {}
Loop your list of members.
For each member, find a matching history. We can do this easily with streams. Make a stream of your history objects, filtering for one whose member identifier matches the identifier of the nth member. An Optional is returned carrying the history object if found. Otherwise, if no matching history is found, the optional carries nothing.
If the optional does indeed have a found history object, instantiate a new MemberWithHistory record object. Collect that new record by adding to our results list named mwhs.
Here is some untested code to get your started.
List< MemberWithHistory > mwhs = new ArrayList<>( members.size() ) ;
for( Member member : members )
{
Optional< History > historyOptional = histories.stream().filter( history -> history.memberId.equals( member.id ) ).findAny() ;
if( historyOptional.isPresent() )
{
MemberWithHistory mwh = new MemberWithHistory( member , historyOptional.get() ) ;
mwhs.add( mwh ) ;
}
}
If you had a large number of items, searching by way of a stream repeatedly might become inefficient. Sorting and possibly deleting from a copy of the histories might be more efficient. But I would not bother for small data size or occasional use.

Aerospike - How to insert a unique item in a given list using Java?

If I run the code below, the value coming from getUserName() gets appended to the list. But if I run it again, same value gets inserted again. My question is, how to make sure the value is appended only once(Unique)?
My current code
Operation operation = ListOperation.append("names", Value.get(usr.getUserName()));
Record record = client.operate( policy, key, operation );
Example: Consider the "name" bin to be a list of strings.
'LIST["T1", "T2"]'
If I add T2 again, the list should remain the same.
ListOperation.append() can take a ListPolicy, which takes flags. For write operations such as this, the ListWriteFlags have the fields ADD_UNIQUE and NO_FAIL which you should combine if you want a value to only be added if it's a new unique list element.
See https://www.slideshare.net/RonenBotzer/asug-tlvmeetup2talk
i'm assuming that ListOperation is backed by a List since the code isn't available. Assuming it is, Lists maintain order while allowing null values. Sets on the other hand only allow unique, non-null, values so this would be a perfect solution for you.
Set<String> values = new HashSet<>();
values.add("T1");
values.add("T1"); // already contains value
Alternative
If you need to maintain the order of your Collection but you want unique values, another option would be to check on add if the value already exists OR using Stream#distinct.
List<String> values = ListOperation.values.stream().distinct().collect(Collectors.toList());

How to add item to dynamodb if a field does not exist or matches a condition?

I want to save an item if a field, is either non-existent or less than a given value.
So the first time the item is written, the field would be non-existent. and on subsequent writes, it needs to be less than a given value.
However it seems DynamoDBSaveExpression does not allow multiple conditions on a column. My current code looks like this:
Map<String, ExpectedAttributeValue> expected = new HashMap<>();
expected.put("lastProcessTime", new ExpectedAttributeValue().withComparisonOperator(ComparisonOperator.LT).withValue(
new AttributeValue().withN(String.valueOf(processTime))
));
DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression()
.withExpected(expected);
dynamoDBMapper.save(item, saveExpression);
However, this does not work if there isnt an existing item in the table with the same primary key since in that case the value of lastProcessTime is null.
How do i achieve what i need here?

Most efficient way to split one arraylist data into 4 seperate arraylists?

I am currently writing code which contains an arraylist. This arraylist includes data which is name, lastname, job and id. I need to seperate the data into different arraylists. Currently i am using the method which is shown below.
for (int i = 0; i < details.size(); i = i + 4) {
names.add(details.get(i));
lastname.add(details.get(i + 1));
job.add(details.get(i + 2));
id.add(details.get(i+3));
}
I want to know if there is a better way of doing this. The initial arraylist can be very long, and i dont know if there are any issues with this method.
You asked: "I want to know if there is a better way of doing this". There is a better way.
You should consider creating a class called Record that contains the data (name, last name, job, and ID), and create an ArrayList. Then, instead of using index locations (and potentially grab the wrong data item), you could use the Record getter methods to get the data item you need (and perhaps store it in a different list).
Step 1: Create a Record class:
public class Record
{
private String firstName;
private String lastName;
private String job;
private String id;
// TODO add constructor(s), getters and setters
}
Step 2: Create a list of Records (this is an better alternative that create a list having the information in different index locations. That way, each set of name, last name, job, and ID will be self-contained which is way better than disjointed in different index locations in a list.
ArrayList<Record> records = new ArrayList<Record>();
Step 3: Instead of using index locations (and potentially grab the wrong data item), you could use the Record getter methods to get the data item you need (and perhaps store it in a different list).
ArrayList<String> names = new ArrayList<String>();
ArrayList<String> jobs = new ArrayList<String>();
...
names.add(records.getLastName() + ", " + records.getFirstName());
jobs.add(records.getJob());
Alternatively, and maybe a better solution, you could use a Map to store this information. For example, a job ID could be the key in a Map that returns a job description and who has been assigned to perform it. Job IDs have to be unique. Adding IDs to a list can be duplicated, because the List interface doesn't restrict entering duplicate data. If you use a Map, they keys are guaranteed to be unique. The value being returned from the Map could be a Record object (or some other kind) that contains the name of the person and the job the person is responsible for. Since values can be duplicates, you can have a person performing multiple jobs, which is probably what you want to do. To use a Map:
Map<String, Record> jobs = new HashMap<String, Record>(); //This record class doesn't have ID in it.
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
jobs.put("321CBA", new Record("Bill", "Smith", "Install Light Fixtures");
A few things to consider if using a Map. If you try to make a new entry with an existing key, the old one will be overwritten.
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
jobs.put("ABC123", new Record("Bill", "Smith", "Install Light Fixtures"); //Overwrote the previous entry because key is the same
If you want to change the key for an existing value, you must obtain the value, store temporarily, remove the old record, and make a new entry with the old temp value:
jobs.put("ABC123", new Record("John", "Doe", "Fix Drywall");
Record rec = jobs.remove("ABC123"); // gets the record and removes old entry
jobs.put("321CBA", rec); // new job ID for old record
The main issue is that your details can have missing data. For example it has the size=5. Then your method will crush with IndexOutOfBounds. Your details list should contain a Person object which has all the details you want and then just use them to fill other lists.
The main performance kill will be the add operation since it will have to grow the data structure over time. Since you know details.size() you should initialize the other arraylists with details.size()/4.
You should also check that details.size() % 4 == 0 before the for loop. If not it means your data is somehow wrong and you will run for sure into an IndexOutOfBounds.
Just for correctness you should write i < details.size()+3 as your condition, since you will access element i+3 in the for body. You should always check for i < details.size()+x do it like this if you ever access i+x in the body. (for the largest x there will be in the body)

How to get the key when a corresponding value is used instead?

I am using Android 2.1 SDK, the application reads from the Sqlite database, a table that has two columns, an id, and a string.
I read in this into a HashMap<Long, String>, the value part of it gets displayed in a List, now, I wish to obtain the key value, so I cooked up this simple routine:
private Map.Entry<Long, String> getEntry(String sValue){
for (Iterator<Map.Entry<Long, String>> itMap = this.dbMap.entrySet().iterator(); itMap.hasNext();) {
Map.Entry<Long, String> curr = itMap.next();
if (curr.getValue().equalsIgnoreCase(sValue)) return curr;
}
return null;
}
My problem is being conscious of cpu cycles being chewed up in respect to Android, battery/cpu time, in looking for the value in the HashMap, that could be seen as a potential cycles of cpu lost.
Is there an easier and more efficient way of doing this instead of iterating?
The reasoning is that I can home in on the id, and directly delete the record from the table or even update it.
Um... it looks like the String should be the key, and the id the value. Which assumes that the Strings are unique, but so does your code.
Alternatively, can't your list keep the ID that corresponds to an entry around invisibly? This is how you'd usually do it (e.g. in Swing or in a HTML select).
Realistically, the only way to avoid having to iterate through is to keep two HashMaps (i.e. pay the memory cost) where one HashMap is the reverse of the first. You can create the reverse-lookup HashMap when you create your forward-lookup HashMap without having to loop through your data twice. That should give you low constant time access in both directions.
If you use an Adapter you can access the ID using the getItemID() method.

Categories

Resources