Get index in lambda foreach expression java 8 - java

I want to remove objects from the list on a certain filter and there are more than one objects.
list.stream().filter(g->g.getName().equalsIgnoreCase("String")).forEach(result ->{
/* is it possible to get the index of the result here?
.remove(), will iterate through the list again. I don't want that.
*/
list.remove(result);
});

There is no way to get an index at this point, but modifying the list you’re streaming over, is not supported anyway. You would likely get a ConcurrentModificationException when you try.
Use the dedicated API for this operation:
list.removeIf(g -> g.getName().equalsIgnoreCase("String"));
The alternative would be collecting the elements you want to keep into a new List:
List<String> result = list.stream()
.filter(g -> !g.getName().equalsIgnoreCase("String"))
.collect(Collectors.toList());

you can using Collection#removeIf instead, for example:
list.removeIf(g -> g.getName().equalsIgnoreCase("String"));

Sorry if can't help for you
list.stream().filter(g->g.getName().equalsIgnoreCase("String")).forEach(result ->{
list.indexOf(result);
});

Related

How do I simultaneously iterate through a list and a set while pairing them in another map (using stream)

What i've tried is creating an iterator for the list and using stream on the set as such
Set //some object which has a getId method
Iterator<String> iterator = list.iterator();
someSet.stream()
.map(Collectors.toMap(e -> e.getId(), e -> iterator.next() );
The Stream API is designed to work and iterate through one and only one collection and no more.
If you want to achieve such iteration called "zipping", as mentioned in the another answer, you have to iterate the indices. Since Set is not ordered, you have to use List instead and know the order is not predictable.
However, the usage of Stream API should be fine here:
Set<MyObject> set = ... // MyObject with getId method
List<MyObject> listFromSet = new ArrayList<>(set);
List<MyObject> list = ... // existing list
IntStream.range(0, Math.min(list.size(), listFromSet.size()))
.mapToObj(index -> new AbstractMap.SimpleEntry<>(
listFromSet.get(index).getId(), // key
list.get(index)) // value
)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)); // to Map
Few notes:
To know the highest number you can iterate through, you need ti find a lower from the sizes of the iterables: Math.min(list.size(), listFromSet.size()).
map(Collector.toMap(...)) doesn't convert a Stream to Map but is not a valid construct, moreover the method map is not a terminal operation. Use .collect(Collectors.toMap(...)) instead which is.
Not all the keys from set might be used, not all the values from list might be used, there is no guaranteed order of the keys and the matching key-value will be random.
If I were to implement this, I'd definetly go for the simple for-loop iteration over the Streams.
I think, what you wat to achieve is called "zip" in fuctional programming. This would be in Java to make a new stream from two existing streams by combining each of two corresponding elements of the given streams.
Look at this question to see how to do it:
Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)

Return specific object from an ArrayList

I have a ArrayList with thousands of properties with many variables(including city). I need access/return only the properties from a specific city e.g all properties from Surrey. How do i get them?
I know how to search for them, by doing city.values("Surrey"). But I do not know how output the values.
I assume you want to search in your AirbnbListing list. You can use Java Stream. Use the filter method for that:
List<AirbnbListing> matchingListings = listings.stream()
.filter(l -> "Surrey".equals(l.getCity()))
.collect(Collectors.toList());
If you want a list of all cities, you can use the map method:
List<String> matchingListings = listings.stream()
.map(l -> l.getCity())
.collect(Collectors.toList());
Additionally here is an official Java stream explanation tutorial.
If you are using Java 8 or higher there is following option:
list.stream().filter(x -> "Berlin".equals(x.getCity())); //This filters the list and returns a list with city = Berlin.
Hopefully this is what you are looking for.

Return List from forEach using Stream in java

I am trying to convert below for-loop to forEach method with help Stream function using Java1.8, but I was messed up and also confused to do that.
List<A> valueList = new ArrayList<>();
List<B> responseList = getResponses();
List<A> value = new ArrayList<>();
for (B getResponse: responseList) {
valueList = getValues(getResponse);
value.addAll(valueList);
}
With streams you generally want to avoid creating empty lists and then adding items. Streams should use functional idioms and avoid side effects as much as possible. It's better to work with the stream as a whole and then "collect" the results into a list at the end.
List<C> value = getResponses().stream()
.flatMap(r -> getValues(r).stream())
.collect(Collectors.toList());
I am trying to convert below for-loop to forEach method with help
Stream function using Java 1.8.
You shouldn't use a stream along with forEach simply to accumulate into a predefined list as there will be side effects (which should be avoided when dealing with streams), rather go with the stream approach suggested by John Kugelman if you want to perform it with streams or using the forEach method it can also be done as:
List<A> value = new ArrayList<>();
responseList.forEach(response -> value.addAll(getValues(response))));
It appears you are trying to add all the values in responseList to valueList and I think you could just do valueList.addAll(responseList); and not have to use a for loop at all.
You could have a problem though if type B doesn't inherit from A because you can't have a list of two unrelated types.

Using Java 8 Stream Reduce to return List after performing operation on each element using previous elements values

I'm new to Streams and Reduce so I'm trying it out and have hit a problem:
I have a list of counters which have a start counter and end counter. The startcounter of an item is always the endcounter of the previous. I have a list of these counters listItems which I want to loop through efficiently, filter out inactive records and then reduce the list into a new List where all the StartCounters are set. I have the following code:
List<CounterChain> active = listItems.stream()
.filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
.reduce(new ArrayList<CounterChain>(), (a,b) -> { b.setStartCounter(a.getEndCounter()); return b; });
But it doesn't really work and I'm kind of stuck, can anyone give me a few suggestions to help me get this working? Or is there an equally efficient better way to do this? Thanks!
A Reduction reduces all elements to a single value. Using a reduction function of the (a,b) -> b form will reduce all elements to the last one, so it’s not appropriate when you want to get a List containing all (matching) elements.
Besides that, you are performing a modification of the input value, which is violating the contract of that operation. Note further, that the function is required to be associative, i.e. it shouldn’t matter whether the stream will perform f(f(e₁,e₂),e₃)) or f(e₁,f(e₂,e₃)) when processing three subsequent stream elements with your reduction function.
Or, to put it in one line, you are not using the right tool for the job.
The cleanest solution is not to mix these unrelated operations:
List<CounterChain> active = listItems.stream()
.filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
.collect(Collectors.toList());
for(int ix=1, num=active.size(); ix<num; ix++)
active.get(ix).setStartCounter(active.get(ix-1).getEndCounter());
The second loop could also be implemented using forEach, but it would require an inner class due to its stateful nature:
active.forEach(new Consumer<CounterChain>() {
CounterChain last;
public void accept(CounterChain next) {
if(last!=null) next.setStartCounter(last.getEndCounter());
last = next;
}
});
Or, using an index based stream:
IntStream.range(1, active.size())
.forEach(ix -> active.get(ix).setStartCounter(active.get(ix-1).getEndCounter()));
But neither has much advantage over a plain for loop.
although the solution with plain for loop provided by #Holger is good enough, I would like to recommend you try third-party library for this kind of common issues. for example: StreamEx or JOOL. Here is solution by StreamEx.
StreamEx.of(listItems).filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
.scanLeft((a,b) -> { b.setStartCounter(a.getEndCounter()); return b; });

Java 8 Streams - Map Multiple Object of same type to a list using streams

Is it possible to do the below mentioned steps using streams in a better way ?
Set<Long> memberIds = new HashSet<>();
marksDistribution.parallelStream().forEach(marksDistribution -> {
memberIds.add(marksDistribution.getStudentId());
memberIds.add(marksDistribution.getTeacherId());
});
instanceDistribution.getStudentId() and instanceDistribution.getTeacherId() are both of type Long.
It might be possible that this kind of question is asked but I am not able to understand it. In simple yes or no. If yes/no, then how and bit explanation. And if possible kindly, discuss the efficiency.
Yes, you can use flatMap to map a single element of your Stream into a Stream of multiple elements, and then flatten them into a single Stream :
Set<Long> memberIds =
marksDistribution.stream()
.flatMap (marksDistribution -> Stream.of(marksDistribution.getStudentId(), marksDistribution.getTeacherId()))
.collect(Collectors.toSet());
You can use the 3-args version of collect:
Set<Long> memberIds =
marksDistribution.parallelStream()
.collect(HashSet::new,
(s, m) -> {
s.add(m.getStudentId());
s.add(m.getTeacherId());
}, Set::addAll);
Your current version may produce wrong results, since you are adding elements in parallel in a non-thread safe collection. So it may be possible that you have multiple times the same value in the set.

Categories

Resources