In Java, remove empty elements from a list of Strings - java

In Java, I have an ArrayList of Strings like:
[,Hi, ,How,are,you]
I want to remove the null and empty elements, how to change it so it is like this:
[Hi,How,are,you]

List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
System.out.println(list);
list.removeAll(Arrays.asList("", null));
System.out.println(list);
Output:
[, Hi, null, How]
[Hi, How]

Its a very late answer, but you can also use the Collections.singleton:
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
// in one line
list.removeAll(Arrays.asList("", null))
// separately
list.removeAll(Collections.singleton(null));
list.removeAll(Collections.singleton(""));

Another way to do this now that we have Java 8 lambda expressions.
arrayList.removeIf(item -> item == null || "".equals(item));

If you are using Java 8 then try this using lambda expression and org.apache.commons.lang.StringUtils, that will also clear null and blank values from array input
public static String[] cleanArray(String[] array) {
return Arrays.stream(array).filter(x -> !StringUtils.isBlank(x)).toArray(String[]::new);
}
ref - https://stackoverflow.com/a/41935895/9696526

If you were asking how to remove the empty strings, you can do it like this (where l is an ArrayList<String>) - this removes all null references and strings of length 0:
Iterator<String> i = l.iterator();
while (i.hasNext())
{
String s = i.next();
if (s == null || s.isEmpty())
{
i.remove();
}
}
Don't confuse an ArrayList with arrays, an ArrayList is a dynamic data-structure that resizes according to it's contents. If you use the code above, you don't have to do anything to get the result as you've described it -if your ArrayList was ["","Hi","","How","are","you"], after removing as above, it's going to be exactly what you need - ["Hi","How","are","you"].
However, if you must have a 'sanitized' copy of the original list (while leaving the original as it is) and by 'store it back' you meant 'make a copy', then krmby's code in the other answer will serve you just fine.

Going to drop this lil nugget in here:
Stream.of("", "Hi", null, "How", "are", "you")
.filter(t -> !Strings.isNullOrEmpty(t))
.collect(ImmutableList.toImmutableList());
I wish with all of my heart that Java had a filterNot.

There are a few approaches that you could use:
Iterate over the list, calling Iterator.remove() for the list elements you want to remove. This is the simplest.
Repeatedly call List.remove(Object). This is simple too, but performs worst of all ... because you repeatedly scan the entire list. (However, this might be an option for a mutable list whose iterator didn't support remove ... for some reason.)
Create a new list, iterate over the old list, adding elements that you want to retain to a new list.
If you can't return the new list, as 3. above and then clear the old list and use addAll to add the elements of the new list back to it.
Which of these is fastest depends on the class of the original list, its size, and the number of elements that need to be removed. Here are some of the factors:
For an ArrayList, each individual remove operation is O(N), where N is the list size. It is expensive to remove multiple elements from a large ArrayList using the Iterator.remove() method (or the ArrayList.remove(element) method).
By contrast, the Iterator.remove method for a LinkedList is O(1).
For an ArrayList, creating and copying a list is O(N) and relatively cheap, especially if you can ensure that the destination list's capacity is large enough (but not too large).
By contrast, creating and copying to a LinkedList is also O(N), but considerably more expensive.
All of this adds up to a fairly complicated decision tree. If the lists are small (say 10 or less elements) you can probably get away with any of the approaches above. If the lists could be large, you need to weigh up all of the issues in the list of the expected list size and expected number of removals. (Otherwise you might end up with quadratic performance.)

This code compiles and runs smoothly.
It uses no iterator so more readable.
list is your collection.
result is filtered form (no null no empty).
public static void listRemove() {
List<String> list = Arrays.asList("", "Hi", "", "How", "are", "you");
List<String> result = new ArrayList<String>();
for (String str : list) {
if (str != null && !str.isEmpty()) {
result.add(str);
}
}
System.out.println(result);
}

If you get UnsupportedOperationException from using one of ther answer above and your List is created from Arrays.asList(), it is because you can't edit such List.
To fix, wrap the Arrays.asList() inside new LinkedList<String>():
List<String> list = new LinkedList<String>(Arrays.asList(split));
Source is from this answer.

Regarding the comment of Andrew Mairose - Although a fine solution, I would just like to add that this solution will not work on fixed size lists.
You could attempt doing like so:
Arrays.asList(new String[]{"a", "b", null, "c", " "})
.removeIf(item -> item == null || "".equals(item));
But you'll encounter an UnsupportedOperationException at java.util.AbstractList.remove(since asList returns a non-resizable List).
A different solution might be this:
List<String> collect =
Stream.of(new String[]{"a", "b", "c", null, ""})
.filter(item -> item != null && !"".equals(item))
.collect(Collectors.toList());
Which will produce a nice list of strings :-)

lukastymo's answer seems the best one.
But it may be worth mentioning this approach as well for it's extensibility:
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
list = list.stream()
.filter(item -> item != null && !item.isEmpty())
.collect(Collectors.toList());
System.out.println(list);
What I mean by that (extensibility) is you could then add additional filters, such as:
.filter(item -> !item.startsWith("a"))
... although of course that's not specifically relevant to the question.

List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", "", "How"));
Stream<String> stream = list .stream();
Predicate<String> empty = empt->(empt.equals(""));
Predicate<String> emptyRev = empty.negate();
list= stream.filter(emptyRev).collect(Collectors.toList());
OR
list = list .stream().filter(empty->(!empty.equals(""))).collect(Collectors.toList());

private List cleanInputs(String[] inputArray) {
List<String> result = new ArrayList<String>(inputArray.length);
for (String input : inputArray) {
if (input != null) {
String str = input.trim();
if (!str.isEmpty()) {
result.add(str);
}
}
}
return result;
}

Related

How to lazy initialize Collectors.toList() in java 8 stream api?

I want to collect items based on a filter. But the resulting list should not be initialized if no match was found. I'd prefer null instead of empty list.
List<String> match = list
.stream()
.filter(item -> item.getProperty == "match")
.collect(Collectors.toList());
if (match != null && !match.isEmpty()) {
//handle seldom match
}
Problem: most of the time I will not have a match, resulting in an empty collection. Which means most of the time the list is instanciated even though I don't need it.
Collecto.toList() allocates a List using ArrayList::new which is a very cheap operation since ArrayList doesn't actually allocate the backing array until elements are inserted. All the constructor does is initialize an internal Object[] field to the value of a statically created empty array. The actual backing array is initialized to its "initial size" only when the first element is inserted.
So why go through the pain of avoiding this construction? It sounds like a premature optimization.
If you're so worried about GC pressure, just don't use Streams. The stream and the Collector itself are probably quite a lot more "expensive" to create than the list.
I am only thinking of a case when something other than Collectors.toList() would be expensive to compute, otherwise use:
... collect(Collectors.collectingAndThen(list -> {
list.isEmpty() ? null: list;
}))
But just keep in mind that someone using that List would most probably expect an empty one in case of missing elements, instead of a null.
Creating an empty ArrayList is quite cheap and laziness here would only make things worse.
Otherwise, here is a variant that could defer to null if you really really wanted to:
private static <T> List<T> list(Stream<T> stream) {
Spliterator<T> sp = stream.spliterator();
if (sp.getExactSizeIfKnown() == 0) {
System.out.println("Exact zero known");
return null;
}
T[] first = (T[]) new Object[1];
boolean b = sp.tryAdvance(x -> first[0] = x);
if (b) {
List<T> list = new ArrayList<>();
list.add(first[0]);
sp.forEachRemaining(list::add);
return list;
}
return null;
}

Splitting List into sublists along elements

I have this list (List<String>):
["a", "b", null, "c", null, "d", "e"]
And I'd like something like this:
[["a", "b"], ["c"], ["d", "e"]]
In other words I want to split my list in sublists using the null value as separator, in order to obtain a list of lists (List<List<String>>). I'm looking for a Java 8 solution. I've tried with Collectors.partitioningBy but I'm not sure it is what I'm looking for. Thanks!
Although there are several answers already, and an accepted answer, there are still a couple points missing from this topic. First, the consensus seems to be that solving this problem using streams is merely an exercise, and that the conventional for-loop approach is preferable. Second, the answers given thus far have overlooked an approach using array or vector-style techniques that I think improves the streams solution considerably.
First, here's a conventional solution, for purposes of discussion and analysis:
static List<List<String>> splitConventional(List<String> input) {
List<List<String>> result = new ArrayList<>();
int prev = 0;
for (int cur = 0; cur < input.size(); cur++) {
if (input.get(cur) == null) {
result.add(input.subList(prev, cur));
prev = cur + 1;
}
}
result.add(input.subList(prev, input.size()));
return result;
}
This is mostly straightforward but there's a bit of subtlety. One point is that a pending sublist from prev to cur is always open. When we encounter null we close it, add it to the result list, and advance prev. After the loop we close the sublist unconditionally.
Another observation is that this is a loop over indexes, not over the values themselves, thus we use an arithmetic for-loop instead of the enhanced "for-each" loop. But it suggests that we can stream using the indexes to generate subranges instead of streaming over values and putting the logic into the collector (as was done by Joop Eggen's proposed solution).
Once we've realized that, we can see that each position of null in the input is the delimiter for a sublist: it's the right end of the sublist to the left, and it (plus one) is the left end of the sublist to the right. If we can handle the edge cases, it leads to an approach where we find the indexes at which null elements occur, map them to sublists, and collect the sublists.
The resulting code is as follows:
static List<List<String>> splitStream(List<String> input) {
int[] indexes = Stream.of(IntStream.of(-1),
IntStream.range(0, input.size())
.filter(i -> input.get(i) == null),
IntStream.of(input.size()))
.flatMapToInt(s -> s)
.toArray();
return IntStream.range(0, indexes.length-1)
.mapToObj(i -> input.subList(indexes[i]+1, indexes[i+1]))
.collect(toList());
}
Getting the indexes at which null occurs is pretty easy. The stumbling block is adding -1 at the left and size at the right end. I've opted to use Stream.of to do the appending and then flatMapToInt to flatten them out. (I tried several other approaches but this one seemed like the cleanest.)
It's a bit more convenient to use arrays for the indexes here. First, the notation for accessing an array is nicer than for a List: indexes[i] vs. indexes.get(i). Second, using an array avoids boxing.
At this point, each index value in the array (except for the last) is one less than the beginning position of a sublist. The index to its immediate right is the end of the sublist. We simply stream over the array and map each pair of indexes into a sublist and collect the output.
Discussion
The streams approach is slightly shorter than the for-loop version, but it's denser. The for-loop version is familiar, because we do this stuff in Java all the time, but if you're not already aware of what this loop is supposed to be doing, it's not obvious. You might have to simulate a few loop executions before you figure out what prev is doing and why the open sublist has to be closed after the end of the loop. (I initially forgot to have it, but I caught this in testing.)
The streams approach is, I think, easier to conceptualize what's going on: get a list (or an array) that indicates the boundaries between sublists. That's an easy streams two-liner. The difficulty, as I mentioned above, is finding a way to tack the edge values onto the ends. If there were a better syntax for doing this, e.g.,
// Java plus pidgin Scala
int[] indexes =
[-1] ++ IntStream.range(0, input.size())
.filter(i -> input.get(i) == null) ++ [input.size()];
it would make things a lot less cluttered. (What we really need is array or list comprehension.) Once you have the indexes, it's a simple matter to map them into actual sublists and collect them into the result list.
And of course this is safe when run in parallel.
UPDATE 2016-02-06
Here's a nicer way to create the array of sublist indexes. It's based on the same principles, but it adjusts the index range and adds some conditions to the filter to avoid having to concatenate and flatmap the indexes.
static List<List<String>> splitStream(List<String> input) {
int sz = input.size();
int[] indexes =
IntStream.rangeClosed(-1, sz)
.filter(i -> i == -1 || i == sz || input.get(i) == null)
.toArray();
return IntStream.range(0, indexes.length-1)
.mapToObj(i -> input.subList(indexes[i]+1, indexes[i+1]))
.collect(toList());
}
UPDATE 2016-11-23
I co-presented a talk with Brian Goetz at Devoxx Antwerp 2016, "Thinking In Parallel" (video) that featured this problem and my solutions. The problem presented there is a slight variation that splits on "#" instead of null, but it's otherwise the same. In the talk, I mentioned that I had a bunch of unit tests for this problem. I've appended them below, as a standalone program, along with my loop and streams implementations. An interesting exercise for readers is to run solutions proposed in other answers against the test cases I've provided here, and to see which ones fail and why. (The other solutions will have to be adapted to split based on a predicate instead of splitting on null.)
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static java.util.Arrays.asList;
public class ListSplitting {
static final Map<List<String>, List<List<String>>> TESTCASES = new LinkedHashMap<>();
static {
TESTCASES.put(asList(),
asList(asList()));
TESTCASES.put(asList("a", "b", "c"),
asList(asList("a", "b", "c")));
TESTCASES.put(asList("a", "b", "#", "c", "#", "d", "e"),
asList(asList("a", "b"), asList("c"), asList("d", "e")));
TESTCASES.put(asList("#"),
asList(asList(), asList()));
TESTCASES.put(asList("#", "a", "b"),
asList(asList(), asList("a", "b")));
TESTCASES.put(asList("a", "b", "#"),
asList(asList("a", "b"), asList()));
TESTCASES.put(asList("#"),
asList(asList(), asList()));
TESTCASES.put(asList("a", "#", "b"),
asList(asList("a"), asList("b")));
TESTCASES.put(asList("a", "#", "#", "b"),
asList(asList("a"), asList(), asList("b")));
TESTCASES.put(asList("a", "#", "#", "#", "b"),
asList(asList("a"), asList(), asList(), asList("b")));
}
static final Predicate<String> TESTPRED = "#"::equals;
static void testAll(BiFunction<List<String>, Predicate<String>, List<List<String>>> f) {
TESTCASES.forEach((input, expected) -> {
List<List<String>> actual = f.apply(input, TESTPRED);
System.out.println(input + " => " + expected);
if (!expected.equals(actual)) {
System.out.println(" ERROR: actual was " + actual);
}
});
}
static <T> List<List<T>> splitStream(List<T> input, Predicate<? super T> pred) {
int[] edges = IntStream.range(-1, input.size()+1)
.filter(i -> i == -1 || i == input.size() ||
pred.test(input.get(i)))
.toArray();
return IntStream.range(0, edges.length-1)
.mapToObj(k -> input.subList(edges[k]+1, edges[k+1]))
.collect(Collectors.toList());
}
static <T> List<List<T>> splitLoop(List<T> input, Predicate<? super T> pred) {
List<List<T>> result = new ArrayList<>();
int start = 0;
for (int cur = 0; cur < input.size(); cur++) {
if (pred.test(input.get(cur))) {
result.add(input.subList(start, cur));
start = cur + 1;
}
}
result.add(input.subList(start, input.size()));
return result;
}
public static void main(String[] args) {
System.out.println("===== Loop =====");
testAll(ListSplitting::splitLoop);
System.out.println("===== Stream =====");
testAll(ListSplitting::splitStream);
}
}
The only solution I come up with for the moment is by implementing your own custom collector.
Before reading the solution, I want to add a few notes about this. I took this question more as a programming exercise, I'm not sure if it can be done with a parallel stream.
So you have to be aware that it'll silently break if the pipeline is run in parallel.
This is not a desirable behavior and should be avoided. This is why I throw an exception in the combiner part (instead of (l1, l2) -> {l1.addAll(l2); return l1;}), as it's used in parallel when combining the two lists, so that you have an exception instead of a wrong result.
Also this is not very efficient due to list copying (although it uses a native method to copy the underlying array).
So here's the collector implementation:
private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) {
final List<String> current = new ArrayList<>();
return Collector.of(() -> new ArrayList<List<String>>(),
(l, elem) -> {
if (sep.test(elem)) {
l.add(new ArrayList<>(current));
current.clear();
}
else {
current.add(elem);
}
},
(l1, l2) -> {
throw new RuntimeException("Should not run this in parallel");
},
l -> {
if (current.size() != 0) {
l.add(current);
return l;
}
);
}
and how to use it:
List<List<String>> ll = list.stream().collect(splitBySeparator(Objects::isNull));
Output:
[[a, b], [c], [d, e]]
As the answer of Joop Eggen is out, it appears that it can be done in parallel (give him credit for that!). With that it reduces the custom collector implementation to:
private static Collector<String, List<List<String>>, List<List<String>>> splitBySeparator(Predicate<String> sep) {
return Collector.of(() -> new ArrayList<List<String>>(Arrays.asList(new ArrayList<>())),
(l, elem) -> {if(sep.test(elem)){l.add(new ArrayList<>());} else l.get(l.size()-1).add(elem);},
(l1, l2) -> {l1.get(l1.size() - 1).addAll(l2.remove(0)); l1.addAll(l2); return l1;});
}
which let the paragraph about parallelism a bit obsolete, however I let it as it can be a good reminder.
Note that the Stream API is not always a substitute. There are tasks that are easier and more suitable using the streams and there are tasks that are not. In your case, you could also create a utility method for that:
private static <T> List<List<T>> splitBySeparator(List<T> list, Predicate<? super T> predicate) {
final List<List<T>> finalList = new ArrayList<>();
int fromIndex = 0;
int toIndex = 0;
for(T elem : list) {
if(predicate.test(elem)) {
finalList.add(list.subList(fromIndex, toIndex));
fromIndex = toIndex + 1;
}
toIndex++;
}
if(fromIndex != toIndex) {
finalList.add(list.subList(fromIndex, toIndex));
}
return finalList;
}
and call it like List<List<String>> list = splitBySeparator(originalList, Objects::isNull);.
It can be improved for checking edge-cases.
The solution is to use Stream.collect. To create a Collector using its builder pattern is already given as solution. The alternative is the other overloaded collect being a tiny bit more primitive.
List<String> strings = Arrays.asList("a", "b", null, "c", null, "d", "e");
List<List<String>> groups = strings.stream()
.collect(() -> {
List<List<String>> list = new ArrayList<>();
list.add(new ArrayList<>());
return list;
},
(list, s) -> {
if (s == null) {
list.add(new ArrayList<>());
} else {
list.get(list.size() - 1).add(s);
}
},
(list1, list2) -> {
// Simple merging of partial sublists would
// introduce a false level-break at the beginning.
list1.get(list1.size() - 1).addAll(list2.remove(0));
list1.addAll(list2);
});
As one sees, I make a list of string lists, where there always is at least one last (empty) string list.
The first function creates a starting list of string lists. It specifies the result (typed) object.
The second function is called to process each element. It is an action on the partial result and an element.
The third is not really used, it comes into play on parallelising the processing, when partial results must be combined.
A solution with an accumulator:
As #StuartMarks points out, the combiner does not fullfill the contract for parallelism.
Due to the comment of #ArnaudDenoyelle a version using reduce.
List<List<String>> groups = strings.stream()
.reduce(new ArrayList<List<String>>(),
(list, s) -> {
if (list.isEmpty()) {
list.add(new ArrayList<>());
}
if (s == null) {
list.add(new ArrayList<>());
} else {
list.get(list.size() - 1).add(s);
}
return list;
},
(list1, list2) -> {
list1.addAll(list2);
return list1;
});
The first parameter is the accumulated object.
The second function accumulates.
The third is the aforementioned combiner.
Please do not vote. I do not have enough place to explain this in comments.
This is a solution with a Stream and a foreach but this is strictly equivalent to Alexis's solution or a foreach loop (and less clear, and I could not get rid of the copy constructor) :
List<List<String>> result = new ArrayList<>();
final List<String> current = new ArrayList<>();
list.stream().forEach(s -> {
if (s == null) {
result.add(new ArrayList<>(current));
current.clear();
} else {
current.add(s);
}
}
);
result.add(current);
System.out.println(result);
I understand that you want to find a more elegant solution with Java 8 but I truly think that it has not been designed for this case. And as said by Mr spoon, highly prefer the naive way in this case.
Although the answer of Marks Stuart is concise, intuitive and parallel safe (and the best), I want to share another interesting solution that doesn't need the start/end boundaries trick.
If we look at the problem domain and think about parallelism, we can easy solve this with a divide-and-conquer strategy. Instead of thinking about the problem as a serial list we have to traverse, we can look at the problem as a composition of the same basic problem: splitting a list at a null value. We can intuitively see quite easily that we can recursively break down the problem with the following recursive strategy:
split(L) :
- if (no null value found) -> return just the simple list
- else -> cut L around 'null' naming the resulting sublists L1 and L2
return split(L1) + split(L2)
In this case, we first search any null value and the moment find one, we immediately cut the list and invoke a recursive call on the sublists. If we don't find null (the base case), we are finished with this branch and just return the list. Concatenating all the results will return the List we are searching for.
A picture is worth a thousand words:
The algorithm is simple and complete: we don't need any special tricks to handle the edge cases of the start/end of the list. We don't need any special tricks to handle edge cases such as empty lists, or lists with only null values. Or lists ending with null or starting with null.
A simple naive implementation of this strategy looks as follows:
public List<List<String>> split(List<String> input) {
OptionalInt index = IntStream.range(0, input.size())
.filter(i -> input.get(i) == null)
.findAny();
if (!index.isPresent())
return asList(input);
List<String> firstHalf = input.subList(0, index.getAsInt());
List<String> secondHalf = input.subList(index.getAsInt()+1, input.size());
return asList(firstHalf, secondHalf).stream()
.map(this::split)
.flatMap(List::stream)
.collect(toList());
}
We first search for the index of any null value in the list. If we don't find one, we return the list. If we find one, we split the list in 2 sublists, stream over them and recursively call the split method again. The resulting lists of the sub-problem are then extracted and combined for the return value.
Remark that the 2 streams can easily be made parallel() and the algorithm will still work because of the functional decomposition of the problem.
Although the code is already pretty concise, it can always be adapted in numerous ways. For the sake of an example, instead of checking the optional value in the base case, we could take advantage of the orElse method on the OptionalInt to return the end-index of the list, enabling us to re-use the second stream and additionally filter out empty lists:
public List<List<String>> split(List<String> input) {
int index = IntStream.range(0, input.size())
.filter(i -> input.get(i) == null)
.findAny().orElse(input.size());
return asList(input.subList(0, index), input.subList(index+1, input.size())).stream()
.map(this::split)
.flatMap(List::stream)
.filter(list -> !list.isEmpty())
.collect(toList());
}
The example is only given to indicate the mere simplicity, adaptability and elegance of a recursive approach. Indeed, this version would introduce a small performance penalty and fail if the input was empty (and as such might need an extra empty check).
In this case, recursion might probably not be the best solution (Stuart Marks algorithm to find indexes is only O(N) and mapping/splitting lists has a significant cost), but it expresses the solution with a simple, intuitive parallelizable algorithm without any side effects.
I won't digg deeper into the complexity and advantages/disadvantages or use cases with stop criteria and/or partial result availability. I just felt the need to share this solution strategy, since the other approaches were merely iterative or using an overly complex solution algorithm that was not parallelizable.
Here's another approach, which uses a grouping function, which makes use of list indices for grouping.
Here I'm grouping the element by the first index following that element, with value null. So, in your example, "a" and "b" would be mapped to 2. Also, I'm mapping null value to -1 index, which should be removed later on.
List<String> list = Arrays.asList("a", "b", null, "c", null, "d", "e");
Function<String, Integer> indexGroupingFunc = (str) -> {
if (str == null) {
return -1;
}
int index = list.indexOf(str) + 1;
while (index < list.size() && list.get(index) != null) {
index++;
}
return index;
};
Map<Integer, List<String>> grouped = list.stream()
.collect(Collectors.groupingBy(indexGroupingFunc));
grouped.remove(-1); // Remove null elements grouped under -1
System.out.println(grouped.values()); // [[a, b], [c], [d, e]]
You can also avoid getting the first index of null element every time, by caching the current min index in an AtomicInteger. The updated Function would be like:
AtomicInteger currentMinIndex = new AtomicInteger(-1);
Function<String, Integer> indexGroupingFunc = (str) -> {
if (str == null) {
return -1;
}
int index = names.indexOf(str) + 1;
if (currentMinIndex.get() > index) {
return currentMinIndex.get();
} else {
while (index < names.size() && names.get(index) != null) {
index++;
}
currentMinIndex.set(index);
return index;
}
};
Well, after a bit of work U have come up with a one-line stream-based solution. It ultimately uses reduce() to do the grouping, which seemed the natural choice, but it was a bit ugly getting the strings into the List<List<String>> required by reduce:
List<List<String>> result = list.stream()
.map(Arrays::asList)
.map(x -> new LinkedList<String>(x))
.map(Arrays::asList)
.map(x -> new LinkedList<List<String>>(x))
.reduce( (a, b) -> {
if (b.getFirst().get(0) == null)
a.add(new LinkedList<String>());
else
a.getLast().addAll(b.getFirst());
return a;}).get();
It is however 1 line!
When run with input from the question,
System.out.println(result);
Produces:
[[a, b], [c], [d, e]]
This is a very interesting problem. I came up with a one line solution. It might not very performant but it works.
List<String> list = Arrays.asList("a", "b", null, "c", null, "d", "e");
Collection<List<String>> cl = IntStream.range(0, list.size())
.filter(i -> list.get(i) != null).boxed()
.collect(Collectors.groupingBy(
i -> IntStream.range(0, i).filter(j -> list.get(j) == null).count(),
Collectors.mapping(i -> list.get(i), Collectors.toList()))
).values();
It is a similar idea that #Rohit Jain came up with. I'm grouping the space between the null values.
If you really want a List<List<String>> you may append:
List<List<String>> ll = cl.stream().collect(Collectors.toList());
Group by different token whenever you find a null (or separator). I used here a different integer (used atomic just as holder)
Then remap the generated map to transform it into a list of lists.
AtomicInteger i = new AtomicInteger();
List<List<String>> x = Stream.of("A", "B", null, "C", "D", "E", null, "H", "K")
.collect(Collectors.groupingBy(s -> s == null ? i.incrementAndGet() : i.get()))
.entrySet().stream().map(e -> e.getValue().stream().filter(v -> v != null).collect(Collectors.toList()))
.collect(Collectors.toList());
System.out.println(x);
Here is code by abacus-common
List<String> list = N.asList(null, null, "a", "b", null, "c", null, null, "d", "e");
Stream.of(list).splitIntoList(null, (e, any) -> e == null, null).filter(e -> e.get(0) != null).forEach(N::println);
Declaration: I'm the developer of abacus-common.
In my StreamEx library there's a groupRuns method which can help you to solve this:
List<String> input = Arrays.asList("a", "b", null, "c", null, "d", "e");
List<List<String>> result = StreamEx.of(input)
.groupRuns((a, b) -> a != null && b != null)
.remove(list -> list.get(0) == null).toList();
The groupRuns method takes a BiPredicate which for the pair of adjacent elements returns true if they should be grouped. After that we remove groups containing nulls and collect the rest to the List.
This solution is parallel-friendly: you may use it for parallel stream as well. Also it works nice with any stream source (not only random access lists like in some other solutions) and it's somewhat better than collector-based solutions as here you can use any terminal operation you want without intermediate memory waste.
With String one can do:
String s = ....;
String[] parts = s.split("sth");
If all sequential collections (as the String is a sequence of chars) had this abstraction this could be doable for them too:
List<T> l = ...
List<List<T>> parts = l.split(condition) (possibly with several overloaded variants)
If we restrict the original problem to List of Strings (and imposing some restrictions on it's elements contents) we could hack it like this:
String als = Arrays.toString(new String[]{"a", "b", null, "c", null, "d", "e"});
String[] sa = als.substring(1, als.length() - 1).split("null, ");
List<List<String>> res = Stream.of(sa).map(s -> Arrays.asList(s.split(", "))).collect(Collectors.toList());
(please don't take it seriously though :))
Otherwise, plain old recursion also works:
List<List<String>> part(List<String> input, List<List<String>> acc, List<String> cur, int i) {
if (i == input.size()) return acc;
if (input.get(i) != null) {
cur.add(input.get(i));
} else if (!cur.isEmpty()) {
acc.add(cur);
cur = new ArrayList<>();
}
return part(input, acc, cur, i + 1);
}
(note in this case null has to be appended to the input list)
part(input, new ArrayList<>(), new ArrayList<>(), 0)
I was watching the video on Thinking in Parallel by Stuart. So decided to solve it before seeing his response in the video. Will update the solution with time. for now
Arrays.asList(IntStream.range(0, abc.size()-1).
filter(index -> abc.get(index).equals("#") ).
map(index -> (index)).toArray()).
stream().forEach( index -> {for (int i = 0; i < index.length; i++) {
if(sublist.size()==0){
sublist.add(new ArrayList<String>(abc.subList(0, index[i])));
}else{
sublist.add(new ArrayList<String>(abc.subList(index[i]-1, index[i])));
}
}
sublist.add(new ArrayList<String>(abc.subList(index[index.length-1]+1, abc.size())));
});

Java. How to delete duplicate objects from both Lists

2nd question, which is continue of first.
I have got two Lists of strings. There is an List of strings (asu) - M1, M2, M3 ... As well as an List of string (rzs) - M1, M2, M3 and all possible combinations thereof. The need for each element (asu) (for example M1) to find an element in (rzs) (M1, M1M2, ..), which contains (e.g. M1). Example: took M1 from (asu) and will start search for duplicate(contain) in (rzs). We found M1M2 in (rzs), it contains M1. After that we should delete both elements from lists. Great thanks to No Idea For Name helped for modification this code. But the program always fails because AbstractList.remove error. Please help to implementation logic and tuning code!
Imports..........
public class work{
List<string> asu = Arrays.asList("M1","M1","M1","M3","M4","M5","M1","M1","M1","M4","M5","M5");
List<string> rzs = Arrays.asList("M1","M2","M3","M4","M5",
"M1M2","M1M3","M1M4","M1M5","M2M3","M2M4","M2M5","M3M4","M3M5","M4M5"
,"M1M2M3","M1M2M4","M1M2M5","M1M3M4","M1M3M4","M1M4M5","M2M4","M2M5");
public static void main(String[] args) {
work bebebe = new work();
bebebe.mywork();
}
List<string> tmp1 = new ArrayList<string>();
List<string> tmp2 = new ArrayList<string>();
System.out.println(Arrays.deepToString(rzs));
System.out.println(Arrays.deepToString(asu));
for (string curr : asu){
for (string currRzs : rzs){
System.out.println("New iteration ");
if (currRzs.contains(curr)) {
System.out.println("Element ("+curr+") in ASU =
element ("+currRzs+") in RZS");
if(tmp1.contains(curr) == false)
tmp1.add(curr);
if(tmp2.contains(currRzs) == false)
tmp2.add(currRzs);
}
}
}
for (string curr : tmp1){
asu.remove(curr);
}
for (string currRzs : tmp2){
rzs.remove(currRzs);
}
You should try to make use of removeAll() or retainAll() methods of Collection.
For example:
List<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("b");
aList.add("c");
aList.add("d");
aList.add("e");
List<String> bList = new ArrayList<String>();
bList.add("b");
bList.add("e");
bList.add("d");
aList.removeAll(bList);
will give you the "a" and "c" elements left in aList
While if you try to make use of retainAll() method:
aList.retainAll(bList);
will give you "b", "d" and "e" elements left in aList;
retainAll() is used to remove all the elements of the invoking collection which are not part of the given collection.
removeAll() is used to remove all the elements of a collection from another collection.
So, it all depends on your use-case.
EDIT
If in any case you want to remove some elements from these collections while iterating conditionally then you should first obtain the Iterator<Type> then call the remove() method over it.
Like:
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals('test')){
iterator.remove();
}
}
Don't remove items from list using foreach loop. Use classic for and iterate over elements, and when removing item, decrease iterator.
To safely remove elements while iterating use Iterator.remove method:
The behavior of an iterator is unspecified if the underlying
collection is modified while the iteration is in progress in any way
other than by calling this method.
Iterator<String> i = tmp1.iterator();
while (i.hasNext()) {
i.next(); // must be called before remove
i.remove();
}
Also it is easier to remove all collection from another by simply calling:
asu.removeAll(tmp1);
instead of List you can use Set, which will remove automatically the duplicate elements...
You can use removeAll() method to remove collection of elements from the list instead of removing one by one.
use
asu.removeAll(tmp1);
instead of
for (string curr : tmp1)
{
asu.remove(curr);
}
and use
rzs.removeAll(tmp2);
instead of
for (string currRzs : tmp2)
{
rzs.remove(currRzs);
}
update
I trace out your problem.The problem lies in Arrays.asList() method.
According to Arrays#asList
asList() returns "a fixed-size list backed by the specified array". If you want to resize the array, you have to create a new one and copy the old data. Then the list won't be backed by the same array instance.
So create a duplicate ArrayList for the lists.Like this
List<string> asuDuplicat = new ArrayList<string>(asu);
List<string> rzsDuplicat = new ArrayList<string>(rzs);
use asuDuplicat,rzsDuplicat.
asuDuplicat.removeAll(tmp1);
rzsDuplicat.removeAll(tmp2);

remove element from list of strings?

AH! I have a list of strings... and i just want to remove an element from the list if it is ""
but i keep causing the program to crash. How do i get around this? I made a list of arrays into a List of strings thinking i could remove it that way. Here is what i have:
List<String>list;
String[] f= new String[file.length()];
f= file.split("<");
list= Arrays.asList(f);
final Iterator<String> iter = list.iterator();
while (iter.hasNext())
{
final String temp = iter.next();
// TODO check for zero-length arrays
if (temp.equals(""))
{
iter.remove();
}
}
Error:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(Unknown Source)
at java.util.AbstractList$Itr.remove(Unknown Source)
at utilities.Validation.Validate(Validation.java:44)
After i convert it to a List, i can print the list just fine.. it's removing elements from it that becomes an problem...
The List returned by Arrays#asList has a fixed size. Since removeing an element would modify the length, it is unsupported. If you want a List that allows removal of items, use new ArrayList<>(Arrays.asList(array));.
The reason why you were getting the exception has already been answered, but I add a little note about your code: You can remove all empty strings from a list in a much simpler way using the removeAll() method. You don't even have to check if the given array is empty, because this method can handle them.
String[] array = { "aa", "", "cc", "", "d" };
List<String> list = new ArrayList<String>(Arrays.asList(array));
list.removeAll(Collections.singleton(""));
System.out.println(list);
Which prints:
[aa, cc, d]
Arrays.asList produces a list that doesn't allow adding or removing elements.
If there is a lot elements to remove then here is a quick way to do that:
List<String> result = new ArrayList<String>();
for (String next : file) {
if (next != null && next.length() != 0) result.add(next);
}

Why do I get an UnsupportedOperationException when trying to remove an element from a List?

I have this code:
public static String SelectRandomFromTemplate(String template,int count) {
String[] split = template.split("|");
List<String> list=Arrays.asList(split);
Random r = new Random();
while( list.size() > count ) {
list.remove(r.nextInt(list.size()));
}
return StringUtils.join(list, ", ");
}
I get this:
06-03 15:05:29.614: ERROR/AndroidRuntime(7737): java.lang.UnsupportedOperationException
06-03 15:05:29.614: ERROR/AndroidRuntime(7737): at java.util.AbstractList.remove(AbstractList.java:645)
How would be this the correct way? Java.15
Quite a few problems with your code:
On Arrays.asList returning a fixed-size list
From the API:
Arrays.asList: Returns a fixed-size list backed by the specified array.
You can't add to it; you can't remove from it. You can't structurally modify the List.
Fix
Create a LinkedList, which supports faster remove.
List<String> list = new LinkedList<String>(Arrays.asList(split));
On split taking regex
From the API:
String.split(String regex): Splits this string around matches of the given regular expression.
| is a regex metacharacter; if you want to split on a literal |, you must escape it to \|, which as a Java string literal is "\\|".
Fix:
template.split("\\|")
On better algorithm
Instead of calling remove one at a time with random indices, it's better to generate enough random numbers in the range, and then traversing the List once with a listIterator(), calling remove() at appropriate indices. There are questions on stackoverflow on how to generate random but distinct numbers in a given range.
With this, your algorithm would be O(N).
This one has burned me many times. Arrays.asList creates an unmodifiable list.
From the Javadoc: Returns a fixed-size list backed by the specified array.
Create a new list with the same content:
newList.addAll(Arrays.asList(newArray));
This will create a little extra garbage, but you will be able to mutate it.
Probably because you're working with unmodifiable wrapper.
Change this line:
List<String> list = Arrays.asList(split);
to this line:
List<String> list = new LinkedList<>(Arrays.asList(split));
The list returned by Arrays.asList() might be immutable. Could you try
List<String> list = new ArrayList<>(Arrays.asList(split));
I think that replacing:
List<String> list = Arrays.asList(split);
with
List<String> list = new ArrayList<String>(Arrays.asList(split));
resolves the problem.
Just read the JavaDoc for the asList method:
Returns a {#code List} of the objects
in the specified array. The size of
the {#code List} cannot be modified,
i.e. adding and removing are
unsupported, but the elements can be
set. Setting an element modifies the
underlying array.
This is from Java 6 but it looks like it is the same for the android java.
EDIT
The type of the resulting list is Arrays.ArrayList, which is a private class inside Arrays.class. Practically speaking, it is nothing but a List-view on the array that you've passed with Arrays.asList. With a consequence: if you change the array, the list is changed too. And because an array is not resizeable, remove and add operation must be unsupported.
The issue is you're creating a List using Arrays.asList() method with fixed Length
meaning that
Since the returned List is a fixed-size List, we can’t add/remove elements.
See the below block of code that I am using
This iteration will give an Exception Since it is an iteration list Created by asList() so remove and add are not possible, it is a fixed array
List<String> words = Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog");
for (String word : words) {
if ("sky".equals(word)) {
words.remove(word);
}
}
This will work fine since we are taking a new ArrayList we can perform modifications while iterating
List<String> words1 = new ArrayList<String>(Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog"));
for (String word : words) {
if ("sky".equals(word)) {
words.remove(word);
}
}
Arrays.asList() returns a list that doesn't allow operations affecting its size (note that this is not the same as "unmodifiable").
You could do new ArrayList<String>(Arrays.asList(split)); to create a real copy, but seeing what you are trying to do, here is an additional suggestion (you have a O(n^2) algorithm right below that).
You want to remove list.size() - count (lets call this k) random elements from the list. Just pick as many random elements and swap them to the end k positions of the list, then delete that whole range (e.g. using subList() and clear() on that). That would turn it to a lean and mean O(n) algorithm (O(k) is more precise).
Update: As noted below, this algorithm only makes sense if the elements are unordered, e.g. if the List represents a Bag. If, on the other hand, the List has a meaningful order, this algorithm would not preserve it (polygenelubricants' algorithm instead would).
Update 2: So in retrospect, a better (linear, maintaining order, but with O(n) random numbers) algorithm would be something like this:
LinkedList<String> elements = ...; //to avoid the slow ArrayList.remove()
int k = elements.size() - count; //elements to select/delete
int remaining = elements.size(); //elements remaining to be iterated
for (Iterator i = elements.iterator(); k > 0 && i.hasNext(); remaining--) {
i.next();
if (random.nextInt(remaining) < k) {
//or (random.nextDouble() < (double)k/remaining)
i.remove();
k--;
}
}
This UnsupportedOperationException comes when you try to perform some operation on collection where its not allowed and in your case, When you call Arrays.asList it does not return a java.util.ArrayList. It returns a java.util.Arrays$ArrayList which is an immutable list. You cannot add to it and you cannot remove from it.
I've got another solution for that problem:
List<String> list = Arrays.asList(split);
List<String> newList = new ArrayList<>(list);
work on newList ;)
Replace
List<String> list=Arrays.asList(split);
to
List<String> list = New ArrayList<>();
list.addAll(Arrays.asList(split));
or
List<String> list = new ArrayList<>(Arrays.asList(split));
or
List<String> list = new ArrayList<String>(Arrays.asList(split));
or (Better for Remove elements)
List<String> list = new LinkedList<>(Arrays.asList(split));
Yes, on Arrays.asList, returning a fixed-size list.
Other than using a linked list, simply use addAll method list.
Example:
String idList = "123,222,333,444";
List<String> parentRecepeIdList = new ArrayList<String>();
parentRecepeIdList.addAll(Arrays.asList(idList.split(",")));
parentRecepeIdList.add("555");
You can't remove, nor can you add to a fixed-size-list of Arrays.
But you can create your sublist from that list.
list = list.subList(0, list.size() - (list.size() - count));
public static String SelectRandomFromTemplate(String template, int count) {
String[] split = template.split("\\|");
List<String> list = Arrays.asList(split);
Random r = new Random();
while( list.size() > count ) {
list = list.subList(0, list.size() - (list.size() - count));
}
return StringUtils.join(list, ", ");
}
*Other way is
ArrayList<String> al = new ArrayList<String>(Arrays.asList(template));
this will create ArrayList which is not fixed size like Arrays.asList
Arrays.asList() uses fixed size array internally.
You can't dynamically add or remove from thisArrays.asList()
Use this
Arraylist<String> narraylist=new ArrayList(Arrays.asList());
In narraylist you can easily add or remove items.
Arraylist narraylist=Arrays.asList(); // Returns immutable arraylist
To make it mutable solution would be:
Arraylist narraylist=new ArrayList(Arrays.asList());
Following is snippet of code from Arrays
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* #serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
so what happens is that when asList method is called then it returns list of its own private static class version which does not override add funcion from AbstractList to store element in array. So by default add method in abstract list throws exception.
So it is not regular array list.
Creating a new list and populating valid values in new list worked for me.
Code throwing error -
List<String> list = new ArrayList<>();
for (String s: list) {
if(s is null or blank) {
list.remove(s);
}
}
desiredObject.setValue(list);
After fix -
List<String> list = new ArrayList<>();
List<String> newList= new ArrayList<>();
for (String s: list) {
if(s is null or blank) {
continue;
}
newList.add(s);
}
desiredObject.setValue(newList);

Categories

Resources