Practically I know ways to reduce duplicate trought distinct()
, or assign List
to Set
, but I have a little different issue.
How to solve smart way below problem in JAVA 8 using stream or may be StreamEx
?
Let's say we have a objects in List
A, A, A, B, B, A, A, A, C, C, C, A, A, B, B, A
Now I need
A, B, A, C, A, B, A
So duplicated was removed but only if appear as next, but should stay if next to then is different object. I tried a few solutions but ware ugly, and not readable.
Using Java 8 Stream. You can use the distinct() method from the Stream API. The distinct() method return a new Stream without duplicates elements based on the result returned by equals() method, which can be used for further processing.
To remove the duplicates from a list, you can make use of the built-in function set(). The specialty of the set() method is that it returns distinct elements.
You could write a stateful filter, but you should never do that, because it violates the contract of filter(Predicate<? super T> predicate)
:
predicate
- a non-interfering, stateless predicate to apply to each element to determine if it should be included
public class NoRepeatFilter<T> implements Predicate<T> {
private T prevValue;
@Override
public boolean test(T value) {
if (value.equals(this.prevValue))
return false;
this.prevValue = value;
return true;
}
}
Test
List<String> result = Stream
.of("A", "A", "A", "B", "B", "A", "A", "A", "C", "C", "C", "A", "A", "B", "B", "A")
// .parallel()
.filter(new NoRepeatFilter<>())
.collect(Collectors.toList());
System.out.println(result);
Output
[A, B, A, C, A, B, A]
The reason it must be stateless is that it'll fail if the stream is parallel, e.g. running test again with .parallel()
uncommented:
[A, A, B, B, A, C, C, C, A, B, B, A]
A valid solution is to create your own Collector
using of(...)
:
public class NoRepeatCollector {
public static <E> Collector<E, ?, List<E>> get() {
return Collector.of(ArrayList::new,
NoRepeatCollector::addNoRepeat,
NoRepeatCollector::combineNoRepeat);
}
private static <E> void addNoRepeat(List<E> list, E value) {
if (list.isEmpty() || ! list.get(list.size() - 1).equals(value))
list.add(value);
}
private static <E> List<E> combineNoRepeat(List<E> left, List<E> right) {
if (left.isEmpty())
return right;
if (! right.isEmpty())
left.addAll(left.get(left.size() - 1).equals(right.get(0))
? right.subList(1, right.size()) : right);
return left;
}
}
Test
List<String> result = Stream
.of("A", "A", "A", "B", "B", "A", "A", "A", "C", "C", "C", "A", "A", "B", "B", "A")
// .parallel()
.collect(NoRepeatCollector.get());
System.out.println(result);
Output (with and without .parallel()
)
[A, B, A, C, A, B, A]
If your input is a List
(or other Iterable
), you could remove repeating values using a simple loop:
public static <E> void removeRepeats(Iterable<E> iterable) {
E prevValue = null;
for (Iterator<E> iter = iterable.iterator(); iter.hasNext(); ) {
E value = iter.next();
if (value.equals(prevValue))
iter.remove();
else
prevValue = value;
}
}
Test
List<String> list = new ArrayList<>(Arrays.asList(
"A", "A", "A", "B", "B", "A", "A", "A", "C", "C", "C", "A", "A", "B", "B", "A"));
removeRepeats(list);
System.out.println(list);
Output
[A, B, A, C, A, B, A]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With