Reputation: 43
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);
}
Upvotes: 1
Views: 2842
Reputation: 5784
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:
String[]
– creating it explicitly or relying on automatic array creation via varargsList<String>
from that array using Arrays.asList()
Since the starting point is a String array, and knowing that you're interested in keeping only every 4th element, you could:
java.util.List<String>
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.
Upvotes: 1
Reputation: 40062
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:
4
, this will work. It will just ignore the extra elements.Upvotes: 1
Reputation: 1471
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]
Upvotes: 3
Reputation: 9135
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.
Upvotes: 3