I am trying to program a method that deletes the first, second and third element of every group of 4 elements.
It seems not working at all.
Could anyone please help?
public static void reduziereKommentare(List<String> zeilen) {
if (!zeilen.isEmpty()) {
if (zeilen.size() % 4 != 0) {
throw new RuntimeException("Illegal size " + zeilen.size() + " of list, must be divisible by 4.");
}
for (int i = 1; i <= zeilen.size() % 4; i++) {
zeilen.remove(i);
zeilen.remove(i + 1);
zeilen.remove(i + 2);
}
}
System.out.println(zeilen);
}
As said in the comments, removing an element impacts the indexing. Whenever I need to do something like this, I either use an Iterator, or loop backwards.:
for (int i = zeilen.size() - 4; i >= 0; i -= 4) {
zeilen.remove(i + 2);
zeilen.remove(i + 1);
zeilen.remove(i);
}
Note that I subtract 4 from i each iteration, so I go back a full block of four each time.
Also note that I remove the largest indexed elements first. If I use i, i + 1 and i + 2 inside the loop, I again run into the same issue. I could also have used i 3 times, but this makes it more clear.
My take...does not require the size precondition check but you may want to still catch that if it represents an error of broader scope than this method.
Given this test code...
// Test code
List<String> myList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
myList.add(String.valueOf(i));
}
the 'zeilen' loop can be implemented as ...
// "before" diagnostics
System.out.println(zeilen);
// The 'zeilen' loop
for (int i = 0, limit = zeilen.size(); i < limit; i++) {
if ((i+1) % 4 > 0) zeilen.remove(i/4);
}
// "after" diagnostics
System.out.println(zeilen);
and produces
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[3, 7, 11, 15, 19]
Works with any length list leaving every '4th' element in list.
A few more test cases :
Given Results in
[] []
[0,1] []
[0,1,2,3] [3]
[0,1,2,3,4] [3]
[0,1,2,3,4,5,6,7] [3,7]
[0,1,2,3,4,5,6,7,8] [3,7]
Would it not be easier to simply add every fourth item to a new list and return that? This would also eliminate any repetitive copying that could be involved when removing elements from a list. And the target list can be appropriately sized to start.
public static List<String> reduziereKommentare(List<String> zeilen) {
Objects.requireNonNull(zeilen);
List<String> zeilen1= new ArrayList<>(zeilen.size()/4);
for(int i = 3; i < zeilen.size(); i+=4) {
zeilen1.add(zeilen.get(i));
}
return zeilen1;
}
You could also use a stream.
zeilen = IntStream.iterate(3, i ->i < zeilen.size(), i->i+=4)
.mapToObj(zeilen::get).toList();
Notes:
whether the list is empty or the size is not divisible by 4, this will work. It will just ignore the extra elements.
assigning the result to the original variable will result in the old list being garbage collected.
I only check for a null argument since that would cause an exception. Of course, if alerting the user of the size is important just add the other check(s) back in.
Your code sample uses a data type of List - List<String> zeilen - but you separately wrote a comment which states that you're starting from an array:
"I used the Arrays.asList() function to add elements to the list"
The signature for asList() shows the input argument is an array, defined using varargs:
public static <T> List<T> asList(T... a)
Thus, you would start from something like this:
// rely on automatic array creation via varargs
List<String> list = Arrays.asList("one", "two", "three");
or from an explicit array, like this:
String[] strings = {"one", "two", "three"};
List<String> list = Arrays.asList(strings);
Here's a more complete picture of your current solution:
start with an array – String[] – creating it explicitly or relying on automatic array creation via varargs
create a List<String> from that array using Arrays.asList()
traverse the List skipping three items at a time, keeping only each fourth item (so: 4th, 8th, 12th, 16th, etc.)
Since the starting point is a String array, and knowing that you're
interested in keeping only every 4th element,
you could:
create a new, empty java.util.List<String>
iterate over each element of the array
for every 4th, 8th, etc element, add that to the final result list; ignore everything else
Here's the code to do that:
private static List<String> buildListOfEveryFourthElement(String[] array) {
List<String> everyFourthElement = new ArrayList<>();
if (array != null) {
// start from "1", a bit easier to reason about "every 4th element"?
int current = 1;
for (String s : array) {
if (current > 1 && current % 4 == 0) {
everyFourthElement.add(s);
}
current++;
}
}
return everyFourthElement;
}
I omitted the check for whether the input is exactly divisible by 4, but you could easily edit the first if statement
to include that: if (array != null && array.length % 4 == 0) { .. }
A benefit to this "build the List as you go" approach (vs. calling Arrays.asList() with a starting array)
is that the original input array would not be associated in any way with the result list.
So what? As you mentioned in one of your comments that you discovered it's not permissible
to modify the list – calling .remove() will throw java.lang.UnsupportedOperationException.
Note this will also happen if you try to add() something to the list.
Why does it throw an exception?
Because asList() returns a java.util.List which is backed by the input array, meaning the list and array are
sort of tied together. If it allowed you to remove (or add) items from (or to) the
list then it would also have to automatically update the backing array, and they didn't implement it that way.
Here's a brief snip from asList() Javadoc:
Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.)
By creating a new List and populating it along the way, you are free to modify that list later in your code
by removing or adding elements, sorting the whole thing, etc. You would also be guarded against any changes to the array
showing up as (possibly surprising) changes in the list – because
the list is backed by the array, a change in an array element would be visible in the associated list.
Related
I was trying this problem where we need to find the permutations of the elements in the array.
This is the leetcode problem no 46. The issue I've faced is that I'm not able to output the ans it just keeps returning a blank ArrayLists:
Code:
public List<List<Integer>> permute(int[] nums)
{
List<List<Integer>> fans = new ArrayList<>();
HashMap<Integer, Integer> fmap = new HashMap<>();
for(int i: nums){
fmap.put(i, fmap.getOrDefault(i, 0) + 1);
}
int n=nums.length;
List<Integer> ans=new ArrayList<>(n);
dfs(1, n, fmap, ans, fans);
return fans;
}
public void dfs(int cs, int ts, HashMap<Integer, Integer> fmap,List<Integer> ans, List<List<Integer>> fans)
{
if (cs > ts)
{
fans.add(ans);
return;
}
for(Integer val: fmap.keySet())
{
if (fmap.get(val) > 0)
{
fmap.put(val, fmap.get(val) - 1);
ans.add(val);
dfs(cs + 1, ts, fmap, ans, fans);
ans.remove(ans.size() - 1);
fmap.put(val, fmap.get(val) + 1);
}
}
}
Output for the test case [0,1]:
[[],[]]
The actual output should be:
[[0,1],[1,0]]
When I'm checking the "potential answer" inside the recursive method, I am able to see the correct answer. I mean, if I print the output in the dfs method, it shows the correct answer:
Change in the code:
if (cs > ts)
{
fans.add(ans);
System.out.println(fans);
return;
}
Now it's printing the value of fans:
[[0, 1]]
[[1, 0], [1, 0]]
But these values are not being updated in the fans and the returned value comes up blank.
I read someone mention this same issue, but it was for Python, and the solution in that case was to do a deep copy of the list.
I'm not sure how to do that in Java.
What I'm doing wrong?
In order to generate a list of permutations, you don't need a Map. You've only introduced redundant actions, which are not useful anyhow. If you doubt, add a couple of print-statements to visualize the map state it will always contain the same key with the value 1 (all numbers in the input are guaranteed to be unique) and it has no impact on the result.
Source of data for generating the Permutations
Besides the fact that attempt to utilize the HashMap as the source of data for generating permutations isn't working because of the bugs, it's also not a good idea because the order iteration over the keySet of HashMap is not guaranteed to be consistent.
As the uniform mean for storing the numbers that haven't yet been applied in current permutation, we can use an ArrayList. In this case because there will be no duplicates in the input (see the quote from the leetcode below), we can use a LinkedHashSet instead to improve performance. As explained below, a removal of elements will happen at before making every recursive a call, and removal from an ArrayList has a cost of O(n), meanwhile with LinkedHashSet it would be reduced to O(1).
Constraints:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
All the integers of nums are unique.
Generating the Permutations
Each generated permutation should be contained in its own list. In your code, you've created one single list which is being passed around during recursive calls and eventually every recursive branch adds the same list to the resulting list. Which obviously should not happen.
You see, the result is being printed as [[],[]]. It seems like a list containing two lists, but in fact they refer to the same empty list.
And this list is empty because every element that was added to it, is being removed after performing a recursive call:
ans.add(val);
... <- rursive call in between
ans.remove(ans.size() - 1); // removes the last element
if I print the output in the dfs method, it shows the correct answer:
Actually, it's not correct. If you take a careful look at the results, you'll see the nested lists are the same [[1, 0], [1, 0]].
The final result is blank because all recursive calls are happening between each value being added and removed (see the code snippet above). I.e. removal will be performed in revered order. That would be the last lines to be executed, not the return statements. To understand it better, I suggest you to walk through the code line by line and draw on paper all the changes done to the ans list for a simple input like [0, 1].
Instead, you should create a copy of the list containing not fully generated permutation (answer) and then add an element into the copy. So that the initial permutation (answer) remains unaffected and can be used as a template in all subsequent iterations.
List<Integer> updatedAnswer = new ArrayList<>(answer);
updatedAnswer.add(next);
And you also need to create a copy of the source of data and remove the element added to the newly created permutation (answer) in order to avoid repeating this element:
Set<Integer> updatedSource = new LinkedHashSet<>(source);
updatedSource.remove(next);
Sidenote: it's a good practice to give meaningful names to methods and variables. For instance, names cs and ts aren't informative (it's not clear what they are meant to store without looking at the code), method-name dfs is confusing, DFS is a well-known algorithm, which is used for traversing tree or graph data structures, but it's not related to this problem.
Building a recursive solution
It makes sense to keep the recursive method to be void to avoid wrapping the result with an additional list that would be thrown away afterwards, but in general it's more handy to return the result rather than accumulating it in the parameter. For performance reasons, I'll keep the method to be void.
Every recursive implementation should contain two parts:
Base case - that represents a simple edge-case (or a set of edge-cases) for which the outcome is known in advance. For this problem the base case would represent a situation when the given permutation has reached the size of the initial array, i.e. the source will contain no element, and we need to check whether it's empty or not. Parameters cs and ts that were used for this check in the solution provided in the question are redundant.
Recursive case - a part of a solution where recursive calls a made and when the main logic resides. In the recursive case, we need to replicate the given answer and source as explained above and use the updated copies as the arguments for each recursive call.
That's how it might be implemented:
public static List<List<Integer>> permute(int[] nums) {
Set<Integer> source = new LinkedHashSet<>();
for (int next: nums) source.add(next);
List<List<Integer>> result = new ArrayList<>();
permute(source, new ArrayList<>(), result);
return result;
}
public static void permute(Set<Integer> source, List<Integer> answer,
List<List<Integer>> result) {
if (source.isEmpty()) {
result.add(answer);
return;
}
for (Integer next: source) {
List<Integer> updatedAnswer = new ArrayList<>(answer);
updatedAnswer.add(next);
Set<Integer> updatedSource = new LinkedHashSet<>(source);
updatedSource.remove(next);
permute(updatedSource, updatedAnswer, result);
}
}
main()
public static void main(String[] args) {
int[] source = {1, 2, 3};
List<List<Integer>> permutations = permute(source);
for (List<Integer> permutation: permutations) {
System.out.println(permutation);
}
}
Output:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
A link to Online Demo
For example, i have this list
List<Integer> list = new ArrayList<>();
list.add(64);
list.add(5);
list.add(10);
list.add(66);
list.add(7);
list.add(68);
How i remove only the numbers that begins with "6"
There are several ways how you can approach this task:
using an Iterator;
by utilizing so-called traditional for loop;
with a help of Stream IPA;
using method Collection.removeIf().
Note that attempt to address this problem using enhanced for loop (sometimes called "for-each" loop) will lead to CuncurrentModdificationException at runtime (if the collection will get modified). Because under hood iteration over a collection with enhanced for loop makes use of an iterator that is provided by the collection. And while iteration happens collection must not be modified other than by means of the iterator itself, i.e. by using method remove() of the iterator.
To iterate over the collection with an iterator firstly you have to an instance of the Iterator by invoking .iterator() on the collection. Methods of the Iterator class hasNext() and next() are used to check whether the next element exists and to move to the next element respectively.
public static List<Integer> removeNumberIfStarsWith(List<Integer> source, int target) {
List<Integer> copy = new ArrayList<>(source); // defencive copy to preserve the source list intact
Iterator<Integer> iterator = copy.iterator();
while (iterator.hasNext()) {
Integer item = iterator.next();
if (item.toString().startsWith(String.valueOf(target))) {
iterator.remove();
}
}
return copy;
}
To implement this task with traditional for loop we have to keep in mind that removal of elements must not mess the indices that are haven't visited yet. Probably the simplest way to do it is to iterate in reversed order starting from the last index.
Note:
there are two flavors of remove() method available with an instance of the List interface. One accepts an int index another an object that has to be removed. And this case is interesting because it is a choice between remove(int) and remove(Integer). Remember that when an overloaded method is called if a version of this method with exactly the same set of parameter types (same types in the same order) exists, the compiler will map this version to that method call. I.e. method remove(int) will be invoked since we are passing a primitive type int.
The code for this task with traditional for loop might look like:
public static List<Integer> removeNumberIfStarsWith(List<Integer> source, int target) {
List<Integer> copy = new ArrayList<>(source); // defencive copy to preserve the source list intact
for (int i = copy.size() - 1; i >= 0; i--) {
Integer item = copy.get(i);
if (item.toString().startsWith(String.valueOf(target))) {
copy.remove(i);
}
}
return copy;
}
Since this question is related to basic operations with collections I don't assume the knowledge of lambda expressions and streams. But for completeness and for future readers, I'll provide two other options that I've mentioned above.
for information on lambda expressions and functional interfaces read this
to get familiar with Stream IPA take a look at this tutorial
The condition inside the method filter() in the stream pipeline and Collection.removeIf() must defenetelly look familiar to you. Both method expect Predicate a function that takes an object and returns a boolean value. Collection.removeIf() will remove the element if the predicate returns true, but filter() does the opposite it'll retain the element if the predicate returns true.
So stream-based implementation might look like this:
public static List<Integer> removeNumberIfStarsWith(List<Integer> source, int target) {
return source.stream()
.filter(item -> !item.toString().startsWith(String.valueOf(target)))
.collect(Collectors.toList());
}
Solution with Collection.removeIf():
public static List<Integer> removeNumberIfStarsWith(List<Integer> source, int target) {
List<Integer> copy = new ArrayList<>(source); // defencive copy to preserve the source list intact
copy.removeIf(item -> item.toString().startsWith(String.valueOf(target)));
return copy;
}
main()
public static void main(String[] args) {
List<Integer> list = List.of(64, 5, 10, 66, 7, 68);
System.out.println(list);
System.out.println(removeNumberIfStarsWith(list, 6)); // value 6 provided as an argument and not hardcoded inside the methods
}
Output (for all the versions)
[64, 5, 10, 66, 7, 68]
[5, 10, 7]
for example, you want to delete the numbers starting with 6
you can convert each int to a list while traversing the list and check if the first character of the string is "6". if it is 6 remove that number from the list
for(int i: list)
{
String s= Integer.toString(i);
if(s.charAt(0)=='6')
{
list.remove(new Integer(i));
}
}
List result = list.stream().filter(a -> !a.toString().startsWith("6")).collect(Collectors.toList());
result.forEach(System.out::println);
So I'm learning about MergeSort right now to solve a problem on Project Euler.
I am trying to sort a list 5163 names alphabetically. I did some research and found that MergeSort would be a fairly efficient way of doing so.
So I coded up an implementation based off of this link.
Below are my mergeSort and merge methods.
public static List<String> mergeSort(List<String> list){
if (list.size() == 1){ //if fully divided
return list;
}
List<String> leftSide = list.subList(0, list.size()/2);
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
leftSide = mergeSort(leftSide);
rightSide = mergeSort(rightSide);
return merge(leftSide, rightSide);
}
public static List<String> merge(List<String> listA, List<String> listB){
List<String> listC = new LinkedList<String>();
while (!listA.isEmpty() & !listB.isEmpty()){ //while both have elements
if (listA.get(0).compareTo(listB.get(0)) > 1){ //if stringA is greater than stringB
listC.add(listA.get(0));
listA.remove(0);
} else { //if stringB is greater than stringA
listC.add(listB.get(0));
listB.remove(0);
}
}
while (!listA.isEmpty()){ //while listA has elements
listC.add(listA.get(0));
listA.remove(0);
}
while (!listB.isEmpty()){ //while listB has elements
listC.add(listB.get(0));
listB.remove(0);
}
return listC;
}
The problem is that when I try and use the mergeSort method with my list of names, it gives me the following error:
Exception in thread "main" java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
Which is pointing at this line:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
I don't really understand why it is happening. It seems to me that I have used the exact same method as shown in the tutorial.
Also, from what I understand, this error is telling me that the size of my list is zero. This would be the only way in which list.size()/2 + 1 could equal one and list.size() could equal zero. However, I don't see why my list could be zero. Because it is going to be divided until the size is one, and then it will be returned.
The only way I could see it equalling zero was if it was zero to begin with, but I have confirmed that my list.size() is 5163 to start with.
Could someone help me point out what I am doing wrong? I am sure it is something really simple, but since I have just started learning this, I am not sure what it is.
EDIT:
I check the list's size here:
System.out.println(namesArray.size()); //prints "5163"
namesArray = mergeSort(namesArray);
So how could my list ever have a size of zero?
The only way I could see it equalling zero was if it was zero to begin with, but I have confirmed that my list.size() is 5163 to start with.
There is a second way. Consider what happens deep into your recursion when on entry to mergeSort the input sublist contains two elements.
List<String> leftSide = list.subList(0, list.size()/2);
The above becomes list.sublist(0,1). Since the ending index is exclusive, the sublist contains one element. Then:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
This becomes list.sublist(2,2). Again, since the ending index is exclusive, this creates a list of length zero, or an emtpy list. Now when you reach the recursive calls
leftSide = mergeSort(leftSide);
rightSide = mergeSort(rightSide);
the left side recursion works but the right side recursion passes a zero-length list. At the top of mergeSort you have
if (list.size() == 1){ //if fully divided
return list;
}
which checks for the terminating condition of list size 1, but does not stop the code from attempting to process an empty zero-length list. This is what causes your exception immediately after this point.
If you changed the test to if (list.size() <= 1) you'd have prevented the exception, but would still have a problem since some of the elements would not have been sorted due to the ending index problem.
The correct solution is to change one line:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
^^^
Remove this----------------------------------^
If you have an empty list, you call
List<String> rightSide = list.subList(0/2 + 1, 0);
So your from index is bigger than the toIndex. And that raises the exception.
Form the javadoc:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.
This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:
list.subList(from, to).clear();
Similar idioms may be constructed for indexOf and lastIndexOf, and all of the algorithms in the Collections class can be applied to a subList.
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
Parameters:fromIndex - low endpoint (inclusive) of the subList
toIndex - high endpoint (exclusive) of the subListReturns:a view of the specified range within this list
Throws:IndexOutOfBoundsException - for an illegal endpoint index value (fromIndex < 0 || toIndex > size || fromIndex > toIndex)
You have to check if the list is empty.
I get exception Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 for the below code. But couldn't understand why.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
//Set index deliberately as 1 (not zero)
s.add(1,"Elephant");
System.out.println(s.size());
}
}
Update
I can make it work, but I am trying to understand the concepts, so I changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
ArrayList index starts from 0(Zero)
Your array list size is 0, and you are adding String element at 1st index. Without adding element at 0th index you can't add next index positions. Which is wrong.
So, Simply make it as
s.add("Elephant");
Or you can
s.add(0,"Elephant");
You must add elements to ArrayList serially, starting from 0, 1 and so on.
If you need to add elements to specific position you can do the following -
String[] strings = new String[5];
strings[1] = "Elephant";
List<String> s = Arrays.asList(strings);
System.out.println(s);
This will produce the sollowing output
[null, Elephant, null, null, null]
Your ArrayList is empty. With this line:
s.add(1,"Elephant");
You are trying to add "Elephant" at index 1 of the ArrayList (second position), which doesn't exist, so it throws a IndexOutOfBoundsException.
Use
s.add("Elephant");
instead.
ArrayList is not self-expandable. To add an item at index 1, you should have element #0.
If you REALLY want "Elephant" at index 1, then you can add another (e.g. null) entry at index 0.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
s.add(null);
s.add("Elephant");
System.out.println(s.size());
}
}
Or change the calls to .add to specify null at index 0 and elephant at index 1.
add(int index, E element) API says, Your array list has zero size, and you are adding an element to 1st index
Throws:
IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())
Use boolean add(E e) instead.
UPDATE based on the question update
I can make it work, but I am trying to understand the concepts, so I
changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
When you call new ArrayList<Integer>(10), you are setting the list's initial capacity to 10, not its size. In other words, when constructed in this manner, the array list starts its life empty.
For Android:
If you need to use a list that is going to have a lot of gaps it is better to use SparseArray in terms of memory (an ArrayList would have lots of null entries).
Example of use:
SparseArray<String> list = new SparseArray<>();
list.put(99, "string1");
list.put(23, "string2");
list.put(45, "string3");
Use list.append() if you add sequential keys, such as 1, 2, 3, 5, 7, 11, 13...
Use list.put() if you add non-sequential keys, such as 100, 23, 45, 277, 42...
If your list is going to have more than hundreds of items is better to use HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array.
You can initialize the size (not the capacity) of an ArrayList in this way:
ArrayList<T> list = new ArrayList<T>(Arrays.asList(new T[size]));
in your case:
ArrayList<String> s = new ArrayList<String>(Arrays.asList(new String[10]));
this creates an ArrayList with 10 null elements, so you can add elements in random order within the size (index 0-9).
Don't add index as 1 directly in list
If you want to add value in list add it like this
s.add("Elephant");
By default list size is 0
If you will add any elements in list, size will increased automatically
you cant add directly in list 1st index.
//s.add(0, "Elephant");
By the way, ArrayList<String> s = new ArrayList<>(10);
set the initialCapacity to 10.
Since the capacity of Arraylist is adjustable, it only makes the java knows the approximate capacity and try to avoid the performance loss caused by the capacity expansion.
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;
}