Combining ArrayList without duplicates - java

import java.util.ArrayList;
import java.util.Collections;
public class SmartCombining {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
Collections.addAll(list1, 4, 3);
Collections.addAll(list2, 5, 10, 4, 3, 7);
smartCombine(list1, list2);
System.out.println(list1);
System.out.println(list2);
}
public static void smartCombine(ArrayList<Integer> first,
ArrayList<Integer> second) {
first.addAll(second);
}
}
So, I want to combine two lists into one, but if the second list contains a number from the first it won't be added. So far my method adds them all together.

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);

Remove duplicates, then merge both lists:
list1.remove(list2);
list1.addAll(list2);
If you dont want to alter the original list, then first create a backup:
list1BP = new ArrayList(list1);
Another approach is to use HashSet, see other answers.

Use Set, it has been created for that purpose. A Set cannot contain 2 identical elements, based on the equals method.
Set<Integer> list1 = new HashSet<Integer>();
Set<Integer> list2 = new HashSet<Integer>();
Using a combination of ArrayList and contains method is an antipattern here.

There are two easy way you can combine two Lists and duplicate will be removed.
1) First and very easiest way you can get your output, by creating equivalent HashSet object of your ArrayList. Since HashSet does not allow duplicates.
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
Collections.addAll(list1, 4, 3);
Collections.addAll(list2, 5, 10, 4, 3, 7);
System.out.println(smartCombine(list1, list2));
}
public static HashSet<Integer> smartCombine(ArrayList<Integer> first, ArrayList<Integer> second) {
first.addAll(second);
HashSet<Integer> hs = new HashSet<Integer>(first);
return hs;
2) There is another way using advanced for loop. Iterate the second list and check if the current element is not in first list and then add the current element.
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
Collections.addAll(list1, 4, 3);
Collections.addAll(list2, 5, 10, 4, 3, 7);
smartCombine(list1, list2);
System.out.println(list1);
}
public static void smartCombine(ArrayList<Integer> first, ArrayList<Integer> second) {
for (Integer num : second) {
if (!first.contains(num)) {
first.add(num);
}
}
}
Note: The second way will work fine only if first list has no duplicates.

Have you tried ArrayList.addAll()
Look at this java doc
As pointer out this would not handle duplicates which can easily be removed using a Set

use contains(Object) method in ArrayList
public static void smartCombine(ArrayList<Integer> first,
ArrayList<Integer> second) {
for(Integer i :second){
if(!first.contains(i)) { // if first list doesn't contain this item, add item to the first list.
first.add(i);
}
}
}

Related

Putting HashSet inside a HashMap

Is there any way to reuse a hash set by adding it to the HashMap, then later changing the HashSet without it changing the previous HashSet that was placed into the hashMap?
public static void hash() {
HashMap<Integer, HashSet<Integer>> hset = new HashMap<Integer, HashSet<Integer>>(
HashSet<Integer> list = new HashSet<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
hset.put(1,list);
System.out.println(hset.get(1));
// the console prints "[1, 2, 3, 4]"
list.clear();
System.out.println(hset.get(1));
// the console prints "[]"
}
I want to take user inputs for the HashSet, store them in the HashMap, then clear the HashSet to be used again by the user. But I need to use the same HashSet as there will be a loop.
Put a new HashSet consisting of the elements of list.
hset.put(1, new HashSet<>(list));
Demo:
import java.util.HashMap;
import java.util.HashSet;
public class Main {
public static void main(String[] args) {
HashMap<Integer, HashSet<Integer>> hset = new HashMap<>();
HashSet<Integer> list = new HashSet<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
hset.put(1, new HashSet<>(list));
System.out.println(hset.get(1));
// the console prints "[1, 2, 3, 4]"
list.clear();
System.out.println(hset.get(1));
// the console prints "[1, 2, 3, 4]"
}
}
On a side note, you need just <> on the right side i.e. instead of new HashMap<Integer, HashSet<Integer>>, you can simply write new HashMap<>.
Parameter passing is done by reference in Java.
HashSet object in your Hashmap and HashSet object in the list variable are the same!
I would recommend you do not clear the HashMap but rather add a new instance of the HashSet into the HashMap as your loop continues. like below.
public static void hash() {
HashMap<Integer, HashSet<Integer>> hset = new HashMap<Integer, HashSet<Integer>>()
int mapKey = 1;
//loop here , create the instance of HashSet for every loop operation
while(true){
HashSet<Integer> list = new HashSet<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
hset.put(mapKey,list);
System.out.println(hset.get(mapKey));
// the console prints "[1, 2, 3, 4]"
//list.clear();
System.out.println(hset.get(map.getKey));
// the console prints "[]"
mapKey++;
}
}
The MapKey would be different for the different HashSets you will have.

remove duplicate list from an arrayList using Set

I have a List that contains duplicate ArrayList.
I'm looking for a solution to remove them.
Here is an example:
listOne = [[1, 0], [0, 1], [3, 2], [2, 3]]
This set contains duplicate List. Normally i want to get :
theListAfterTransformation = [[1, 0],[3, 2]]
Here is my tiny example, i tried to use the Set but it didn't work well.
public class Example {
public static void main( String[] args ) {
ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
ArrayList<Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(0);
ArrayList<Integer> list2 = new ArrayList<>(); list2.add(0); list2.add(1);
ArrayList<Integer> list3 = new ArrayList<>(); list3.add(3); list3.add(2);
ArrayList<Integer> list4 = new ArrayList<>(); list4.add(2); list4.add(3);
lists.add(list1);lists.add(list2);lists.add(list3);lists.add(list4);
System.out.println(getUnduplicateList(lists));
}
public static ArrayList<ArrayList<Integer>> getUnduplicateList( ArrayList<ArrayList<Integer>> lists) {
Iterator iterator = lists.iterator();
Set<ArrayList<Integer>> set = new HashSet<>();
while (iterator.hasNext()){
ArrayList<Integer> list = (ArrayList<Integer>) iterator.next();
set.add(list);
}
return new ArrayList<>(set);
}
}
Note that is a tiny example from my project and it will be very hard to use a solution that change many thing in this implementation.
So take into account that the getUnduplicateList should keep the same signature. the good idea will be to change only the implementation.
This program print the same list as the input. any idea please.
A couple notes on terminology—Set is a distinct data structure from List, where the former is unordered and does not allow duplicates, while the latter is a basic, linear collection, that's generally ordered, and allows duplicates. You seem to be using the terms interchangeably, which may be part of the issue you're having: Set is probably the appropriate data structure here.
That said, it seems that your code is relying on the List API, so we can follow that along. Note that you should, in general, code to the interface (List), rather than the specific class (ArrayList).
Additionally, consider using the Arrays.asList shorthand method for initializing a list (note that this returns an immutable list).
Finally, note that a HashSet eliminates duplicates by checking if both objects have the same hashCode. Lists containing the same elements are still not considered to be the same list unless the elements appear in the same order, and will typically not be treated as duplicates. Sets, however, implement equals and hashCode in such a way that two sets containing exactly the same elements are considered equal (order doesn't matter).
Using your original starting collection, you can convert each inner-list to a set. Then, eliminate duplicates from the outer collection. Finally, convert the inner-collections back to lists, to maintain compatibility with the rest of your code (if needed). This approach will work regardless of the size of the inner-lists.
You can simulate these steps using a Stream, and using method references to convert to and from the Set, as below.
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.stream.Collectors;
public class Example {
public static void main( String[] args ) {
List<Integer> list1 = Arrays.asList(1, 0);
List<Integer> list2 = Arrays.asList(0, 1);
List<Integer> list3 = Arrays.asList(3, 2);
List<Integer> list4 = Arrays.asList(2, 3);
List<List<Integer>> lists = Arrays.asList(list1, list2, list3, list4);
System.out.println(getUnduplicateList(lists));
}
public static List<List<Integer>> getUnduplicateList(List<List<Integer>> lists) {
return lists
.stream()
.map(HashSet::new)
.distinct()
.map(ArrayList::new)
.collect(Collectors.toList());
}
}
You need to convert the inner lists to sets as well.
Another solution is to sort your lists and then run them through distinct Although this is not very efficient and you will also obtain a set of sorted lists:
Set<List<Integer>> collect = set.stream()
.map(list -> {
list.sort(Comparator.comparingInt(Integer::intValue));
return list;
})
.distinct()
.collect(Collectors.toSet());

Finding multiple min/max using Collections.min/max

In an example I was working on, I was trying to find the smallest three elements in a list (without sorting the list) and then add those three elements into a new list.
Because it was an example I could just simple use a for loop, use Collections.min(list) add that element to the new list, then remove that element from the original list. If I had not removed the element, I would get the same element three times. However, by removing the element, I got my desired outcome.
How can I do this without removing the max/min elements from the list?
If you want to find the max/min 3 elements, I would suggest you to use a PriorityQueue:
PriorityQueue<Integer> pq = new PriorityQueue<>(k);
And then in a loop, add elements to this queue.
And then you can add these 3 elements to the list by removing from the queue and simply return from the method.
Even better would be to use 3 seperate variables and directly loop on the main list. Note: this method will not be feasible if you later on update 3 to some other value. Whereas PriorityQueue approach will be flexible.
public static void main (String[] args) throws java.lang.Exception {
Integer arr[] = {2, 6, 5, 3, 7, 9, 12, 35, 1, 3};
List<Integer> incoming = Arrays.asList(arr);
Comparator<Integer> maxFirstComparator = (x, y) -> Integer.compare(y,x);
printList(getMinOrMaxKNumbers(incoming, 3, null));
System.out.println();
printList(getMinOrMaxKNumbers(incoming, 3, maxFirstComparator));
}
/*
* gets the max/min K elements from the List
* #param comparator if null is passed the method uses natural ordering
*
*/
private static List<Integer> getMinOrMaxKNumbers(List<Integer> incoming, int k, Comparator<Integer> comparator) {
int n = incoming.size();
PriorityQueue<Integer> pq = comparator == null ? new PriorityQueue<>(n) : new PriorityQueue<>(n, comparator);
for (int i : incoming) {
pq.add(i);
}
List<Integer> outgoing = new ArrayList<>(k);
for (int i = 0; i < k; i++) {
outgoing.add(pq.poll());
}
return outgoing;
}
private static void printList(List<Integer> list) {
list.stream().forEach(x -> System.out.print(x + " "));
}
I don't think there is any straight forward way to do this using the standard library.
The following code contains a utility method to get the set of a number of maximal elements.
It works by keeping the maximal elements in a TreeSet, to which an element is inserted only if it belongs to the maximal elements. A tree set is a good fit here because it is fast to find the minimal element, and to test if an element is contained in the set.
In this way you get good performance, and can be flexible with the number of maximal elements you want.
public class MultipleMaxElements {
public static void main(String[] args) {
List<Integer> l = List.of(4, 2, 5, 8, 2, 8, 0, 1);
System.out.println(maxElements(l, 3, Comparator.naturalOrder()));
System.out.println(maxElements(l, 3, Comparator.<Integer>naturalOrder().reversed()));
}
public static <T> Set<T> maxElements(Iterable<T> list, int nrElems, Comparator<T> cmp) {
TreeSet<T> maxSet = new TreeSet<>(cmp);
for (T elem : list) {
if (maxSet.size() < nrElems) {
maxSet.add(elem);
} else if (!maxSet.contains(elem) && cmp.compare(elem, maxSet.first()) > 0) {
maxSet.pollFirst();
maxSet.add(elem);
}
}
return maxSet;
}
}
In the question text you don't write anything about how duplicate elements in the input list should be handled. This method returns maximal unique elements.
If duplicates should be preserved then a tree based Guava multi-set could be used instead of a normal TreeSet.

ArrayList method "lastIndexOf" returning -1 when item does exist in ArrayList

I'm trying to get the basics of ArrayLists, but I can't get the lastIndexOf method to work properly. As you can see in my code below, the program runs and should print "1", the index of the number 3, but prints "-1" instead (which should be printed only if 3 didn't exist in the ArrayList). What's my problem?
import java.util.ArrayList;
public class Pile {
public static void main(String[] args)
{
int[] myArray = {1,3,23,4};
ArrayList<Integer> myList = new ArrayList<Integer>();
for (int i=0;i<myArray.length;i++) {
myList.add(myArray[0]);
}
System.out.println(myList.lastIndexOf(3));
}
}
You're only adding the first element of myArray to myList
You should replace
myList.add(myArray[0]);
with this
myList.add(myArray[i]);
Also, instead of manually copying the elements, you could use Arrays#asList (but you would need to change the type of myArray to Integer[]):
List<Integer> myList = Arrays.asList(myArray);
You are only adding the first element of the array multiple times to the list.
myList.add(myArray[0]);
That's why your list doesn't contain 3.
Change it to
myList.add(myArray[i]);

removing elements from an array list

consider I have an array list like : [2,5,1,8,6]
and I want to remove all elements from 1 till the end .and the arraylist will be like :[2,5]
how can i do this?
thanks
It is worth noting that you cannot add/remove from an Arrays.asList(), but you can do.
List<Integer> list = Arrays.asList(2, 5, 1, 8, 6);
int idx = list.indexOf(1);
if (idx>=0) list = list.subList(0, idx);
List<Integer> list = Arrays.asList(2, 5, 1, 8, 6);
boolean remove = false;
Iterator<Integer> it = list.iterator();
while (it.hasNext() {
if (!remove && it.next() == 1) {
remove = true;
}
if (remove) {
it.remove();
}
}
list = list.subList(0, 1);
The most efficient way to remove elements from ArrayList is to remove them from the end of the list. Each element you remove from the middle of the list will result in all the latter elements being moved to the left. If the list is large, this can result in a significant performance issue.
However, in your case, you might be better off just creating a new sublist with the remaining two elements.
Another way of doing it would be to simply pop items off the end of the List until we have popped the starting element. Each item can be popped in O(1) time, so the entire operation is O(n).
class Main{
private static List<Integer> inputs = new ArrayList<Integer>();
public static void main(String args[]){
for (int x: new int[]{2,5,1,8,6})
inputs.add(x);
System.out.println(inputs);
int start=inputs.indexOf(1);
if (start>=0){ //check if there is a 1 in input
while (inputs.size()>start)
inputs.remove(inputs.size()-1);
}
System.out.println(inputs);
}
}

Categories

Resources