Assuming the expected output is aaa, ccc, eee, fff, xxx (all the not-common items), you can use List#removeAll, but you need to use it twice to get both the items in name but not in name2 AND the items in name2 and not in name:
List<String> list = new ArrayList<> (name);
list.removeAll(name2); //list contains items only in name
List<String> list2 = new ArrayList<> (name2);
list2.removeAll(name); //list2 contains items only in name2
list2.addAll(list); //list2 now contains all the not-common items
As per your edit, you can't use remove or removeAll - in that case you can simply run two loops:
List<String> uncommon = new ArrayList<> ();
for (String s : name) {
if (!name2.contains(s)) uncommon.add(s);
}
for (String s : name2) {
if (!name.contains(s)) uncommon.add(s);
}
Answer from assylias on Stack OverflowAssuming the expected output is aaa, ccc, eee, fff, xxx (all the not-common items), you can use List#removeAll, but you need to use it twice to get both the items in name but not in name2 AND the items in name2 and not in name:
List<String> list = new ArrayList<> (name);
list.removeAll(name2); //list contains items only in name
List<String> list2 = new ArrayList<> (name2);
list2.removeAll(name); //list2 contains items only in name2
list2.addAll(list); //list2 now contains all the not-common items
As per your edit, you can't use remove or removeAll - in that case you can simply run two loops:
List<String> uncommon = new ArrayList<> ();
for (String s : name) {
if (!name2.contains(s)) uncommon.add(s);
}
for (String s : name2) {
if (!name.contains(s)) uncommon.add(s);
}
You can use the Guava libraries to do this. The Sets class has a difference method
You would have to run it twice I think to get all the differences from both sides.
Is this a method? You can't just plop code anywhere in Java.
The name of the operation is symmetric difference, so you should probably call it that.
Here's a more compact implementation.
private Set<T> symmetricDifference(Set<T> a, Set<T> b) {
Set<T> result = new HashSet<T>(a);
for (T element : b) {
// .add() returns false if element already exists
if (!result.add(element)) {
result.remove(element);
}
}
return result;
}
With Apache Commons Collections (javadoc):
CollectionUtils.disjunction(a, b);
With Guava (javadoc):
Sets.symmetricDifference(a, b);
See also: Effective Java, 2nd edition, Item 47: Know and use the libraries (The author mentions only the JDK's built-in libraries but I think the reasoning could be true for other libraries too.)
You could do something like this:
public static void main(String[] args) {
List<Foo> fooList = asList(new Foo("1", "1"), new Foo("2", "2"), new Foo("3", "3"));
List<Bar> barList = asList(new Bar("4", 4), new Bar("3", 3), new Bar("5", 5));
Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));
Set<Blah> barSet = barList.stream().map(Main::barToBlah).collect(toCollection(HashSet::new));
barList.stream()
.filter(bar -> !fooSet.contains(barToBlah(bar)))
.forEach(System.out::println);
fooList.stream()
.filter(foo -> !barSet.contains(fooToBlah(foo)))
.forEach(System.out::println);
}
static Blah fooToBlah(Foo foo) {
return new Blah(foo.name, foo.age);
}
static Blah barToBlah(Bar bar) {
return new Blah(bar.barName, "" + bar.codeBar);
}
static class Blah {
String name;
String age;
public Blah(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
...
}
@Override
public int hashCode() {
...
}
}
- Take all
Fooobjects and convert them toBlah(name it appropriately of course) - this is needed becauseFoohas other fields that we don't care about. - Put
Blahobjects into aHashSetso that you don't end up withO(n*m)time complexity. You could do justcollect(toSet()), but I prefer to be explicit about it here as it is important for performance. - Go over
Barobjects and if an object is not in the above set, that's the uncommonBarobject. It needs to be converted toBlahand unfortunately you cannot useStream.mapas in the end it should still beStream<Bar>, notStream<Blah>. - Repeat above three steps for the other list to find all uncommon
Fooobjects.
Remember that equals and hashCode methods are needed on Blah class. Foo and Bar classes don't need them.
EDIT:
You can convert the following code
Set<Blah> fooSet = fooList.stream().map(Main::fooToBlah).collect(toCollection(HashSet::new));
barList.stream()
.filter(bar -> !fooSet.contains(barToBlah(bar)))
.forEach(System.out::println);
into something like this
barList.stream()
.filter(bar -> fooList.stream()
.map(Main::fooToBlah)
.noneMatch(foo -> foo.equals(barToBlah(bar)))
)
.forEach(System.out::println);
or even remove Blah class completely
barList.stream()
.filter(bar -> fooList.stream().noneMatch(foo -> Objects.equals(foo.name, bar.barName) && Objects.equals(foo.age, "" + bar.codeBar)
.forEach(System.out::println);
but you will end up with much worse time complexity and then there is also a question of readability.
A naive solution is to loop through both lists. Assuming you can formulate your equality condition as BiPredicate<Foo, Bar>:
for (Bar bar: bars) {
boolean foundEqualFoo = false;
for(Foo foo: foos) {
if (predicate.test(foo, bar)) {
foundEqualFoo = true;
break;
}
}
if (!foundEqualFoo) {
barsNotInFoos.add(bar);
}
}
That's O(n*m) time complexity.
A better solution would be to define a key class like FooBarKey which would hold name and age and implement equals(...) and hashCode() correctly. You could then do the following:
Map<FooBarKey, List<Bar>> barsByKey = bars
.stream()
.collect(Collectors.groupingBy(
FooBarKey::ofBar,
HashMap::new,
Collectors.toList()));
Set<FooBarKey> fooKeys = foos
.stream()
.map(FooBarKey::ofFoo)
.collect(Collectors.toCollection(HashSet::new));
barsByKey.keySet().removeAll(fooKeys);
Set<Bar> uncommonBars = barsByKey
.values()
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet());
Few notes on the solution:
- I think creating
Map<FooBarKey, ...>is reasonable here to avoid constantly creating newFooBarKeyinstances for the sameBars. - A multi-map
Map<FooBarKey, List<Bar>>is needed since there may be bars with the same key in the list. HashMap::newandHashSet::newsuppliers are used where applicable to make sure hash tables are used to guaranteeO(1)on corresponding operations.
That should be O(2*n + m).
These feel like set operations to me: union, overlap, and difference.
Have a look at this:
Classical set operations for java.util.Collection
Works perfectly for me. Here's the code:
import java.util.ArrayList;
import java.util.List;
/**
* Add something descriptive here.
* User: mduffy
* Date: 3/26/2015
* Time: 1:27 PM
* @link https://stackoverflow.com/questions/29284061/find-the-uncommon-common-all-elements-from-two-different-array-list-objects-in/29284162?noredirect=1#comment46767251_29284162
*/
public class SetOperationDemo {
public static void main(String[] args) {
List<String> setOne = new ArrayList<String>() {{
add("A");
add("B");
add("C");
add("D");
add("E");
}};
List<String> setTwo = new ArrayList<String>() {{
add("D");
add("E");
add("F");
add("G");
}};
System.out.println("Set A : " + setOne);
System.out.println("Set B : " + setTwo);
List<String> base = new ArrayList<String>(setOne);
base.retainAll(setTwo);
System.out.println("Intersection A+B: " + base);
base = new ArrayList<String>(setOne);
base.removeAll(setTwo);
System.out.println("Subtraction A-B: " + base);
base = new ArrayList<String>(setTwo);
base.removeAll(setOne);
System.out.println("Subtraction B-A: " + base);
base = new ArrayList<String>(setOne);
base.addAll(setTwo);
System.out.println("Union A union B : " + base);
}
}
Here's the output:
Set A : [A, B, C, D, E]
Set B : [D, E, F, G]
Intersection A+B: [D, E]
Subtraction A-B: [A, B, C]
Subtraction B-A: [F, G]
Union A union B : [A, B, C, D, E, D, E, F, G]
Process finished with exit code 0
If your Lists contain custom classes, you have to be sure that they override equals and hashCode properly or they won't give the expected behavior. Here's my code using a custom class that shows how it's done.
import java.util.ArrayList;
import java.util.List;
/**
* Add something descriptive here.
* User: mduffy
* Date: 3/26/2015
* Time: 1:27 PM
* @link https://stackoverflow.com/questions/29284061/find-the-uncommon-common-all-elements-from-two-different-array-list-objects-in/29284162?noredirect=1#comment46767251_29284162
*/
public class SetOperationDemo {
public static void main(String[] args) {
List<DemoPerson> setOne = new ArrayList<DemoPerson>() {{
add(new DemoPerson("Andy", "A"));
add(new DemoPerson("Bob", "B"));
add(new DemoPerson("Carl", "C"));
add(new DemoPerson("David", "D"));
add(new DemoPerson("Ernie", "E"));
}};
List<DemoPerson> setTwo = new ArrayList<DemoPerson>() {{
add(new DemoPerson("David", "D"));
add(new DemoPerson("Ernie", "E"));
add(new DemoPerson("Frank", "F"));
add(new DemoPerson("Gary", "G"));
}};
System.out.println("Set A : " + setOne);
System.out.println("Set B : " + setTwo);
List<DemoPerson> base = new ArrayList<DemoPerson>(setOne);
base.retainAll(setTwo);
System.out.println("Intersection A+B: " + base);
base = new ArrayList<DemoPerson>(setOne);
base.removeAll(setTwo);
System.out.println("Subtraction A-B: " + base);
base = new ArrayList<DemoPerson>(setTwo);
base.removeAll(setOne);
System.out.println("Subtraction B-A: " + base);
base = new ArrayList<DemoPerson>(setOne);
base.addAll(setTwo);
System.out.println("Union A union B : " + base);
}
}
class DemoPerson {
private final String firstName;
private final String lastName;
public DemoPerson(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DemoPerson that = (DemoPerson) o;
return !(firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) && !(lastName != null ? !lastName.equals(that.lastName) : that.lastName != null);
}
@Override
public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("'").append(firstName).append('\'');
sb.append(" '").append(lastName).append('\'');
sb.append('}');
return sb.toString();
}
}
And here's the output, still behaving as it should:
Set A : [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}, {'David' 'D'}, {'Ernie' 'E'}]
Set B : [{'David' 'D'}, {'Ernie' 'E'}, {'Frank' 'F'}, {'Gary' 'G'}]
Intersection A+B: [{'David' 'D'}, {'Ernie' 'E'}]
Subtraction A-B: [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}]
Subtraction B-A: [{'Frank' 'F'}, {'Gary' 'G'}]
Union A union B : [{'Andy' 'A'}, {'Bob' 'B'}, {'Carl' 'C'}, {'David' 'D'}, {'Ernie' 'E'}, {'David' 'D'}, {'Ernie' 'E'}, {'Frank' 'F'}, {'Gary' 'G'}]
Process finished with exit code 0
I have not tested this code but you can try this:
for(CustomStation user1 : localStationLists)
{
boolean flag = false;
for(CustomStation user2 : serverStationLists)
{
if(user1.getStationId().equals(user2.getStationId()))
{
if(!user1.getTitle().equals(user2.getTitle()))
{
resultList.add(user1);
}
resultList.add(user2);
flag = true;
break;
}
}
if(flag == false)
{
resultList1.add(user1);
}
}
If you want to ignore the requirement of using a Set, you could use Java 8 Streams to solve the problem.
public static int numInCommon(List<Integer> list1, List<Integer> list2) {
return (int) list1.stream()
.distinct()
.filter(i -> list2.contains(i))
.count()
}
This is a four-step process:
- Get a stream of
list1(if you are not used to streams you can think of this as a loop, but in reality a call to.stream()by itself is not looping) - Use
.distinct()to ignore duplicate elements (that is, if the first list contains a number multiple times, it only processes it once. The.distinct()part uses aSetinternally to keep track of which elements it has seen. - Use
.filterto only keep the numbers that also occur in the second list. .count()to count the number of seen elements. This returns alongwhich is why the entire result is typecast to aninthere.
It's not until you call .count() that the loop is actually performed, this is because .count() is a "terminal operation".
I would recommend to read more about Streams on Oracle's website
I personally would shorten this greatly:
public static int numInCommon(List<Integer> list1, List<Integer> list2){
if(list1.isEmpty() || list2.isEmpty()){
return 0;
}
Set<Integer> setCommon = new HashSet<>(list1);
setCommon.retainAll(list2);
return setCommon.size();
}
Returning 0 at the start is good, but no need for the else block on the If code. Next task is to calculate the common elements - putting list1 into the set removes duplicates and then retainAll removes anything that isn't in list2. This leaves everythin in list1 and list2 without duplicates. Returning the size() counts the elements.
For performance - I'm taking the major assumption that HashSet is one of the quickest ways to check if an item exists and handle removal.
Use a hash table.
Iterate through the first linked list, entering the values you come across into a hash table.
Iterate through the second linked list, adding any element not found into the hash table into your list of non-common elements.
This solution should be O(n), assuming no collisions in the hash table.
create a new empty list. have a hash table and populate it with elements of both lists. complexity n. then iterate over each list sequentially and while iterating, put those elements in the new list which are not present in the hash table. complexity n. overall complexity=n
You may use a set. Contains for a HashSet is O(1) compared to O(n) for a list, therefore you should never use a list if you often need to run contains.
Set<String> bSet = new HashSet<>(bListb);
aLista.stream().filter(a -> !bSet.contains(a)).collect(Collectors.toList());
If you don't care about the duplicates, then try this:
Set<String> uncommon = new HashSet<>(aLista);
uncommon.removeAll(bListb);
to explain this in logic format(not Java):
UncommonRows = (currentRow union newErrorRow ) - (currentRow intersection newErrorRow)
here is a quick and dirty way of doing it in Java. hope the comments explains what I've done.
Set<Row> uncommonRows=new HashSet<Row>(currentRow);
uncommonRows.addAll(newErrorRow); //at this point uncommonRows contains all the Rows
Set<Row> retained=new HashSet<Row>(currentRow);
retained.retainAll(newErrorRow); //retained contains all rows that are in both sets.
uncommonRows.removeAll(retained) ; // at this point uncommonRows contains the uncommon Rows
You can use Sets retainAll property & the use removeAll !
Set < Row > rows1 = new HashSet(currentRow);
Set < Row > rows2 = new HashSet(newErrorRow);
rows1.retainAll(rows2); // rows1 now contains only elements in both set !
rows2.removeAll(rows1); // rows2 now contains only the unique elements !