Either:
Set<Foo> fooSet = new LinkedHashSet<>(one);
fooSet.addAll(two);
List<Foo> finalFoo = new ArrayList<>(fooSet);
or
List<Foo> twoCopy = new ArrayList<>(two);
twoCopy.removeAll(one);
one.addAll(twoCopy);
Answer from Puce on Stack OverflowEither:
Set<Foo> fooSet = new LinkedHashSet<>(one);
fooSet.addAll(two);
List<Foo> finalFoo = new ArrayList<>(fooSet);
or
List<Foo> twoCopy = new ArrayList<>(two);
twoCopy.removeAll(one);
one.addAll(twoCopy);
for (Object x : two){
if (!one.contains(x))
one.add(x);
}
assuming you don't want to use the set suggested in the comment. If you are looking for something fancier than this please clarify your question.
A lot of code but here is a sample..
try{
int[] a = {1,2,3,4,5,6,7};
int[] b = {5,6,7,8,9,10};
int[] c = new int[a.length+b.length];
int[] fin = new int[a.length+b.length];
int i = 0;
for(int j : fin){
fin[i++] = -1;
}
i = 0;
for(int j : a){
c[i++] = j;
}
for(int j : b){
c[i++] = j;
}
boolean check = false;
for(int j = 0,k = 0; j < c.length; j++){
for(int l : fin){
if( l == c[j] )
check = true;
}
if(!check){
fin[k++] = c[j];
} else check = false;
}
} catch(Exception ex){
ex.printStackTrace();
}
Depends on how you want to merge them, since an array has an ordering of its elements.
You can just add one after the other (easiest) or "zip" them (one element from A, then one from B, then another from A, etc).
The main problem is that you have to do a check in your new array whether it already contains a value you're trying to add. This is easiest with built-in functions and implementations, like Set. There are Set implementations, like HashSet, which give you constant-time performance for looking up values and inserting them.
I really advise you to just do it the "usual" way and not try and reinvent the wheel.
I find this quite readable:
static int[] merge(int[] a, int[] b) {
Set<Integer> set = new HashSet<>(Arrays.asList(a));
set.addAll(Arrays.asList(b)); // skips duplicate as per Set implementation
return set.toArray(new int[0]);
}
This uses both duplication and built-in methods, but it's clear and concise.
I took a first try to shorten the iterator implementation while trying to keep it readeable. The difficulty I think comes in part in that with an iterator we actually consume elements, so you can not arbitrarily index elements. I came up with something like this:
public static List<Integer> mergeWithIterator(List<Integer> list1, List<Integer> list2) {
List<Integer> sorted = new ArrayList<>();
Iterator<Integer> iterator1 = list1.iterator();
Iterator<Integer> iterator2 = list2.iterator();
Integer element1 = null;
Integer element2 = null;
while (iterator1.hasNext() && iterator2.hasNext()) {
if (element1 == null) {
element1 = iterator1.next();
}
if (element2 == null) {
element2 = iterator2.next();
}
if (element1 < element2) {
sorted.add(element1);
element1 = null;
} else if (element1 > element2) {
sorted.add(element2);
element2 = null;
} else {
sorted.add(element1);
element1 = null;
element2 = null;
}
}
if (element1 != null) {
sorted.add(element1);
}
if (element2 != null) {
sorted.add(element2);
}
while (iterator1.hasNext()) {
sorted.add(iterator1.next());
}
while (iterator2.hasNext()) {
sorted.add(iterator2.next());
}
return sorted;
}
This of course assumes null is an invalid element of your list (it would currently blow up anyway). You could even make some inline checks in order to remove the add remaining things after the loop, but I'd wager it will hinder the readeability. EDIT: tried it nonetheless:
public static List<Integer> mergeWithIterator(List<Integer> list1, List<Integer> list2) {
List<Integer> sorted = new ArrayList<>();
Iterator<Integer> iterator1 = list1.iterator();
Iterator<Integer> iterator2 = list2.iterator();
Integer element1 = iterator1.hasNext() ? iterator1.next() : null;
Integer element2 = iterator2.hasNext() ? iterator2.next() : null;
while (!(element1 == null && element2 == null)) {
if (element2 == null || (element1 != null && element1 < element2)) {
sorted.add(element1);
element1 = iterator1.hasNext() ? iterator1.next() : null;
} else if (element1 == null || (element2 != null && element2 < element1)) {
sorted.add(element2);
element2 = iterator2.hasNext() ? iterator2.next() : null;
} else {
sorted.add(element1);
element1 = iterator1.hasNext() ? iterator1.next() : null;
element2 = iterator2.hasNext() ? iterator2.next() : null;
}
}
return sorted;
}
A simple (but possibly naive) streams implementation could be:
public static List<Integer> mergeWithStreams(List<Integer> list1, List<Integer> list2) {
return Stream.concat(list1.stream(), list2.stream())
.sorted()
.distinct()
.collect(toList());
}
As a sidenote, you you could make the methods more generic by defining the types of the elements as implementing the Comparable interface. This of course could invalidate the iterator implementation using null.
About performance, I think you should measure. For sufficiently large lists the iterator implementation might indeed get that extra bit of speed, as it's complexity is indeed linear w.r.t. the inputs.
For streaming and parallel, I do not think I have enough knowledge to answer that. My guess is that it would depend a lot on the format of the input, but in general would not be an improvement performance wise. Measure, measure, measure! It definitely is an improvement readeability wise though.
Maybe an other nice way would be to return an implementation of a list using given lists (or copies of them) as the backing collections. I'd imagine the implementation would be quite simple (depending on requirements).
Here's the neatest solution poor Java iterators allowed me:
import java.util.*;
import java.util.function.BiFunction;
public class SortedListMerger<T> {
public static void main(String[] args) {
Integer[] l1 = new Integer[] {1, 3, 5};
Integer[] l2 = new Integer[] {1, 3, 4};
SortedListMerger<Integer> m = new SortedListMerger<>((a, b)-> a * b, Comparator.comparingInt(a -> a));
ArrayList<Integer> d = new ArrayList<>();
m.merge(Arrays.asList(l1), Arrays.asList(l2), d);
for (int i : d)
System.out.println(i);
}
private BiFunction<T, T, T> ifEqual;
private Comparator<T> cmp;
public SortedListMerger(BiFunction<T, T, T> ifEqual, Comparator<T> cmp) {
this.ifEqual = ifEqual;
this.cmp = cmp;
}
public void merge(Iterable<T> src1, Iterable<T> src2, List<T> dest) {
Iterator<T> src1i = src1.iterator();
Iterator<T> src2i = src2.iterator();
T elem1 = src1i.hasNext() ? src1i.next() : null;
T elem2 = src2i.hasNext() ? src2i.next() : null;
T prev = null;
T curr;
while (elem1 != null || elem2 != null) {
if (elem1 != null && elem2 != null) {
curr = cmp.compare(elem1, elem2) <= 0 ? elem1 : elem2;
if (curr == elem1) {
elem1 = src1i.hasNext() ? src1i.next() : null;
} else {
elem2 = src2i.hasNext() ? src2i.next() : null;
}
} else if (elem1 != null) {
curr = elem1;
elem1 = src1i.hasNext() ? src1i.next() : null;
} else {
curr = elem2;
elem2 = src2i.hasNext() ? src2i.next() : null;
}
if (prev != null && cmp.compare(prev, curr) == 0) {
prev = ifEqual.apply(prev, curr);
} else {
dest.add(curr);
prev = curr;
}
}
}
}
the main method, merge, is only 30 lines
You can also use java 8 to merge list without duplicate.
public class Test {
public static void main(String[] args) {
List<Student> list1 = Arrays.asList(new Student("a", 1), new Student("b", 2), new Student("f", 3));
List<Student> list2 = Arrays.asList(new Student("b", 4), new Student("c", 5), new Student("f", 6));
List<Student> list3 = Arrays.asList(new Student("b", 7), new Student("d", 8), new Student("e", 9));
List<Student> dogs = new ArrayList<>(Stream.of(list1, list2, list3).flatMap(List::stream)
.collect(Collectors.toMap(Student::getName, d -> d, (Student x, Student y) -> x == null ? y : x)).values());
dogs.forEach(System.out::println);
}
}
class Student {
String name;
int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", id=" + id + '}';
}
}
A Set is a list that it can help you to avoid duplicate.
You can simply override the equals and hashCode methods of your Java Bean/POJO object and use set. This way a duplicate entry will be rejected and you don't need to merge your data anymore. Your list elements are always unique.
Take a look at this thread to see how to implement Set in java -> Verify there is a combination of unique string
You can do it using this one-liner
Set<String> combined = Stream.concat(newStringSet.stream(), oldStringSet.stream())
.collect(Collectors.toSet());
With a static import it looks even nicer
Set<String> combined = concat(newStringSet.stream(), oldStringSet.stream())
.collect(toSet());
Another way is to use flatMap method:
Set<String> combined = Stream.of(newStringSet, oldStringSet).flatMap(Set::stream)
.collect(toSet());
Also any collection could easily be combined with a single element
Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
.collect(toSet());
Since a Set does not contain duplicate entries, you can therefore combine the two by:
newStringSet.addAll(oldStringSet);
It does not matter if you add things twice, the set will only contain the element once... e.g it's no need to check using contains method.
Well, one way to do it is to iterate through the second list while checking if each element exists in the first list. If it doesn't, add it.
public static void smartCombine(ArrayList<Integer> first, ArrayList<Integer> second) {
for(Integer num : second) { // iterate through the second list
if(!first.contains(num)) { // if first list doesn't contain current element
first.add(num); // add it to the first list
}
}
}
Another way would be for you to hold your values inside a set (like HashSet) which doesn't allow any duplicates. Then you can combine them like:
first.addAll(second);
One more way you could do it is to first remove all elements from the first list that exist in the second list (the ones that would be duplicated). Then you add all elements of the second list to the first list.
public static void smartCombine(ArrayList<Integer> first, ArrayList<Integer> second) {
first.removeAll(second); // remove elements that would be duplicated
first.addAll(second); // add elements from second list
}
The simple, no brains solution:
Set<Integer> joinedSet = new HashSet<Integer>();
joinedSet.addAll(list1);
joinedSet.addAll(list2);