In previous question I asked previously Which FunctionalInterface should I use?
Now I was trying to add to List<Integer> and not just two Integers a and b, such that each index adds to the same index of another list.
I had previously
BinaryOperator<Integer> binaryOperator = Integer::sum;
for adding two integers using binaryOperator.apply(int a,int b). Is there a similar way like
BinaryOperator<List<Integer>> binaryOperator = List<Integer>::sum;
and then get the result in List<Integer> cList?
if you want to perform some computation on the elements at corresponding indices (in this specific case the summation) there's no need to use a BinaryOperator, instead use IntStream.range to generate the indices:
// generates numbers from 0 until list.size exclusive
IntStream.range(0, list.size())....
// generates numbers from 0 until the minimum of the two lists exclusive if needed
IntStream.range(0, Math.min(list.size(), list2.size()))....
The common name for this type of logic is "zip"; i.e. when given two input sequences it produces an output sequence in which every two elements from the input sequences at the same position are combined using some function.
There's no built-in method in the standard library for this, but you can find some generic implementations here.
for example, using the zip method in the accepted answer of the linked post, you could simply do:
List<Integer> result = zip(f.stream(), s.stream(), (l, r) -> l + r).collect(toList());
or using method reference:
List<Integer> result = zip(f.stream(), s.stream(), Math::addExact).collect(toList());
where f and s are your list of integers.
You can use IntStream.range() to iterate over elements, then mapToObj() to map them to their sum and the collect() them in the third list.
Given that your lists are of same size
List<Integer> first = List.of(); // initialised
List<Integer> second = List.of(); // initialised
you can get the third list as :
List<Integer> third = IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
.collect(Collectors.toList());
In terms of BinaryOperator, you can represent it as :
BinaryOperator<List<Integer>> listBinaryOperator = (a, b) -> IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
// OR from your existing code
// .mapToObj(i -> binaryOperator.apply(first.get(i), second.get(i)))
.collect(Collectors.toList());
Or you can make it more readable by abstracting out the logic into a method and using it as :
BinaryOperator<List<Integer>> listBinaryOperator = YourClass::sumOfList;
where sumOfList is defined as :
private List<Integer> sumOfList(List<Integer> first, List<Integer> second) {
return IntStream.range(0, first.size())
.mapToObj(i -> first.get(i) + second.get(i))
.collect(Collectors.toList());
}
What you can do is define your own utility method whose single task is zipping the two input lists :
<T> List<T> zip(final List<? extends T> first, final List<? extends T> second, final BinaryOperator<T> operation)
{
return IntStream.range(0, Math.min(first.size(), second.size()))
.mapToObj(index -> operation.apply(first.get(index), second.get(index)))
.collect(Collectors.toList());
}
This way, you can sum your two input lists as :
zip(first, second, Integer::sum)
Related
I’m trying to obtain a only duplicated numbers list from a list of integers:
final Set<Integer> setOfNmums = new HashSet<>();
Arrays.asList(5,6,7,7,7,6,2,4,2,4).stream()
.peek(integer -> System.out.println("XX -> " + integer))
.filter(n -> !setOfNmums.add(n))
.peek(System.out::println)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());
The output is 2,4,6,7,7
Expected : 2,4,6,7
I don’t understand how that’s happening.. is this running in parallel? how am I getting two '7'?
The hashset should return false if it exists and that used by the filter?
Yes I can use distinct, but I’m curious to know why would the filter fail.. is it being done in parallel?
Your filter rejects the first occurrence of each element and accepts all subsequent occurrences. Therefore, when an element occurs n times, you’ll add it n-1 times.
Since you want to accept all elements which occur more than once, but only accept them a single time, you could use .filter(n -> !setOfNmums.add(n)) .distinct() or you enhance the set to a map, to be able to accept an element only on its second occurrence.
Map<Integer, Integer> occurrences = new HashMap<>();
List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.filter(n -> occurrences.merge(n, 1, Integer::sum) == 2)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());
But generally, using stateful filters with streams is discouraged.
A cleaner solution would be
List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.collect(Collectors.collectingAndThen(
Collectors.toMap(String::valueOf, x -> true, (a,b) -> false, TreeMap::new),
map -> { map.values().removeIf(b -> b); return new ArrayList<>(map.keySet()); }));
Note that this approach doesn’t count the occurrences but only remembers whether an element is unique or has seen at least a second time. This works by mapping each element to true with the second argument to the toMap collector, x -> true, and resolving multiple occurrences with a merge function of (a,b) -> false. The subsequent map.values().removeIf(b -> b) will remove all unique elements, i.e. those mapped to true.
You can use .distinct() function in your stream check this out.
Since Holger already explained why your solution didn't work, I'll just provide an alternative.
Why not use Collections.frequency(collection, element) together with distinct()?
The solution would be quite simple(i apologize for the formatting, i just copied it from my ide and there doesn't seem to be an autoformat feature in SOF):
List<Integer> numbers = List.of(5, 6, 7, 7, 7, 6, 2, 4, 2, 4);
List<String> onlyDuplicates = numbers.stream()
.filter(n -> Collections.frequency(numbers, n) > 1)
.distinct()
.sorted()
.map(String::valueOf)
.toList();
This simply keeps all elements that occur more than once and then filters out the duplicates before sorting, converting each element to a string and collecting to a list since that seems to be what you want.
if you need a mutable list you can use collect(toCollection(ArrayList::new)) instead of toList()
Let's start with the following lists :
List<Double> firstList = new ArrayList<>();
firstList.add(2.0);
firstList.add(3.0);
List<Double> secondList = new ArrayList<>();
secondList .add(2.0000000001);
secondList .add(2.99999999994);
I know I can compare each element one by one using brute force. Of course, I already checked that both lists have the same number of elements.
boolean isEqual = true;
for (int i = 0; i < firstList.size(); i++) {
isEqual &= Math.abs(firstList.get(i) - secondList.get(i)) < 1.0e-6;
}
return isEqual;
My question : is there a way to compare these two lists of double values using lambda expressions? It seems to be easy with any other type of objects, but not with doubles. I need to check if the two lists are numerically equal.
Thanks in advance.
Given the statement:
Of course, I already checked that both lists have the same number of
elements.
Then you can accomplish the same result with IntStream.range along with allMatch like so:
boolean isEqual = firstList.isEmpty() ||
IntStream.range(0, firstList.size())
.allMatch(i -> Math.abs(firstList.get(i) - secondList.get(i)) < 1.0e-6);
Another solution using reduce:
BiPredicate<Double, Double> biPredicate = (e, a) -> Math.abs(e - a) < 1.0e-6;
boolean isEqual =
IntStream.range(0, firstList.size())
.boxed()
.reduce(Boolean.TRUE,
(accumulator, i) -> Boolean.logicalAnd(accumulator, biPredicate.test(firstList.get(i), secondList.get(i))),
(a, b) -> {
throw new RuntimeException("not implemented");
});
I've intentionally left the third parameter (the combiner) to reduce unimplemented as it will not be called at all. Reason being that this specific overload was designed to be used with a parallel stream, so in order for a combiner to work, a stream must be parallel.
Yet, it's still necessary for us to use this overload in order to accomplish the task at hand.
In functional languages you can use zip function to zip two lists (streams) and then use map or reduce to manipulate merged list using lambdas. Unfortunately Java doesn't have such function out-of-the-box. However, you can use Google Guava Streams to zip two lists and obtain result with lambda (functional style):
BiFunction<Double, Double, Boolean> zippingFunction = (a, b) -> Math.abs(a - b) < 1.0e-6;
boolean result = Streams.zip(firstList.stream(), secondList.stream(), zippingFunction)
.reduce(true, Boolean::logicalAnd);
Sure. Create an interface with a compare method that takes two List and returns boolean. Wrap your existing code in a lambda expression.
Let's say I have a list
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
How do I find "N4", I mean, how I find that the missing integer is 4?
What I've tried so far
Integer missingID = arr.stream().map(p -> Integer.parseInt(p.substring(1))).sorted()
.reduce((p1, p2) -> (p2 - p1) > 1 ? p1 + 1 : 0).get();
This doesn't work because reduce is not intended to work in the way I need in this situation, actually, I have no idea how do that.
If there's no missing number, than the next must be "N6" - or just 6 - (in this example)
It must be done with java standard stream's library, no use of third parties.
The algorithm to implement here is based from this one: to find the missing number in a sequence of integers, the trick is to:
calculate the sum of the elements in the sequence.
calculate the sum of the elements the sequence would have with the missing number: this is easy to do since we can determine the minimum, the maximum and we know that the sum from a sequence of integer going from min to max is max*(max+1)/2 - (min-1)*min/2.
find the difference between those two sums: that's our missing number
In this case, we can collect statistics on our Stream by first mapping to an IntStream formed by only the numbers themselves and then calling summaryStatistics(). This returns a IntSummaryStatistics that has all the values we want: min, max and sum:
public static void main(String[] args) {
List<String> arr = Arrays.asList("N3", "N7", "N4", "N5", "N2");
IntSummaryStatistics statistics =
arr.stream()
.mapToInt(s -> Integer.parseInt(s.substring(1)))
.summaryStatistics();
long max = statistics.getMax();
long min = statistics.getMin();
long missing = max*(max+1)/2 - (min-1)*min/2 - statistics.getSum();
System.out.println(missing); // prints "6" here
}
If there is no missing number, this will print 0.
Here's the solution involving the pairMap operation from my free StreamEx library. It prints all the missing elements of the sorted input:
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
StreamEx.of(arr).map(n -> Integer.parseInt(n.substring(1)))
.pairMap((a, b) -> IntStream.range(a+1, b))
.flatMapToInt(Function.identity())
.forEach(System.out::println);
The pairMap operation allows you to map every adjacent pair of the stream to something else. Here we map them to the streams of the skipped numbers, then flatten these streams.
The same solution is possible without third-party library, but looks more verbose:
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
IntStream.range(0, arr.size()-1)
.flatMap(idx -> IntStream.range(
Integer.parseInt(arr.get(idx).substring(1))+1,
Integer.parseInt(arr.get(idx+1).substring(1))))
.forEach(System.out::println);
If there's only ONE missing number in the array, and if all numbers are positive, you could use the XOR algorithm, as explained in this question and its answers:
List<String> list = Arrays.asList("N5", "N2", "N3", "N6");
int xorArray = list.stream()
.mapToInt(p -> Integer.parseInt(p.substring(1)))
.reduce(0, (p1, p2) -> p1 ^ p2);
int xorAll = IntStream.rangeClosed(2, 6)
.reduce(0, (p1, p2) -> p1 ^ p2);
System.out.println(xorArray ^ xorAll); // 4
The advantage of this approach is that you don't need to use extra data structures, all you need is a couple of ints.
EDIT as per #Holger's comments below:
This solution requires you to know the range of the numbers in advance. Although on the other hand, it doesn't require the list and stream to be sorted.
Even if the list wasn't sorted, you could still get min and max (hence, the range) with IntSummaryStatistics, but this would require an extra iteration.
You could create a state object which is used to transform a single input stream into multiple streams of missing entries. These missing entry streams can then be flat mapped to produce a single output:
public class GapCheck {
private String last;
public GapCheck(String first) {
last = first;
}
public Stream<String> streamMissing(String next) {
final int n = Integer.parseInt(next.replaceAll("N", ""));
final int l = Integer.parseInt(last.replaceAll("N", ""));
last = next;
return IntStream.range(l + 1, n).mapToObj(Integer::toString);
}
}
Usage:
final List<String> arr = new ArrayList(Arrays.asList("N1", "N3", "N5"));
arr.stream()
.flatMap(new GapCheck(arr.get(0))::streamMissing)
.forEach(System.out::println);
output:
2
4
This is more work than you might expect, but it can be done with a collect call.
public class Main {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>(Arrays.asList("N1", "N2", "N3", "N5", "N7", "N14"));
Stream<Integer> st = arr.stream().map(p -> Integer.parseInt(p.substring(1))).sorted();
Holder<Integer> holder = st.collect(() -> new Holder<Integer>(),
(h, i) -> {
Integer last = h.getProcessed().isEmpty() ? null : h.getProcessed().get(h.getProcessed().size() - 1);
if (last != null) {
while (i - last > 1) {
h.getMissing().add(++last);
}
}
h.getProcessed().add(i);
},
(h, h2) -> {});
holder.getMissing().forEach(System.out::println);
}
private static class Holder<T> {
private ArrayList<T> processed;
private ArrayList<T> missing;
public Holder() {
this.processed = new ArrayList<>();
this.missing = new ArrayList<>();
}
public ArrayList<T> getProcessed() {
return this.processed;
}
public ArrayList<T> getMissing() {
return this.missing;
}
}
}
This prints
4
6
8
9
10
11
12
13
Note that this sort of thing isn't really a particularly strong fit for Streams. All of the stream processing methods will tend to pass you each item exactly one time, so you need to handle all runs of missing numbers at once, and in the end, you're writing kind of a lot of code to avoid just writing a loop.
Here is one solution using pure streams, albeit not very efficient.
public void test() {
List<String> arr = new ArrayList(
Arrays.asList("N1", "N2", "N3", "N5", "N7"));
List<Integer> list = IntStream
.range(1, arr.size())
.mapToObj(t -> new AbstractMap.SimpleEntry<Integer, Integer>(
extract(arr, t), extract(arr, t) - extract(arr, t - 1)))
.filter(t -> t.getValue() > 1)
.map(t -> t.getKey() - 1)
.collect(Collectors.toList());
System.out.println(list);
}
private int extract(List<String> arr, int t) {
return Integer.parseInt(arr.get(t).substring(1));
}
Major performance block will be because of repeated parsing of list elements. However, this solution will be able to provide all missing numbers.
Given a list of elements, I want to get the element with a given property and remove it from the list. The best solution I found is:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Is it possible to combine get and remove in a lambda expression?
To Remove element from the list
objectA.removeIf(x -> conditions);
eg:
objectA.removeIf(x -> blockedWorkerIds.contains(x));
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
str1.removeIf(x -> str2.contains(x));
str1.forEach(System.out::println);
OUTPUT:
A
B
C
Although the thread is quite old, still thought to provide solution - using Java8.
Make the use of removeIf function. Time complexity is O(n)
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
API reference: removeIf docs
Assumption: producersProcedureActive is a List
NOTE: With this approach you won't be able to get the hold of the deleted item.
Consider using vanilla java iterators to perform the task:
public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
T value = null;
for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
if (test.test(value = it.next())) {
it.remove();
return value;
}
return null;
}
Advantages:
It is plain and obvious.
It traverses only once and only up to the matching element.
You can do it on any Iterable even without stream() support (at least those implementing remove() on their iterator).
Disadvantages:
You cannot do it in place as a single expression (auxiliary method or variable required)
As for the
Is it possible to combine get and remove in a lambda expression?
other answers clearly show that it is possible, but you should be aware of
Search and removal may traverse the list twice
ConcurrentModificationException may be thrown when removing element from the list being iterated
The direct solution would be to invoke ifPresent(consumer) on the Optional returned by findFirst(). This consumer will be invoked when the optional is not empty. The benefit also is that it won't throw an exception if the find operation returned an empty optional, like your current code would do; instead, nothing will happen.
If you want to return the removed value, you can map the Optional to the result of calling remove:
producersProcedureActive.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.map(p -> {
producersProcedureActive.remove(p);
return p;
});
But note that the remove(Object) operation will again traverse the list to find the element to remove. If you have a list with random access, like an ArrayList, it would be better to make a Stream over the indexes of the list and find the first index matching the predicate:
IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int) i));
With this solution, the remove(int) operation operates directly on the index.
Use can use filter of Java 8, and create another list if you don't want to change the old list:
List<ProducerDTO> result = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.collect(Collectors.toList());
I'm sure this will be an unpopular answer, but it works...
ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0] will either hold the found element or be null.
The "trick" here is circumventing the "effectively final" problem by using an array reference that is effectively final, but setting its first element.
With Eclipse Collections you can use detectIndex along with remove(int) on any java.util.List.
List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
If you use the MutableList type from Eclipse Collections, you can call the detectIndex method directly on the list.
MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
integers.remove(index);
}
Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
Note: I am a committer for Eclipse Collections
The below logic is the solution without modifying the original list
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");
List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");
List<String> str3 = str1.stream()
.filter(item -> !str2.contains(item))
.collect(Collectors.toList());
str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]
When we want to get multiple elements from a List into a new list (filter using a predicate) and remove them from the existing list, I could not find a proper answer anywhere.
Here is how we can do it using Java Streaming API partitioning.
Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
.stream()
.collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));
// get two new lists
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);
// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);
This way you effectively remove the filtered elements from the original list and add them to a new list.
Refer the 5.2. Collectors.partitioningBy section of this article.
As others have suggested, this might be a use case for loops and iterables. In my opinion, this is the simplest approach. If you want to modify the list in-place, it cannot be considered "real" functional programming anyway. But you could use Collectors.partitioningBy() in order to get a new list with elements which satisfy your condition, and a new list of those which don't. Of course with this approach, if you have multiple elements satisfying the condition, all of those will be in that list and not only the first.
the task is: get ✶and✶ remove element from list
p.stream().collect( Collectors.collectingAndThen( Collector.of(
ArrayDeque::new,
(a, producer) -> {
if( producer.getPod().equals( pod ) )
a.addLast( producer );
},
(a1, a2) -> {
return( a1 );
},
rslt -> rslt.pollFirst()
),
(e) -> {
if( e != null )
p.remove( e ); // remove
return( e ); // get
} ) );
resumoRemessaPorInstrucoes.removeIf(item ->
item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.PEDIDO_PROTESTO.getNome() ||
item.getTipoOcorrenciaRegistro() == TipoOcorrenciaRegistroRemessa.SUSTAR_PROTESTO_BAIXAR_TITULO.getNome());
Combining my initial idea and your answers I reached what seems to be the solution
to my own question:
public ProducerDTO findAndRemove(String pod) {
ProducerDTO p = null;
try {
p = IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.get();
logger.debug(p);
} catch (NoSuchElementException e) {
logger.error("No producer found with POD [" + pod + "]");
}
return p;
}
It lets remove the object using remove(int) that do not traverse again the
list (as suggested by #Tunaki) and it lets return the removed object to
the function caller.
I read your answers that suggest me to choose safe methods like ifPresent instead of get but I do not find a way to use them in this scenario.
Are there any important drawback in this kind of solution?
Edit following #Holger advice
This should be the function I needed
public ProducerDTO findAndRemove(String pod) {
return IntStream.range(0, producersProcedureActive.size())
.filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
.boxed()
.findFirst()
.map(i -> producersProcedureActive.remove((int)i))
.orElseGet(() -> {
logger.error("No producer found with POD [" + pod + "]");
return null;
});
}
A variation of the above:
import static java.util.function.Predicate.not;
final Optional<MyItem> myItem = originalCollection.stream().filter(myPredicate(someInfo)).findFirst();
final List<MyItem> myOtherItems = originalCollection.stream().filter(not(myPredicate(someInfo))).toList();
private Predicate<MyItem> myPredicate(Object someInfo) {
return myItem -> myItem.someField() == someInfo;
}
I'm interested in identifying an approach that returns a list of elements excluding the elements in another list.
for example
List<Integer> multiplesOfThree = ... // 3,6,9,12 etc
List<Integer> evens = ... // 2,4,6,8 etc
List<Integer> others = multiplesOfThree.except(evens) // should return a list of elements that are not in the other list
how do you do this?
i found an approach that's a bit clunky and difficult to read....
multiplesOfThree.stream()
.filter(intval -> evens.stream().noneMatch(even -> even.intValue() == intval.intValue()))
You can use Stream's filter method, passing a Predicate that ensures that the element doesn't exist in evens.
List<Integer> others = multiplesOfThree.stream()
.filter(i -> !evens.contains(i))
.collect(Collectors.toList());
But assuming you have a mutable List (e.g. ArrayList), you don't even need streams, just Collections's removeAll method.
multiplesOfThree.removeAll(evens);
You could use
multipleOfThree.stream()
.filter(((Predicate<Integer>) evens::contains).negate())
or more efficient for big even lists
HashSet<Integer> evenSet = new HashSet<>(even);
multipleOfThree.stream()
.filter(((Predicate<Integer>) evenSet::contains).negate())
There are a few solutions.
First, without using streams, you can just create a new list and remove all elements from another collection from it...
final List<Integer> multiplesOfThree = Arrays.asList(3,6,9,12);
final List<Integer> evens = Arrays.asList(2,4,6,8,10,12);
final List<Integer> others1 = new ArrayList<>(multiplesOfThree);
others1.removeAll(evens);
Another solution would be to pass the stream through a filter():
final List<Integer> others2 = multiplesOfThree
.stream()
.filter(x -> !evens.contains(x))
.collect(Collectors.toList());
(You may want to consider making evens a Set in this case).
And finally, you could modify the logic above to represent the "evens" as a function rather than a collection of all even numbers. This is essentially the same as above, but you don't have to have a second collection.
final List<Integer> others3 = multiplesOfThree
.stream()
.filter(x -> x % 2 != 0)
.collect(Collectors.toList());