Related
It has been a long since something came to my mind while starting to code and using lists or array lists. When comparing values of one array to every other elements in another array, I used to do it in two for loops since it was the easiest way to do that.but recently I came to know that it increases much time complexity, I thought about another solution.can anyone help me in solving this case using any algorithm. I am using java.but solution in any language would be fine. just the algorithm to do that is needed. Thanks in advance.
This is what i am doing:
a1 = [1,2,3,4,5]
b1 = [9,5,4,3,8,3,7]
I want to check how much time an element in a1 occurs in b1
So what i am doing is:
count = 0;
for(int i = 0;i <a1.length;i++)
{
for(j=0;j<b1.length;j++)
{
if (a1[i] == b1[j])
{
count = count+1;
}
}
}
print("count is" count);
Theres no need of loop to obtain what you want
ArrayList<Integer> l1 = new ArrayList<Integer>();
l1.add(1);
l1.add(2);
l1.add(3);
l1.add(4);
l1.add(5);
ArrayList<Integer> l2 = new ArrayList<Integer>();
l2.add(9);
l2.add(5);
l2.add(4);
l2.add(3);
l2.add(8);
l2.add(3);
l2.add(7);
ArrayList<Integer> lFiltered = new ArrayList<Integer>(l2);
lFiltered.removeAll(l1);
int Times = l2.size() - lFiltered.size();
System.out.println("number of migrants : " + Times);
Suffice it to to generate from l2 a list without elements and l1 and to count elements which have been removed
Use hashing, e.g. using a Set or Map
If you want to compare the objects as a whole:
properly implement equals and hashcode for your class (if not implemented already)
put all the elements of list A into a Set, then see which elements from list B are in that Set
If you just want to compare objects by some attribute:
define a method that maps the objects to that attribute (or combination of attriutes, e.g. as a List)
create a Map<KeyAttributeType, List<YourClass>> and for each element from list A, add the element to that Map: map.get(getKey(x)).add(x)
for each element from list B, calculate the value of the key function and get the elements it "matches" from the map: matches = map.get(getKey(y))
Given your code, your case seems to be a bit different, though. You have lists or arrays of numbers, so no additional hashing is necessary, and you do not just want to see which items "match", but count all combinations of matching items. For this, you could create a Map<Integer, Long> to count how often each element of the first list appears, and then get the sum of those counts for the elements from the second list.
int[] a1 = {1,2,3,4,5};
int[] b1 = {9,5,4,3,8,3,7};
Map<Integer, Long> counts = IntStream.of(b1).boxed()
.collect(Collectors.groupingBy(x -> x, Collectors.counting()));
System.out.println(counts); // {3=2, 4=1, 5=1, 7=1, 8=1, 9=1}
long total = IntStream.of(a1).mapToLong(x -> counts.getOrDefault(x, 0L)).sum();
System.out.println(total); // 4
Of course, instead of using the Stream API you can just as well use regular loops.
Use ArrayLists.
To compare the content of both arrays:
ArrayList<String> listOne = new ArrayList<>(Arrays.asList(yourArray1);
ArrayList<String> listTwo = new ArrayList<>(Arrays.asList(yourArray);
listOne.retainAll(listTwo);
System.out.println(listOne)
To find missing elements:
listTwo.removeAll(listOne);
System.out.println(listTwo);
To enumerate the Common elements:
//Time complexity is O(n^2)
int count =0;
for (String element : listOne){
for (String element2: listTwo){
if (element.equalsIgnoreCase(elemnt2){
count += 1;
}
}
}
I've two ArrayList both containing Integer values. My objective is to get identical/common/duplicate values comparing these 2 list. In other words (in SQL parlance), I need the INTERSECT result of two lists, that is, values that appear in both list.
Example:
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(100);
list1.add(200);
list1.add(300);
list1.add(400);
list1.add(500);
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(300);
list2.add(600);
One kind of implementation/solution I could think along is looping one of the list something like:
ArrayList<Integer> intersectList = new ArrayList<Integer>();
for (Integer intValue : list1)
{
if(list2.contains(intValue))
intersectList.add(intValue);
}
In this case, intersectList would contain only 1 Integer item being added, that is 300, which appears in both list.
My question is, are there any better/fastest/efficient way of implementing this logic? Any options available in Apache Commons library?. Any other ideas/suggestions/comments are appreciated.
NOTE: For illustration purpose, I've just shown here 5 items and 2 items being added into the list. In my real-time implementation, there will be more than 1000 elements in each list. Therefore, performance is also a key factor to be considered.
If you're okay with overwriting result for list1:
list1.retainAll(list2);
otherwise clone/copy list1 first.
Not sure on performance though.
list1.retainAll(list2)//for intersection
Use ListUtils from org.apache.commons.collections if you do not want to modify existing list.
ListUtils.intersection(list1, list2)
I have two arrayLists and I am trying to "subtract" one arrayList from another. For example, if I have one arrayList [1,2,3] and I am trying to subtract [0, 2, 4] the resulting arrayList should be [1,3].
List<Integer> a = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> b = Arrays.asList(0, 2, 4);
subtract(a,b) // should return [1,3]
Here is my code.
//returns a new IntSet after subtracting a from b
// .minus().toString()
ArrayList<Integer> minusArray = new ArrayList<Integer>();
minusArray.addAll(array1);
for(int i =0; i< minusArray.size(); i++){
for(int j = 0; j < array2.size(); j++){
if(minusArray.get(i).equals(array2.get(j))){
minusArray.remove(i);
if(i == 0){
;
}
else if(j == 0){
;
}
else{
i = 0;
j = 0;
}
}
else{}
}
}
return minusArray;
My code works in some cases, like if arrayList1 = [4,6] and arrayList2 = [6] it will will give me a result of [4]. But if I try something like [1,2,4] and [0,4,8]
I get this exception:
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at IntSet.minus(IntSet.java:119)
at IntSetDriver.main(IntSetDriver.java:62)
Here is the code I have come up with. I have done test runs through it and to me I think it should work. The user inputs these arrayLists and they are presorted, I also do not know Hash or big-O.
ArrayList<Integer> minusArray = new ArrayList<Integer>();
minusArray.addAll(array1);
for(int i =0; i< minusArray.size(); i++){
for(int j = 0; j < array2.size(); j++){
if(minusArray.get(i).equals(array2.get(j))){
minusArray.remove(i);
}
else{}
}
}
return minusArray;
Is there some reason you can't simply use List.removeAll(List)?
List<Integer> one = new ArrayList<Integer>();
one.add(1);
one.add(2);
one.add(3);
List<Integer> two = new ArrayList<Integer>();
two.add(0);
two.add(2);
two.add(4);
one.removeAll(two);
System.out.println(one);
result: "[1, 3]"
Try to use subtract method of org.apache.commons.collections.CollectionUtils class.
Returns a new Collection containing a - b. The cardinality of each element e in the returned Collection will be the cardinality of e in a minus the cardinality of e in b, or zero, whichever is greater.
CollectionUtils.subtract(java.util.Collection a, java.util.Collection b)
From Apache Commons Collections
Java 8
You can also use streams:
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(1, 2, 4, 5);
List<Integer> diff = list1.stream()
.filter(e -> !list2.contains(e))
.collect (Collectors.toList()); // (3)
This answer does not manipulate the original list. If intention is to modify the original list then we can use remove. Also we can use forEach (default method in Iterator) or stream with filter.
Using ListUtils
Another option is to use ListUtils if we are using Apache common:
ListUtils.subtract(list, list2)
This subtracts all elements in the second list from the first list, placing the results in a new list. This differs from List.removeAll(Collection) in that cardinality is respected; if list1 contains two occurrences of null and list2 only contains one occurrence, then the returned list will still contain one occurrence.
Traversing the minusArray using an index is one way to do this, but I suggest you make use of the contains(Object) method, which will allow you then to use remove(Object) for the particular element of array2.
Of course, there's always the removeAll(Collection) which does pretty much everything you need...
You can use org.apache.commons.collections.ListUtils and make all that you want in only one line =)
List resultList = ListUtils.subtract(list, list2);
Your problem is that in your minusArray.remove(...) call you may shrink the size of the minusArray. To fix this, start at array.size() - 1 and count backwards to 0
Check that - even that won't fix it. You need to reverse the order of your loops
I'm guessing you get the range problem because you've eliminated one of the elements which changes what the inner loop is looking for (I know this problem occurs when dealing with normal Lists and Collections).
What I've had to do in the past to work around this, is to create a list of items that need to be removed (that is ones that are found in the original list). Iterate through that new list and directly eliminate the original list's elements without having to have an iterator moving through it.
Try this answer if removeAll() is not what you want. e.g. if you are interested in something like Calculating difference of two lists with duplicates
subtract(a,b)
b.forEach((i)->a.remove(i));
a now contains
[1, 3]
This follows the suggestion of Guava implementors on how to implement subtract
"create an ArrayList containing a and then call remove on it for each element in b."
Which behaves like this implementation used in Apache commons
Difference to removeAll()
[1,2,2,3].removeAll([1,2,3]) //is empty
[1,2,3].forEach((i)->[1,2,2,3].remove(i)); //a is [2]
My parameterized solution would be like:
<T> ArrayList<T> subtract(ArrayList<T> alpha, ArrayList<T> beta) {
ArrayList<T> gamma = new ArrayList<T>();
alpha.forEach(n -> {if (!beta.contains(n)) gamma.add(n); });
return gamma;
}
I am trying to "combine" two arrayLists, producing a new arrayList that contains all the numbers in the two combined arrayLists, but without any duplicate elements and they should be in order. I came up with this code below. I run through it and it makes sense to me, but Im not sure if I can be using < or > to compare get(i)'s in arrayLists. I am adding all the elements in array1 into the plusArray. Then I am going through the plusArray and comparing it to array2 to see if any of array2's elements exist inside plusArray. If they do I am doing nothing, but if they dont then I am trying to add it in its correct position. Perhaps my nested for loops being used incorrectly? Note: The ArrayLists are presorted by the user in increasing order.
ArrayList<Integer> plusArray = new ArrayList<Integer>();
for(int i = 0; i < array1.size(); i++){
plusArray.add(array1.get(i));
}
for(int i = 0; i < plusArray.size(); i++){
for(int j = 0; j < array2.size(); j++){
if(array2.get(j) < plusArray.get(i)){
plusArray.add(i,array2.get(j));
}
else if(plusArray.get(i).equals(array2.get(j))){
;
}
else if(array2.get(j) > plusArray.get(i)){
plusArray.add(i, array2.get(j));
}
}
UPDATE: I dont get the exception below anymore. Instead it seems the program runs forever. I changed the location of where to add the elements in the < and > conditions.
///
Here is the exception that I get when my array lists are:
IntSet 1: { 1 2 }
IntSet 2: { 1 3 4 }
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Arrays.copyOf(Unknown Source)
at java.util.ArrayList.grow(Unknown Source)
at java.util.ArrayList.ensureCapacityInternal(Unknown Source)
at java.util.ArrayList.add(Unknown Source)
at IntSet.plus(IntSet.java:92)
at IntSetDriver.main(IntSetDriver.java:61)
Firstly remove duplicates:
arrayList1.removeAll(arrayList2);
Then merge two arrayList:
arrayList1.addAll(arrayList2);
Lastly, sort your arrayList if you wish:
collections.sort(arrayList1);
In case you don't want to make any changes on the existing list, first create their backup lists:
arrayList1Backup = new ArrayList(arrayList1);
Instead of the code you wrote, you may use ArrayList.addAll() to merge the lists, Collections.sort() to sort it and finally traverse of the resulting ArrayList to remove duplicates. The aggregate complexity is thus O(n)+O(n*log(n))+O(n) which is equivalent to O(n*log(n)).
List<String> listA = new ArrayList<String>();
listA.add("A");
listA.add("B");
List<String> listB = new ArrayList<String>();
listB.add("B");
listB.add("C");
Set<String> newSet = new HashSet<String>(listA);
newSet.addAll(listB);
List<String> newList = new ArrayList<String>(newSet);
System.out.println("New List :"+newList);
is giving you
New List :[A, B, C]
Add ArrayList1, ArrayList2 and produce a Single arraylist ArrayList3.
Now convert it into
Set Unique_set = new HashSet(Arraylist3);
in the unique set you will get the unique elements.
Note
ArrayList allows to duplicate values.
Set doesn't allow the values to duplicate.
Hope your problem solves.
Java 8 Stream API can be used for the purpose,
ArrayList<String> list1 = new ArrayList<>();
list1.add("A");
list1.add("B");
list1.add("A");
list1.add("D");
list1.add("G");
ArrayList<String> list2 = new ArrayList<>();
list2.add("B");
list2.add("D");
list2.add("E");
list2.add("G");
List<String> noDup = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
noDup.forEach(System.out::println);
En passant, it shouldn't be forgetten that distinct() makes use of hashCode().
Here is one solution using java 8:
Stream.of(list1, list2)
.flatMap(Collection::stream)
.distinct()
// .sorted() uncomment if you want sorted list
.collect(Collectors.toList());
Perhaps my nested for loops being used incorrectly?
Hint: nested loops won't work for this problem. A simple for loop won't work either.
You need to visualize the problem.
Write two ordered lists on a piece of paper, and using two fingers to point the elements of the respective lists, step through them as you do the merge in your head. Then translate your mental decision process into an algorithm and then code.
The optimal solution makes a single pass through the two lists.
Add elements in first arraylist
ArrayList<String> firstArrayList = new ArrayList<String>();
firstArrayList.add("A");
firstArrayList.add("B");
firstArrayList.add("C");
firstArrayList.add("D");
firstArrayList.add("E");
Add elements in second arraylist
ArrayList<String> secondArrayList = new ArrayList<String>();
secondArrayList.add("B");
secondArrayList.add("D");
secondArrayList.add("F");
secondArrayList.add("G");
Add first arraylist's elements in second arraylist
secondArrayList.addAll(firstArrayList);
Assign new combine arraylist and add all elements from both arraylists
ArrayList<String> comboArrayList = new ArrayList<String>(firstArrayList);
comboArrayList.addAll(secondArrayList);
Assign new Set for remove duplicate entries from arraylist
Set<String> setList = new LinkedHashSet<String>(comboArrayList);
comboArrayList.clear();
comboArrayList.addAll(setList);
Sorting arraylist
Collections.sort(comboArrayList);
Output
A
B
C
D
E
F
G
Your second for loop should have j++ instead of i++
I'm not sure why your current code is failing (what is the Exception you get?), but I would like to point out this approach performs O(N-squared). Consider pre-sorting your input arrays (if they are not defined to be pre-sorted) and merging the sorted arrays:
http://www.algolist.net/Algorithms/Merge/Sorted_arrays
Sorting is generally O(N logN) and the merge is O(m+n).
your nested for loop
for(int j = 0; j < array2.size(); i++){
is infinite as j will always equal to zero, on the other hand, i will be increased at will in this loop. You get OutOfBoundaryException when i is larger than plusArray.size()
You don't have to handcode this. The problem definition is precisely the behavior of Apache Commons CollectionUtils#collate. It's also overloaded for different sort orders and allowing duplicates.
**Add elements in Final arraylist,**
**This will Help you sure**
import java.util.ArrayList;
import java.util.List;
public class NonDuplicateList {
public static void main(String[] args) {
List<String> l1 = new ArrayList<String>();
l1.add("1");l1.add("2");l1.add("3");l1.add("4");l1.add("5");l1.add("6");
List<String> l2 = new ArrayList<String>();
l2.add("1");l2.add("7");l2.add("8");l2.add("9");l2.add("10");l2.add("3");
List<String> l3 = new ArrayList<String>();
l3.addAll(l1);
l3.addAll(l2);
for (int i = 0; i < l3.size(); i++) {
for (int j=i+1; j < l3.size(); j++) {
if(l3.get(i) == l3.get(j)) {
l3.remove(j);
}
}
}
System.out.println(l3);
}
}
Output : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I got your point that you don't wanna use the built-in functions for merging or remove duplicates from the ArrayList.
Your first code is running forever because the outer for loop condition is 'Always True'.
Since you are adding elements to plusArray, so the size of the plusArray is increasing with every addition and hence 'i' is always less than it. As a result the condition never fails and the program runs forever.
Tip: Try to first merge the list and then from the merged list remove the duplicate elements. :)
I have a bunch of indexes and I want to remove elements at these indexes from an ArrayList. I can't do a simple sequence of remove()s because the elements are shifted after each removal. How do I solve this?
To remove elements at indexes:
Collections.sort(indexes, Collections.reverseOrder());
for (int i : indexes)
strs.remove(i);
Or, using the Stream API from Java 8:
indexes.sort(Comparator.reverseOrder());
indexes.stream().mapToInt(i -> i).forEach(l::remove);
Sort the indices in descending order and then remove them one by one. If you do that, there's no way a remove will affect any indices that you later want to remove.
How you sort them will depend on the collection you are using to store the indices. If it's a list, you can do this:
List<Integer> indices;
Collections.sort(indices, new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
//todo: handle null
return b.compareTo(a);
}
}
Edit
#aioobe found the helper that I failed to find. Instead of the above, you can use
Collections.sort(indices, Collections.reverseOrder());
I came here for removing elements in specific range (i.e., all elements between 2 indexes), and found this:
list.subList(indexStart, indexEnd).clear()
You can remove the elements starting from the largest index downwards, or if you have references to the objects you wish to remove, you can use the removeAll method.
you might want to use the subList method with the range of index you would like to remove and
then call clear() on it.
(pay attention that the second parameter is exclusive - for example in this case, I pass 2 meaning only index 0 and 1 will be removed.):
public static void main(String[] args) {
ArrayList<String> animals = new ArrayList<String>();
animals.add("cow");
animals.add("dog");
animals.add("chicken");
animals.add("cat");
animals.subList(0, 2).clear();
for(String s : animals)
System.out.println(s);
}
}
the result will be:
chicken
cat
You can remove the indexes in reverse order. If the indexes are in order like 1,2,3 you can do removeRange(1, 3).
I think nanda was the correct answer.
List<T> toRemove = new LinkedList<T>();
for (T t : masterList) {
if (t.shouldRemove()) {
toRemove.add(t);
}
}
masterList.removeAll(toRemove);
You can sort the indices as many said, or you can use an iterator and call remove()
List<String> list = new ArrayList<String>();
list.add("0");
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
List<Integer> indexes = new ArrayList<Integer>();
indexes.add(2);
indexes.add(5);
indexes.add(3);
int cpt = 0;
Iterator<String> it = list.iterator();
while(it.hasNext()){
it.next();
if(indexes.contains(cpt)){
it.remove();
}
cpt++;
}
it depends what you need, but the sort will be faster in most cases
Use guava! The method you are looking is Iterators.removeAll(Iterator removeFrom, Collection elementsToRemove)
If you have really many elements to remove (and a long list), it may be faster to iterate over the list and add all elements who are not to be removed to a new list, since each remove()-step in a array-list copies all elements after the removed one by one. In this case, if you index list is not already sorted (and you can iterate over it parallel to the main list), you may want to use a HashSet or BitSet or some similar O(1)-access-structure for the contains() check:
/**
* creates a new List containing all elements of {#code original},
* apart from those with an index in {#code indices}.
* Neither the original list nor the indices collection is changed.
* #return a new list containing only the remaining elements.
*/
public <X> List<X> removeElements(List<X> original, Collection<Integer> indices) {
// wrap for faster access.
indices = new HashSet<Integer>(indices);
List<X> output = new ArrayList<X>();
int len = original.size();
for(int i = 0; i < len; i++) {
if(!indices.contains(i)) {
output.add(original.get(i));
}
}
return output;
}
order your list of indexes, like this
if 2,12,9,7,3 order desc to 12,9,7,3,2
and then do this
for(var i = 0; i < indexes.length; i++)
{
source_array.remove(indexes[0]);
}
this should resolve your problem.
If the elements you wish to remove are all grouped together, you can do a subList(start, end).clear() operation.
If the elements you wish to remove are scattered, it may be better to create a new ArrayList, add only the elements you wish to include, and then copy back into the original list.
Edit: I realize now this was not a question of performance but of logic.
If you want to remove positions X to the Size
//a is the ArrayList
a=(ArrayList)a.sublist(0,X-1);
Assuming your indexes array is sorted (eg: 1, 3, 19, 29), you can do this:
for (int i = 0; i < indexes.size(); i++){
originalArray.remove(indexes.get(i) - i);
}
A more efficient method that I guess I have not seen above is creating a new Arraylist and selecting which indices survive by copying them to the new array. And finally reassign the reference.
I ended up here for a similar query and #aioobe's answer helped me figure out the solution.
However, if you are populating the list of indices to delete yourself, might want to consider using this:
indices.add(0, i);
This will eliminate the need for (the costly) reverse-sorting of the list before iterating over it, while removing elements from the main ArrayList.