I've got the ConcurrentModificationException and do not know why. I know that trying to iterate through a list using for loops and deleting elements inside the loop block is bad idea and can throw such exception, but I have no idea how to fix it in my case.
private static final List<Integer> originalList = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
originalList.add(i);
}
final int MAX_GROUP_SIZE = 5;
int partitionSize = 4;
List<List<Integer>> partitions = new LinkedList<>();
for (int i = 0; i < originalList.size(); i += partitionSize) {
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
}
int lastGroupSize = partitions.get(partitions.size() - 1).size();
if (lastGroupSize < partitionSize && partitions.size() > lastGroupSize){
List<Integer> lastGroup = partitions.remove(partitions.size() - 1);
for (int i = 0; i < lastGroupSize; i++) {
partitions.get(i).add(lastGroup.get(i));
}
}
System.out.println("GROUPS: " + partitions.size());
printGroups(new LinkedList<>(partitions));
}
The problem is that your calls to subList() don't create new lists. As the javadoc says it:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
The javadoc also says:
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.
When you call partitions.get(i).add(...), you're structurally modifying originalList, causing the error.
I don't believe you intended that, so to fix the problem, you just need to make sure your sub-lists are independent of the original list, i.e. copies, which is easy to do:
new ArrayList<>(originalList.subList(...))
Using the ArrayList(Collection) constructor will create a copy of the sub-list.
So, change this statement:
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
to this:
partitions.add(new ArrayList<>(originalList.subList(i,
Math.min(i + partitionSize, originalList.size()))));
You should never iterate the list and perform updating actions while doing so (where updating means adding or removing elements). This is a recipe for disaster.
In order to resolve this there are three possible scenarios to follow:
1) Copy the list, iterate over the copy and remove from the original one.
for (var number : new ArrayList<>(original)) {
if (element > 10) {
original.remove(element);
}
}
2) Use Streams
List<Integer> filtered = original.stream()
.filter(i -> i > 10)
.collect(Collectors.toList());
3) Use an iterator to loop over the list
Iterator<Integer> iterator = original.iterator();
while (iterator.hasNex()) {
Integer number = iterator.next();
if (number > 10) {
iterator.remove();
}
}
Personally I prefer streams.
Related
I am trying to remove duplicate elements from an unordered linked list in Java (a question from Cracking the Coding Interview).
I am using nested iterators over the same List object, but I get a ConcurrentModificationException when I remove an item. This is my code:
Iterator<String> i = list.iterator();
String curr;
while (i.hasNext()) {
curr = i.next();
Iterator<String> j = list.iterator();
while (j.hasNext()) {
String runner = j.next();
if (curr == runner){
j.remove();
}
}
}
The solution in the book uses a LinkedListNode object, which makes it possible to just change the pointers of the nodes, but is there any way to solve this using java.util.LinkedList only?
EDIT : The challenge was to do this without using a temporary buffer, otherwise an additional list would do the trick.
If you will not use iterators or foreach cycles, you will not receive ConcurrentModificationException. For example, you can do it like this:
List<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 1, 2, 3));
for (int i = 0; i < list.size() - 1; i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) {
list.remove(j);
j--;
}
}
}
System.out.println(list); // [1, 2, 3]
The following is an O(N^2) algorithm that doesn't use any temporary additional collections. Iterating backwards, from the last to the second element, if the current element of the list is already present earlier in the list, then remove the current element.
public static void removeDuplicates(List<?> list) {
ListIterator<?> iter = list.listIterator(list.size());
for (int index = list.size() - 1; index > 0; index--) {
Object element = iter.previous();
if (list.subList(0, index).contains(element))
iter.remove();
}
}
Test:
#Test
void removeDuplicatesShouldRemoveAllDuplicates() {
List<Integer> list = new LinkedList<>(Arrays.asList(1,2,1,3,1,4,5,5,1));
Main.removeDuplicates(list);
assertEquals(Arrays.asList(1,2,3,4,5), list);
}
This question already has answers here:
How can I turn a List of Lists into a List in Java 8?
(12 answers)
Closed 6 years ago.
The following example may be trivial, but I have created it to show what I need to achieve using different data (not integers). This code is runnable etc.
List<List<Integer>> master = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Integer> inner = new ArrayList<Integer>();
master.add(inner);
for (int j = 0; j < 10; j++) {
inner.add(i * 10 + j);
}
}
System.out.println(master);
//lets make single collections from that but add 1000 to every element - this represents some intermediate operations to generate part of final result
List<Integer> finalAccumulated = new ArrayList<Integer>(); // this will represent our accumulated, final result
for (List<Integer> topLvl : master) {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>(); //this step is important as it represents returning a collection of results for stream#map not single result
for (Integer nested : topLvl) { // this represents stream#map (or maybe collector?)
intermedialeAccumulated.add(nested + 1000);
}
finalAccumulated.addAll(intermedialeAccumulated); // this represent accumulation of collection of results, not just single result like stream#map do
}
System.out.println(finalAccumulated);
How to get the same result as in finalAccumulated using single Stream. By single I mean that in call chain there can be only single terminate action so the resulting form would be
finalAccumulated=master.stream()...intermediateCalls()...terminateCollectingCall();
Online ide with running code here
Just use a flat map:
List<Integer> finalAccumulated = master
.stream()
.flatMap((x) -> x.stream())
.map((i) -> i + 1000)
.collect(Collectors.toList());
private static void test() {
List<List<Integer>> master = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Integer> inner = new ArrayList<Integer>();
master.add(inner);
for (int j = 0; j < 10; j++) {
inner.add(i * 10 + j);
}
}
System.out.println(master);
//lets make single collections from that but add 1000 to every element - this represents some intermediate operations to generate part of final result
List<Integer> finalAccumulated = new ArrayList<Integer>(); // this will represent our accumulated, final result
for (List<Integer> topLvl : master) {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>(); //this step is important as it represents returning a collection of results for stream#map not single result
for (Integer nested : topLvl) { // this represents stream#map (or maybe collector?)
intermedialeAccumulated.add(nested + 1000);
}
finalAccumulated.addAll(intermedialeAccumulated); // this represent accumulation of collection of results, not just single result like stream#map do
}
//map then using flatmap
List<Integer> finalAccumulated2 = master.stream().map(topLvl -> {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>();
for (Integer nested : topLvl) {
intermedialeAccumulated.add(nested + 1000);
}
return intermedialeAccumulated;
}).flatMap(intermedialeAccumulated -> intermedialeAccumulated.stream())
.collect(Collectors.toList());
}
Here's how to produce the same finalAccumulated using a single Stream
List<Integer> finalAccumulated = IntStream.range(1000, 1000 + 10 * 10).boxed().collect(Collectors.toList());
Here would be an over-engineered and generic approach for such an operation
private static <T> List<T> flattenWith(List<List<T>> master, Function<T, T> transform) {
return master.stream()
.flatMap(List::stream)
.map(transform)
.collect(Collectors.toList());
}
Usage
flattenWith(master, x -> x + 1000)
I've been trying to understand the following code that uses Depth-First-Search (DFS) to print out all unique combinations of length k comprising of numbers [1..n]
Please see the line commented "doubt" in the private dfs function
public ArrayList<ArrayList<Integer>> combine(int n, int k) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if (n <= 0 || n < k)
return result;
ArrayList<Integer> item = new ArrayList<Integer>();
dfs(n, k, 1, item, result);
return result;
}
private void dfs(int n, int k, int start, ArrayList<Integer> item,
ArrayList<ArrayList<Integer>> res) {
if (item.size() == k) {
res.add(new ArrayList<Integer>(item)); /*doubt*/
return;
}
for (int i = start; i <= n; i++) {
item.add(i);
dfs(n, k, i + 1, item, res);
item.remove(item.size() - 1);
}
}
If I change it to res.add(item) it returns result as list of null lists. ListObject.add(E e) is a perfectly valid function, why doesn't it work here?
So your question concerns these two alternatives:
// works
res.add(new ArrayList<Integer>(item));
// won't work, results in empty lists
res.add(item);
The purpose of new ArrayList<Integer>(item) is to create a new list with the same content as the original, effectively cloning the original.
If you don't clone the original, it will stay empty. The first time dfs is called, item is empty, and then look at this loop:
for (int i = start; i <= n; i++) {
item.add(i);
dfs(n, k, i + 1, item, res);
item.remove(item.size() - 1);
}
Every element added to item will be later removed. This is why you end up with empty lists without the cloning step. Without cloning, not only you get a list of empty lists, all of the empty lists are actually the same original ArrayList that you created in combine, before the first call to dfs.
It's because item.remove(item.size() - 1); is modifying the same list that you just added to your list of results. So it always ends up removing all the items. The solution you have is actually copying the list of items and storing them in your result list. No one has a reference to that list so it doesn't get modified.
I already have a list type Integer with values in it and I want to test sequentially from index zero if the sum of one range of elements satisfy a particular value then copy this range in an list and store it in a list of linkedlist. Then again test sequentially but now from the following index of the previous range, so if the previous range was index 0 to index 9 then start at index 10, and repeat the process until the last index.
List<Integer> arrayB = new LinkedList<Integer>(); //this is the array with values in it
List<LinkedList> p = new LinkedList<LinkedList>();// this is the array of arrays
List<Integer> arrayA = new LinkedList<Integer>();// this is the range or the sub list of arrayB
public void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
for (int w : arrayB)
{
count = w + count;
arrayA.add(w);
if(count == n)
{
count = 0;
p.add((LinkedList) arrayA);
arrayA.clear();
}
}
}
However, this code fail when I call method clear in arrayA so is there any alternative to code with this logic regardless of the data structure used?
My understanding of the problem is the following:
There exists an array from which you would like to extract a certain range of values given that they satisfy some criteria. In this case, the criterion is that the range evaluates to some sum. Once this has been completed, you would like to repeat the process until all of the values in the original data-structure have been exhausted.
I will assume that your original data-structure is an array of integers, and that your resulting data-structure is a linkedlist of integer arrays.
One way to do it may be to keep a global counter that keeps track of the current index of the original array, such as something like the following:
int[] originalArray = {//list of numbers separated by commas};
LinkedList<Integer[]> resultingList = new LinkedList<>();
int currentIndex = 0;
public static void function(int totalSum) {
int currentSum = 0;
int initialIndex = currentIndex;
while((currentSum != totalSum) && (currentIndex < (originalArray.length - 1))) {
if(currentSum + initialArray[currentIndex] <= totalSum) {
currentSum += initialArray[currentIndex];
currentIndex++;
}
else {
break;
}
}
if(currentSum = totalSum) {
int[] arrayToAdd = new int[currentIndex - initialIndex - 1];
for(int i = 0; i < currentIndex - initialIndex; i++) {
arrayToAdd[i] = originalArray[initialIndex + i];
}
resultingList.add(arrayToAdd);
}
}
You are using the same list reference arrayA every time you add a sub list into p, every list element in p is pointing to the same arrayA . So when you call arrayA.clear(); You clear all the list elements in p.
To correct that, you need to create a new list object when you add a sublist to arrayA:
public static void function(int n)// suppose that n = 6 and arrayB have these value {1,2,3,1,1,1,1,2}
{
int count = 0;
LinkedList<Integer> subList = new LinkedList<>();
for (int w : arrayB) {
count = w + count;
subList.add(w);
if (count == n) {
count = 0;
p.add((LinkedList) subList); // p is adding a new list reference every time
subList = new LinkedList<>(); // create a new list object, subList points to a new list object
}
}
}
The issue is that when you add your linked list into the final storage p, you are assuming the elements of the list are put in there. Only a pointer is referenced, so when you clear it the next line, all the elements are gone.
p.add((LinkedList) arrayA);
arrayA.clear();
One tip is to move arrayA's scope to inside the function. This is because it's temporary, and only a sublist, so it shouldn't be at the instance level. It can be reused by doing a
arrayA = new LinkedList<Integer>();
and when doing so, you haven't lost the old list because p is keeping a reference to it.
Another tip is to name your lists with meaningful names.
originalIntList, groupedIntList, singleGroupIntList help the reader figure out what they could be doing more than a comment stating obvious aspects of the Java object.
I have a NavigableSet and I would like to get its median object.
Being that it's a NavigableSet, I know it's sorted, and thus I know that the median of it is either the middle element, or the arithmetic middle of the two middle elements.
Therefore I would like to access the element at set.size() / 2, but the NavigableSet interface doesn't allow me to.
Is there an easy way to get the specific element without having to iterate through the set manually?
Yes set does not allow you to get an element from a particular index. But I think if you convert it to an array then you will be able to achieve what you need. I tried this sample code, see if it helps:
NavigableSet set = new TreeSet<Integer>();
set.add(new Integer(5));
set.add(new Integer(4));
set.add(new Integer(3));
set.add(new Integer(2));
set.add(new Integer(1));
Integer medianIndex = set.size()/2;
System.out.println(set.toArray()[medianIndex]);
Output: 3
Strings are ordered alphabetically, have a look at this small example:
Edit: Now it really does what you wanted.
public static void main(String[] args) {
NavigableSet<String> set = new TreeSet<String>();
set.add("gamma");
set.add("alpha");
set.add("beta");
System.out.println(Arrays.toString(set.toArray()));
int indexOfGamma = set.headSet("gamma").size();
System.out.println(indexOfGamma);
System.out.println(get(set, set.first(), indexOfGamma));
}
public static String get(NavigableSet<String> set, String e, int index) {
if (index == 0) {
return e;
}
return get(set, set.higher(e), --index);
}
This is the output:
[alpha, beta, gamma]
2
gamma
I didn't do any benchmarks with greater data-sets, but I guess it should perform quite decent. The higher() method should directly point to the next element in the tree.
I haven't been able to find any way other than iterating through the set "index" number of times. However, since we know the size of the set, we can speed that up to at least half the size by using both ascending and descending iteration:
public static <T> T getInNavigableSetByIndex(NavigableSet<T> set, int index) {
Objects.requireNonNull(set);
final int size = set.size();
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
final boolean descend = index >= size / 2;
final Iterator<T> itr = descend ? set.descendingIterator() : set.iterator();
final int stepCount = descend ? size - index : index + 1;
T object = null;
for (int i = 0; i < stepCount && itr.hasNext(); i++) {
object = itr.next();
}
return object;
}