Comparing two object arraylists - java

First time here so I hope this makes sense!
I have two object arrays say l1 and l2, I want to run a compare between these two lists and get a the unmatched value in say in l3.
User class contains 2 Strings:
userEnteredValue
valueReturnedFromDatabase
Say, l1 contains: Java, JSF, JAXR, foo
l2 contains: JSF, JAXR
I could run a compare for matching values, but for not for non-matching values. The logic seems to be flawed. Any help?
For matching values:
for(User u1 : l1) {
for(User u2: l2) {
if(u1.getUserEnteredValue().equals(u2.getValueReturnedFromDatabase())) {
l3.add(u1);
}
}
But, for the non-matching when I say not equal to, instead of getting only the unique values I get all values.
A couple of similar posts on Stackoverflow suggest to implement the equals and hashcode method in the User class. Is this necessary, since my arraylist size don't go beyond 5 to 10.

You can do something like this:
for(User u1 : l1) {
boolean unique = true;
for(User u2: l2) {
if(u1.getUserEnteredValue().equals(u2.getValueReturnedFromDatabase())) {
unique = false;
break;
}
}
if(unique){
l3.add(u1);
}
}

You can use the contains() method from java.util.ArrayList to determine whether your list contains the object.
Example:
for(User u1:l1) {
if(!l2.contains(u1)) {
l3.add(u1);
}
}

https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#contains(java.lang.Object)
Iterate through one array and check if it "contains()" the element from other array, using the above method provided with ArrayList in java.

If you can use Java 8 and you would like some shorter format:
List<String> l1 = Arrays.asList("a", "b", "c");
List<String> l2 = Arrays.asList("b");
List<String> l3 = l1.stream().filter(e -> !l2.contains(e)).collect(Collectors.toList());

Let's make your problem even smaller.
I have 2 arrays
l1 = [1, 2]
l2 = [2, 3]
for(int i : l1) {
for(int j : l2) {
if(i != j) {
l3.add(i);
}
}
}
Now let's examine this code.
When i = 1, j = 2 then i != j i.e. 1 != 2 is true and 1 will be inserted in l3.
When i = 1, j = 3 then i != j i.e. 1 != 3 is true and 1 again will be inserted in l3.
When i = 2, j = 2 then i != j i.e. 2 != 2 is false
When i = 2, j = 3 then i != j i.e. 1 != 2 is true and 2 will be inserted in l3.
So the final array will be l3 = [1, 2] (if it is a set. or [1, 1, 2] if it is a list) i.e. all the elements of l1 will be inserted in l2.
To get unique elements of l1 you will have to check all elements of l2 for same element in l1 1 by 1 and if it is not found in complete l2 then add it in l3.
rootloop:
for(int i : l1) {
for(int j : l2) {
if(i == j) {
continue rootloop;
}
l3.add(i);
}
}
Now change the above code to work for your problem.
But these kind of searching are so common that their implementation is already given in collection framework.
for(int i : l1) {
if(!l2.contains(i)){
l3.add(i);
}
}

One could use the Array of matching entities for the creation of unmatched entities as follows:
User[] matchingUsers = ...
User[] AllUsers = ...
List<User> listOfMatchingUsers = Arrays.asList(matchingUsers);
List<User> listOfAllUsers = Arrays.asList(allUsers);
List<User> unmatchedUsers = listOfAllUsers.removeAll(listOfMatchingUsers);

I see many answers using list.contains, i do not agree as contains use direct loop so the performance will be an issue for large lists O(n^2).
Instead you can use maps, add each list to a map with key and value the same then do the following:
Iterator it = map1.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
if(map2.get(pair.getValue())!=null) {
list3.add(map2.get(pair.getValue()));
}
}

Related

Find an element from list a and b such that the sum of their values is less or equal to target and as close to target as possible

https://leetcode.com/discuss/interview-question/373202/amazon-oa-2019-optimal-utilization Given 2 lists a and b. Each element is a pair of integers where the first integer represents the unique id and the second integer represents a value. Your task is to find an element from a and an element form b such that the sum of their values is less or equal to target and as close to target as possible. Return a list of ids of selected elements. If no pair is possible, return an empty list.
question was this but I had to use Lists (like <<1,1>, <2,2> <3,3>>)
my solution was something like below. I kept failing some test cases with a NullPointerException. I am trying to find out WHAT SPECIFIC INPUT(foreground, background) COULD HAVE CAUSED THIS. (the assessment website HID the error line). the problem did not have any specifications or guarantees like 0 < deviceCapacity < 1000000, so I do NOT know what was passed in
Things I checked for:
foreground and background were not null. they did not have null values (how i checked is below)
I put a System.out.println(foregroundApplications.get(i)); System.out.println(backgroundApplications.get(j)) just before initializing "sums", the goal being to see if any values were something like null or <null, null>, but they were all valid number pairs like <1,8>. (if it was null, it would have printed null right? unsure about this). example of what I saw (there were no nulls): <1, 14> <2, 14> <3, 14> <4, 14>
i checked in the beginning if the lists (foreground and background) were null with an if(foregroundApps == null), they weren't.
I can't change my code anymore, this was an assessment with obfuscated test cases which I am trying to figure out.
P.S. If there is a better approach than O(M*N) time, I would like to know
public List<List<Integer>> optimize(int deviceCapacity, List<List<Integer> foregroundApplications, List<List<Integer>> backgroundApplications)
{
TreeMap<Integer, List<List<Integer>>> map = new TreeMap<>();
for(int i = 0; i < foregroundApplications.size(); i++)
{
for(int j = 0; j < backgroundApplications.size(); j++)
{
int sum = foregroundApplications.get(i).get(1) + backgroundApplications.get(j).get(1);
if(sum<=deviceCapacity)
{
List<List<Integer>> list= new ArrayList<>();
if(map.containsKey(sum))
{
list = map.get(sum);
}
List<Integer> pair = new ArrayList<>();
pair.add(foregroundApplications.get(i).get(0));
pair.add(backgroundApplications.get(j).get(0));
list.add(pair);
map.put(sum, list);
}
}
}
if(map.size() == 0)
{
List<List<Integer>> list= new ArrayList<>();
List<Integer> emptyPair = new ArrayList<>();
emptyPair.add(null);
emptyPair.add(null);
list.add(emptyPair);
return list;
}
return map.get(map.lastKey());
}

remove (and count) duplicates from a list

Is it possible to iterate between two iterators of a same list and remove an item within the nested iterator?
Version 1 (does not work):
var i = all.iterator();
while (i.hasNext()) {
var a = i.next();
var j = all.iterator();
while (j.hasNext()) {
var b = j.next();
if (!a.shouldBRemoved(b)) {
a.setDuplicates(a.getDuplicates + 1);
// I want to remove the element on the fly
// because on each iteration the iterated elements will have a decreased size and will iterate faster (because of fewer elements)
// However: this does NOT work because of ConcurrentModificationException:
j.remove();
}
}
}
I get a java.util.ConcurrentModificationException, because I modify an element within the same iterator..
I can solve this issue by using another list removableItems and put those items in it:
Version 2 (works):
for (var a : all) {
for (var b : all) {
if (!a.shouldBRemoved(b)) {
a.setDuplicates(a.getDuplicates + 1);
// this works,
// however I must use an additation list to keep track of the items to be removed
// it's also not more performant than removing the elements on the fly
// because on each iteration the iterated elements has the same size
removableItems.add(b);
}
}
}
all.removeAll(removableItems);
Is there a way to solve this without needing an intermediate list removableItems? I want to remove the element on the fly.
Try to Use CopyOnWriteArrayList which support concurrent modification
List < String > myList = new CopyOnWriteArrayList < String > ();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
for (int i = 0; i < myList.size(); i++) {
System.out.println("List value: " + myList.get(i));
if (myList.get(i).equals("3")) {
myList.remove(i);
i--;
myList.add("6");
}
}
System.out.println("List Size:" + myList.size());
I found a good solution so far (Version 3):
List<Item> removeDuplicates(List<Item> all) {
var uniqueResults = new ArrayList<Item>();
for (var a : all) {
for (var b : all) {
// check if "a" and "b" is not the same instance, but have equal content
if (!a.equals(b) && a.isDeepEqualTo(b)) {
if (a.duplicates == 0 && b.duplicates == 0) {
// "a" has duplicates:
// Add only "a" and discard "b" for the rest of the loops.
uniqueResults.add(a);
}
// count the number of duplicates
a.duplicates = a.duplicates + 1;
}
}
// "a" has no duplicates, add it.
if (a.duplicates == 0 && !uniqueResults.contains(a)) {
uniqueResults.add(a);
}
}
return uniqueResults;
}
It works so far - I don't see any edge cases where this would wrongly (not) remove.
It's also better than using version 2 (with its removableItems()-list) as this is more performant (especially for huge lists) because we do not use remove or removAll, we only add items (which has O(1)).

How to find difference in Java list?

I a having two string's
Here the + is missing after 5 characters so the answer is +5|. The logic I wrote has a problem when I use list1.contains(s) and if the number s is more than once then also index will increase and the wrong position will be saved as +1| which is wrong.
Here I am facing one more issue difference in list I am not getting the answer.
String s1 = "7 + 8 = 7 8";
String s2 = "7 + 8 = 7 + 8";
List<String> list1 = Arrays.asList(s1.split("\\s+"));
List<String> list2 = Arrays.asList(s2.split("\\s+"));
int index = 0;
for(String s : list2){
if(list1.contains(s)){
index++;
}else{
System.out.print(s+index+"|");
}
}
This function I created to find the difference, sometimes it returns the difference but sometimes it does not
public static <T> List<T> difference(List<T> answer, List<T> header) {
List<T> toReturn = new ArrayList<>(answer);
toReturn.removeAll(header);
return toReturn;
}
Your question indicates that a difference is not only defined by the elements being present in both lists but that their order matters too, i.e. 1,2,3 would be different from 3,2,1.
Hence, don't use contains() as this only reports whether one list contains an element but doesn't take the order into account.
To find positional differences, you need to compare elements at the same index. Here's a simple example of what that could look like (note that this would be inefficient when using LinkedList in which case you'd better use an iterator).
<T> int findFirstDifference(List<T> list1, List<T> list2) {
//restrict iteration to the size of the smaller list
int smallerSize = Math.min(list1.size(), list2.size());
for( int index = 0; index < smallerSize; index++) {
T e1 = list1.get(index);
T e2 = list2.get(index);
//objects are different, this is the first difference
if(!Objects.equals(e1, e2)) {
return index;
}
}
//at this point the smaller list completely matches the start of the larger list
//if there is a larger list the first difference is the index after the end of the matching portion
if( list1.size() != list2.size() ) {
return smallerSize;
}
//no difference found
return -1;
}

Merging a 2D array of intervals

Below is code that takes a 2D array (a list of intervals) and merges them. Each interval is of size 2, but the list of intervals is of size n, e.g.
intervals = [[1,2], [2,4], [8,10]]
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
LinkedList<int[]> mergedList = new LinkedList<int[]>();
for (int i = 0; i < intervals.length; i++) {
if (mergedList.size() == 0 || mergedList.getLast()[1] < intervals[i][0]) {
mergedList.add(intervals[i]);
} else {
int max = Math.max(mergedList.getLast()[1], intervals[i][1]);
mergedList.getLast()[1] = max;
// mergedList.getLast() = new int[] { mergedList.getLast()[0], max };
}
}
return mergedList.toArray(new int[mergedList.size()][]);
}
In the else statement, I originally tried the commented line but it gave me an unexpected type error. Why am I unable to replace the array within the mergedList BUT I'm able to replace the value within it? And when replacing the value, how do I know it's not just creating a copy and actually modifying the linked list?
this line is the error
mergedList.getLast() = new int[] { mergedList.getLast()[0], max };
i think you wanted to write setLast in order to modify the last member of the list
mergedList.setLast()
You cannot assign a value to a function like you can to a variable!
a = 123 // works
a() = 123 // doesn't work
I'm guessing you want to overwrite the last item on the list. Since a LinkedList has no setLast method, you first have to determine the index of the last element and then overwrite it with set(index, yourReplacingValueHere).Try this:
mergedList.set(mergedList.indexOf(mergedList.getLast()), yourReplacingValueHere)
Maybe that's an acceptable solution.

Concerns with listiterator remove java

I am trying to work the Josephus problem in an arrayList data structure and the task is to use ListIterator. I am getting stuck at the removing part.
Lets say I have following 10 soldiers - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
And I have to step every 3rd soldier - which means remove 3, then 6, then 9, and so on.
I am able to successfully remove 3, but next time it removes 7 and not 6. Can you please review my code and tell me what I am doing wrong:
private static int suicide (List<Integer> list, int step) {
ListIterator<Integer> itr = list.listIterator();
while(itr.hasNext()&& list.size() != 1){
if((itr.nextIndex()+1) % step == 0) {
System.out.println(itr.previousIndex()+1);
itr.next();
itr.remove();
itr.next();
}
else {
itr.next();
}
if(!itr.hasNext() ){
itr=list.listIterator();
}
}
return 0;
}
One possibility is, that you make a copy of the list, and remove the items in that copy, still iterating the "original" (and skipping on iterator step in the copy).
ListIterator itr = list.listIterator();
ListIterator itr2 = copy.listIterator();
while(itr.hasNext()&& list.size() != 1){
if((itr.nextIndex()+1) % step == 0) {
System.out.println(itr.previousIndex()+1);
itr.next();
itr2.next();
itr2.remove();
itr.next();
}
else {
itr.next();
itr2.next();
}
Try this;
int i = 3;
int step = 1;
while (list.size() > i) {
list.remove(i);
step++;
i = 3 * step - step + 1;
}
Revert you design logic !
Building a new List with accepted elements only is a really best approach.
Removing is always harder to understand and to maintain.
For example you methode suicide should return a new List<> with the elements you want.
Your code will be really simpler :
List<Integer> cleanedList = new ArrayList<Integer>();
for(Integer soldier : list) {
if(solider%3!=0){
cleanedList.add(soldier);
}
}
return cleanedList;
The most of the time I prefer API that creates new objets. If you apply this rule you code will always get simpler code ! And updating parameters is not a good behavior : if you call the method twice, you obtains two differents results.

Categories