You can convert them to Set collections, and perform a set difference operation on them.
Like this:
Set<Date> ad = new HashSet<Date>(a);
Set<Date> bd = new HashSet<Date>(b);
ad.removeAll(bd);
Answer from Pablo Santa Cruz on Stack OverflowYou can convert them to Set collections, and perform a set difference operation on them.
Like this:
Set<Date> ad = new HashSet<Date>(a);
Set<Date> bd = new HashSet<Date>(b);
ad.removeAll(bd);
If you only want find missing values in b, you can do:
List toReturn = new ArrayList(a);
toReturn.removeAll(b);
return toReturn;
If you want to find out values which are present in either list you can execute upper code twice. With changed lists.
If I understand correctly, this is the example scenario:
- listOne [datab] items:
[A, B, C, D] - listTwo [front] items:
[B, C, D, E, F]
and what you need to get as an effect is:
- added:
[E, F] - deleted:
[A]
First thing first, I would use some type adapter or extend the different types from one common class and override the equals method so you can match them by id and name
Secondly, this is very easy operations on sets (you could use set's but list are fine too). I recommend using a library: https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html
And now basically:
- added is
listTwo - listOne - deleted is
listOne - listTwo
and using java code:
- added:
CollectionUtils.removeAll(listTwo, listOne) - deleted:
CollectionUtils.removeAll(listOne, listTwo)
Otherwise, all collections implementing Collection (Java Docs) also has removeAll method, which you can use.
I propose solution using java 8 streams:
ArrayList<ObjOne> list = new ArrayList<>(Arrays.asList(new ObjOne("1","1"),new ObjOne("3","3"),new ObjOne("2","2")));
ArrayList<ObjTwo> list2 = new ArrayList<>(Arrays.asList(new ObjTwo("1","1"),new ObjTwo("3","3"),new ObjTwo("4","4")));
List<ObjOne> removed = list.stream().filter(o1 -> list2.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
.collect(Collectors.toList());
System.out.print("added ");
removed.forEach(System.out::println);
List<ObjTwo> added = list2.stream().filter(o1 -> list.stream().noneMatch(o2 -> o2.getId().equals(o1.getId())))
.collect(Collectors.toList());
System.out.print("removed ");
added.forEach(System.out::println);
This is basically your solution but implemented using streams, which will make your code shorter and easer to read
Using Java 8 streams:
Logic Here:
I have used the java 8
streamoperation to get the difference between two given lists and created a output message usingStringBuilderas mentioned in the problem statement.
Code:
public class Test {
public static void main(String[] args) {
// Case 1
List<String> order1 = Arrays.asList("A", "B");
List<String> sended1 = Arrays.asList("A", "B");
System.out.println("Case 1: " + getMissingItems(order1,sended1));
// Case 2
List<String> order2 = Arrays.asList("A", "B", "C");
List<String> sended2 = Arrays.asList("B", "C");
System.out.println("Case 2: " + getMissingItems(order2,sended2));
// Case 3
List<String> order3 = Arrays.asList("A", "D");
List<String> sended3 = Arrays.asList("A", "B", "C", "D");
System.out.println("Case 3: " + getMissingItems(order3,sended3));
// Case 4
List<String> order4 = Arrays.asList("A", "D", "F");
List<String> sended4 = Arrays.asList("A", "B", "C", "D");
System.out.println("Case 4: " + getMissingItems(order4,sended4));
}
private static String getMissingItems(List<String> order,
List<String> sended){
StringBuilder sb = new StringBuilder();
List<String> missingInOrder = getMissingInList(order, sended);
createMessage(sb, missingInOrder," item(s) missing in order: ");
List<String> missingInSended = getMissingInList(sended, order);
if(!missingInOrder.isEmpty() && !missingInSended.isEmpty()){
sb.append(" && ");
}
createMessage(sb, missingInSended," item(s) missing in sended: ");
return sb.isEmpty() ? "No difference":sb.toString();
}
private static void createMessage(StringBuilder sb,
List<String> diffInList,
String msg) {
if(!diffInList.isEmpty()){
sb.append(diffInList.size())
.append(msg)
.append(String.join(",", diffInList));
}
}
private static List<String> getMissingInList(List<String> list1,
List<String> list2) {
return list2.stream()
.filter(e -> !list1.contains(e))
.collect(Collectors.toList());
}
}
Output:
Case 1: No difference
Case 2: 1 item(s) missing in sended: A
Case 3: 2 item(s) missing in order: B,C
Case 4: 2 item(s) missing in order: B,C && 1 item(s) missing in sended:F
First, it's not clear enough if there are duplicates or not. Secondly, if you don't care about order then don't use the list equals() method as it will fail in many cases where both lists contains the same items but in different order.
Besides that, the algorithm should work but it could be optimized. I believe the main problem is that using the removeAll() method between a list of size n and a list of size m has a worst time complexity of O(n*m) (quadratic) when you could actually achieve a time complexity of O(n+m) (lineal). If you don't care about duplicates you could slightly modify your solution to use HashSet to achieve this goal in the following way:
// If duplicates are irrelevant
private static String getDifference(List<String> ordered, List<String> sent) {
Set<String> orderedSet = new HashSet<>(ordered);
Set<String> sentSet = new HashSet<>(sent);
if (orderedSet.equals(sentSet)) {
return "No difference";
} else {
StringBuilder output = new StringBuilder();
orderedSet.removeAll(sent);
if (!orderedSet.isEmpty()) {
output.append(orderedSet.size()).append(" item(s) missing in sent: ");
output.append("\"").append(String.join("\", \"", orderedSet)).append("\"");
}
sentSet.removeAll(ordered);
if (!sentSet.isEmpty()) {
if (output.length() > 0) {
output.append(" & ");
}
output.append(sentSet.size()).append(" item(s) missing in ordered: ");
output.append("\"").append(String.join("\", \"", sentSet)).append("\"");
}
return output.toString();
}
}
If you care about duplicates then you aren't actually comparing Lists but Multisets. For a Multiset implementation you could use the Guava library. A possible implementation with Multisets looks like this:
// If duplicates are relevant, with Guava
private static String getDifferenceWithDuplicates(List<String> ordered, List<String> sent) {
Multiset<String> orderedSet = HashMultiset.<String>create(ordered);
Multiset<String> sentSet = HashMultiset.<String>create(sent);
if (orderedSet.equals(sentSet)) {
return "No difference";
} else {
StringBuilder output = new StringBuilder();
for (String item : Sets.union(orderedSet.elementSet(), sentSet.elementSet())) {
int orderedCount = orderedSet.count(item);
int sentCount = sentSet.count(item);
if (orderedCount != sentCount) {
if (output.length() > 0) {
output.append(" & ");
}
output.append(Math.abs(orderedCount - sentCount)).append(" item(s) missing in ");
output.append(orderedCount > sentCount ? "sent" : "ordered").append(": ");
output.append("\"").append(item).append("\"");
}
}
return output.toString();
}
}
If for some reason you can't use Guava, then you can implement the same behavior of a HashMultiset using a HashMap of type Map<String, Integer> where the key is the item and the value is the amount of appearances in the original list (this is an exercise for the reader).
Here's an algorithm I put together to merge two lists, old and new. It's not the most elegant or efficient, but it seems to work okay for the data I'm using it for.
new is the most updated list of data, and old is the out-of-date list that needs to get transformed into new. The algorithm performs its operations on the old list - removing, moving, and inserting items accordingly.
for(item in old)
if (new does not contain item)
remove item from old
for(item in new)
if (item exists in old)
if (position(item, old) == position(item, new))
continue // next loop iteration
else
move old item to position(item, new)
else
insert new item into old at position(item, new)
The deletions are all done up front to make the positions of the items more predictable in the second loop.
The driving force behind this was to sync a list of data from the server with <table> rows in a browser DOM (using javascript). It was needed because we didn't want to redraw the entire table whenever the data changed; the differences between the lists were likely to be small and only affect one or two rows. It may not be the algorithm you're looking for for your data. If not, let me know and I'll delete this.
There are probably some optimizations that could be made for this. But it is performant and predictable enough for me and the data I'm working with.
Levenshtein distance metric.
http://www.levenshtein.net/