How to pass in Streams Prev element result to next one - java

I have List of items where on each item I need to create some calculation.
Each calculation is built by the preceding element.
So for example:
List<Object> Users=new ArrayList<>();
users.stream().filter(element->calculateSomething(<need-prev-element-input>).findFirst();
The calculateSomething will return true/false depends on the prev element calculation result in the stream
Any idea how can I do that?

Streams are not designed to be able to do any operation like this. You might be able to hack something together to do that, but it'll be awful; you should go back to using normal loops instead.

If you really want to use streams, stream over indexes:
IntStream.range(1, users.size())
.filter(i -> calculateSomething(users.get(i-1) , users.get(i)))
.map(users::get)
.findFirst();
There are also a number of non-standard libraries that let you stream over pairs from a list.

Here's the Java 8 way of doing it:
<T extends User> Optional<T> findUserSomehow(List<T> users) {
for (int idx = 1; idx < users.size(); ++idx)
if (calculateSomething(users.get(idx - 1)))
return Optional.of(users.get(idx));
return Optional.empty();
}

Related

indexing <Stream> data Java

need some help with indexing Stream data in Java. The context is that we need to manually set index for document that is embedded to other document (tldr; the output needs to be Stream in this method)
return Stream.concat(firstStream, secondStream) <- these need to be indexed
.sorted(// sorted using Comparator)
.forEach? .map? // the class has index field with getter and setter so I think need to do `setIndex(i)` but wasnt sure where to get 'i'
Any advice would be greatly appreciated!
If you can construct your streams yourself from lists, use IntStream of indices rather than Stream of objects.
IntStream.range(0, firstList.size()).forEach(i -> firstList.get(i).setIndex(i));
int offsetForSecondList = firstList.size();
IntStream.range(0, secondList.size())
.forEach(i -> secondList.get(i).setIndex(offsetForSecondList + i));
I have not tried to compile the code, so forgive any typo.
Otherwise your AtomicReference approach works too.
Assuming you have a class MyObject:
class MyObject{
int index;
String name;
//getters,setters,cons, toString...
}
Something like below may be a starting point:
public static Stream<MyObject> fooBar(){
//just for example, inorder to get the streams to be concatnated
List<MyObject> first = List.of(new MyObject("foo"),new MyObject("foo"),new MyObject("foo"));
List<MyObject> second = List.of(new MyObject("bar"),new MyObject("bar"),new MyObject("bar"));
AtomicInteger ai = new AtomicInteger(0);
return Stream.concat(first.stream(), second.stream())
.peek(myo -> myo.setIndex(ai.getAndIncrement()));
}

Change all position of elements in List using Java 8

I am a fresher in Java. I just want to change all position of elements in List using Java 8. For example:
public static List<Integer> change(List<Integer> data){
for (int i = 0; i < data.size(); i++) {
Collections.swap(data, i, data.size()-1-i);
}
return data;
}
this is a simple example, it just reverse the list. let me explain more :
i have a list {1,3,4,5,3,7,8,9} >> and i want to change to {3,1,5,4,7,3,9,8}.
But I want to do it in Java 8(Stream). My problem is:
1) How can I get the next element in a stream ?
2) Can I put my own method when I traverse with stream? (for example, I can write my own swap method?) - Like : list.foreach(doSomething());
3) And how can i return result when using foreach?
Try this :
List<String> list = Arrays.asList("A", "B", "C", "D", "1", "2", "3");
// shuffle or randomize
Collections.shuffle(list);
If you want to shuffle in such a way that no item is where it was before then you can simply use
Collections.reverse(list);
and then swap the 1st and the middle item if the length of the list is odd. This way all your items will be at a different index then they were before.
If using streams is required
public static List<Integer> change2( List<Integer> data ) {
return data.stream().collect( LinkedList::new, LinkedList::offerFirst, LinkedList::addAll );
}
The stream is reversed using a LinkedList to collect data ; offerFirst inserting next element in front.
Or with a custom function
final Random random = new Random();
return data.stream().collect(
LinkedList::new,
( list, elem ) -> {
int size = list.size();
list.add( random.nextInt( size + 1 ), elem );
},
LinkedList::addAll
);
You can use the "forEach" that Streams provide.
by saying "a->" you map each element of the data list to the "a" where is every element of the list
This is the best solution if you dont want just to shuffle them but do something more with each one of the elements of the list.
public static List<Integer> change(List<Integer> data){
return data.stream().forEach(a-> "whatever you want to do with them").collect(Collectors.toList());
}
One way is to drive your list through an IntStream and swap elements within forEach:
public static List<Integer> change(List<Integer> data) {
IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.forEach(i -> Collections.swap(data, i, i + 1));
return data;
}
You have to use Stream.limit so that the stream is not infinite.
However, using Collections.swap to mutate the list within forEach is discouraged because it has side-effects. If the IntStream were declared to be parallel, the code above would be broken. Ideally, the functions and consumers used in a stream should be stateless and without side-effects, as recommended in the java.util.stream package documentation.
Here's another way to achieve what you want, while also adhering to recommendations:
public static List<Integer> changeCorrectly(List<Integer> data) {
return IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.flatMap(i -> IntStream.of(data.get(i + 1), data.get(i)))
.boxed()
.collect(Collectors.toList());
}
Note: I'm using IntStream.flatMap to return an IntStream of swapped pairs, which has the effect to unbox Integer elements from the data list. Immediately, I'm boxing the IntStream to a Stream<Integer>.
The alternative to avoid this unboxing/boxing stuff is:
public static List<Integer> changeCorrectly(List<Integer> data) {
return IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.mapToObj(i -> Stream.of(data.get(i + 1), data.get(i)))
.flatMap(Function.identity())
.collect(Collectors.toList());
}
Final comment: this only works when the data list has an even size. Handling the addition of the last element when the data list has an odd size is left as an exercise.

filtering a stream against items in another list

trying to filter a stream against data within a different list:
It works, but I use a for loop in the middle of the stream. I cannot find any information of how to convert the for loop to a stream.
I could just .stream() the selction.getItems() than .forEach() and have a new .stream() of DATA.accounts, but that is poor code as it would have to restream on every .forEach.
y=1;
DATA.accounts.stream()
.flatMap(estimate -> estimate.getElements().stream())
.filter( ele-> {
// different list;
for (Element element:selection.getItems()){
if (element.getId()==ele.getId()){
return true;
}
}
return false;
})
.forEach(element -> {
element.setDateSchedualed(selectedDate);
element.setOrder(y);
y++;
});
I think what you really need is:
list1.removeAll(list2);
No streams involved though.
You can express the filter as
.filter(ele -> selection.getItems().stream()
.anyMatch(element -> element.getId()==ele.getId())
The fact that this “would have to restream” shouldn’t bother you more than the fact that the original code will loop for every element. You have created an operation with O(n×m) time complexity in either case. This is acceptable if you can surely predict that one of these lists will always be very small.
Otherwise, there is no way around preparing this operation by storing the id values in a structure with a fast (O(1) in the best case) lookup. I.e.
Set<IdType> id = selection.getItems().stream()
.map(element -> element.getId())
.collect(Collectors.toSet());
…
.filter(ele -> id.contains(ele.getId())
Besides that, your forEach approach incrementing the y variable clearly is an anti-pattern and it doesn’t even compile, when y is a local variable. And if y is a field, it would make this code even worse. Here, it’s much cleaner to accept a temporary storage into a List:
Set<IdType> id = selection.getItems().stream().map(element -> element.getId());
List<ElementType> list = DATA.accounts.stream()
.flatMap(estimate -> estimate.getElements().stream())
.filter(ele -> id.contains(ele.getId())
.collect(Collectors.toList());
IntStream.range(0, list.size())
.forEach(ix -> {
ElementType element = list.get(ix);
element.setDateSchedualed(selectedDate);
element.setOrder(ix+1);
});
Put the other list's IDs in a Set selectedIds, then filter based on ele-> selectedIds.contains(ele.getId()).
That will give you (amortized) linear time complexity.
Since you need to check presence among all elements in selected for each item in the stream, I don't expect there will be any straightforward method using only streams (because you cannot really stream the selected collection for this task).
I think there is actually nothing wrong with using a for-each loop if you want to search for the id in linear time, because for example if your items list was an ArrayList and you used its contains method for filtering, it would actually also just loop over the elements. You could write a general contains function like:
public static <E1, E2> boolean contains(Collection<E1> collection, E2 e2, BiPredicate<E1, E2> predicate){
for (E1 e1 : collection){
if (predicate.test(e1, e2)){
return true;
}
}
return false;
}
and replace your for-each loop with it:
ele -> contains(selection.getItems(), ele, (e1, e2) -> e1.getId() == e2.getId())

Removing values in an arraylist that DO NOT match a value

I am having some trouble with removing values that do not match a given value. At the moment I am copying over values to a new list and trying to clear the original list - but this is inefficient.
This is my code:
int size = list.size();
ArrayList<String> newList;
int count = 0;
newList = new ArrayList<>();
for (int i=0; i<list.size(); i++){
if(list.get(i).getForename().equals(forename)){
newList.add(i, list);
}
}
list.clear();
Is there a way where I can just remove an item in the arraylist if it does NOT match the name?
EDIT:
It works but then I might need a copy, as if I select a another name from the dropdown it will be referring to the old one
Thanks
A first thought would be to iterate on the list and as soon as you find an item not matching the value, you remove it. But it will create a Concurrent modification exception, as you iterate on list while trying to remove elements in it.
An other, still not efficient would be to iterate on the list, keep track of the indexes to remove, and after iterating on the list, remove them.
ArrayList<Integer> indexList = new ArrayList<Integer>();
for(int i = 0; i<list.size(); i++){
if(!list.get(i).getForename().equals(forename)){
indexList.add(i);
}
for(Integer index : indexList){
list.remove(index);
}
indexList.clear();
Please not that this is not really efficient too, but maybe you were looking for a way to delete from the same list.
A simple solution is
while (list.contains(value)) {
list.remove(list.indexOf(value));
}
Depending on what you want, you might want to use streams instead (seems to be what you actually want, since you don't really seem to want to delete elements in your list):
newList = list.stream()
.filter(e -> getForename().equals(forename))
.collect(Collectors.toList());
or to perform your action what you might want to do:
list.stream()
.filter(e -> getForename().equals(forename))
.forEach(person -> doStuff(person));
Another way would be using iterators to avoid conflicts with modifications during iteration:
ListIterator iterator = list.listIterator();
while(iterator.hasNext()){
if(!iterator.getNext().getForename().equals(forename))
iterator.remove();
}
EDIT: Since OP can't use lambdas and streams (because of Java-version), here is what nearly happens for the second stream (the forEach). I am not using the proper interfaces, since OP can't do so either. The difference to streams is, that they also might split this into several threads and hence would be faster (especially on multi-core processors and big lists):
interface Consumer<T>{ //this is normally given by the JAVA 8 API (which has one more default method)
void accept(T t);
}
Consumer<YourObject> doIt = new Consumer<YourObject>(){ //This is what the lambda expression actually does
#Override
public void accept(YourObject e) {
doStuff(e);
}
};
for(YourObject element : list){ //since JAVA 1.5. Alternativ your old for-loop with element=list.get(i);
if(!element.getForename().equals(forename)) //the filter written in easy
continue;
doIt.accept(element); //You could also use a method or expressions instead in this context.
//doStuff(element); //What actually the upper stream does.
}
You might want to look at the oracle tutorial (this chapter) to get a feeling, when this design is appropriate https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html (I have a strong feeling, you might want to use it).
Assuming your List contains String objects the following should be what you are looking for:
for (Iterator<String> it = list.iterator(); it.hasNext()){
String foreName = it.next();
if(forName != null && foreName.equals(forename)){
it.remove();
}
}
try
for (int i=0; i<list.size();){
if(!list.get(i).getForename().equals(forename)){
list.remove(i);
}
else {
i++;
}
}

Peek the next Element in a stream

Is there a way to peek the next element in a stream? The idea rose from a stream of a list of objects, where two following objects should be compared (to smooth some diffs, but that shouldn't matter here). As an old for loop this would look like:
List<Car> autobahn = getCars();
for (int i = 0; i < autobahn.size()-1; i++) {
if(autobahn.get(i).speed>autobahn.get(i+1).speed)
autobahn.get(i).honk();
}
The best way so far as stream would be:
autobahn.stream()
.limit(autobahn.size()-1)
.filter(car -> car.speed < autobahn.get(autobahn.indexOf(car)+1).speed)
.forEach(car -> car.honk());
The main-problem with this solution is the indexOf method, since there might be twice the same car on the autobahn. A better solution would be some way to peek the next (or the one before) element (with an helping class, this might be even possible, but looks horrible)
BoxedCar boxedCar = new BoxedCar(autobahn.get(0));
autobahn.stream()
.skip(1)
.filter(car -> boxedCar.setContent(car))
.forEach(car -> car.winTheRace());
with helperclass
class BoxedCar {
Car content;
BoxedCar(Car content) {
this.content = content;
}
boolean setContent(Car content) {
double speed = this.content.speed;
this.content = content;
return content.speed > speed;
}
}
or to divert the Stream<Car> into a kind of Stream<(Car,Car)> with the second stream somehow created by the first one (this sounds also awful and here I have no idea, how this would look).
Is there a nice way to do this with streams, or are we stuck to the for-loop?
Sticking with the for loop wouldn't be a bad idea. The Stream API isn't designed for this type of requirement. You can refer to that answer for more insight.
However, a simple way to do this using the Stream API would be to use a Stream over the indexes of your list, supposing that you have random access.
IntStream.range(0, autobahn.size() - 1)
.filter(i -> autobahn.get(i).speed > autobahn.get(i+1).speed)
.forEach(i -> autobahn.get(i).honk());
Note that this highly resemble the for loop.
Using my free StreamEx library:
StreamEx.of(autobahn)
.pairMap((car, nextCar) -> car.speed < nextCar.speed ? car : null)
.nonNull()
.forEach(Car::honk);
Here non-standard pairMap operation is used which can map the adjacent pair of elements to the single element. This works for any stream source (not only random-access indexed list) and can be parallelized pretty well.
what about using an IntStream instead of a loop:
IntStream.range(0, autobahn.size() - 1)
.filter(i -> autobahn.get(i).speed < autobahn.get(i + 1).speed)
.forEach(i -> autobahn.get(i).honk());

Categories