Nested for loop to lambda - Java - java

I'm trying to replace java for loop with a lambda.
I have at first a class Arrs:
public class Arrs {
private boolean isX;
public boolean isX() {
return isX;
}
public void setX(boolean x) {
isX = x;
}
}
Then I have a class called Example:
public class Example {
private Arrs[] arrs;
public Arrs[] getArrs() {
return arrs;
}
public void setArrs(Arrs[] arrs) {
this.arrs = arrs;
}
}
Then in my main I want to count the total times of true value of isX variable.
Using pure Java for-loop the code is:
int count = 0;
for(Example anExample : exampleList) {
for(Arrs anArray : anExample.getArrs()) {
if(anArray.isX()) {
count++;
}
}
}
With lambda I tried the following:
Stream<Object> x = a.map(anArray -> {
return Arrays.stream(anArray).filter(array -> array.isX()).count();
});
But it does not return the correct number of element.

Well, you could just use flatMap and filter:
long count = exampleList.stream()
.flatMap(example -> Arrays.stream(example.getArrs()))
.filter(Arrs::isX) // Arrs::isX == arr -> arr.isX()
.count();
First, we're streaming over the list of examples, and flat map each element to getArrs(). Flat mapping makes sure that all elements of all getArrs() are present in a single stream.
Then we just filter by the predicate arr.isX(). At last, we call count() on the stream, which returns the number of (remaining) elements.

same flatmap, but with reduce instead of filter+count:
exampleList.stream()
.flatMap(example -> Arrays.stream(example.getArrs()))
.reduce(0, (total, arr) -> arr.isX() ? total+1 : 0);
reduce is a nice little function that goes over a stream and does something to each element while "carrying over" a value.
more on the subject Here

You are not getting objects from exampleList. Here is a piece of code that you are trying to achieve. :)
long x = exampleList.stream().map(example-> {
return Arrays
.stream(example.getArrs())
.filter(array -> array.isX())
.count();
}).count();

Related

Create a stream that is based on a custom generator/iterator method

How can I create a Stream that creates a number of items based on a custom generate() method?
The question is different from the one referred to. The final result is a Stream, so I could (simplistically) use a ".forach( System.out::println)".
An example would be: Stream.generate( myGenerateMethod).forEach( System.out::println);
Or a simplistic example would be:
Stream<String> overallStream = Stream.generate( () -> {
if( generateCounter++ < 5) {
return "String-" + generateCounter;
}
// close the stream
return null;
});
overallStream.forEach( System.out::println) ;
UPDATE and SOLUTION: referred to answers often don't give a Stream. So reopening was better.
maxGenerateCounter = 6;
StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>() {
int counter = 0;
#Override
public boolean hasNext() {
return counter < maxGenerateCounter;
}
#Override
public String next() {
// do something
// check if the 'end' of the Stream is reached
counter++; // simplistically
if( counter > maxGenerateCounter) {
return null; // Not important answer
}
return "String-" + counter;
}
}, Spliterator.IMMUTABLE), false).forEach( System.out::println);
Thank you, developers!! You inspired me in finding the solution. Many thanks!
My problem was a bit complex, and simplifying let to a over simplified question.
As we can read the many solutions, it looks like Java and Streams is fun to solve!
Experimenting with many answers, this one works. It gives a fairly easy approach of getting a STREAM that easily can be controlled. No double checking of the criteria. I liked those anyXxx( ) answers giving insight!
maxGenerateCounter = 6;
System.out.println( "Using Splitter: ");
StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>() {
int counter = 0;
#Override
public boolean hasNext() {
// simplistic solution, see below for explanation
return counter < maxGenerateCounter;
}
#Override
public String next() {
// executing stuff
// providing info for 'stopping' the stream
counter++; // for simplicity
if( counter > maxGenerateCounter) {
return null; // this could be any answer. It will be filtered out.
}
return "String-" + counter;
}
}, Spliterator.IMMUTABLE), false).forEach( System.out::println);
Thank you, contributors, again!
You've answered your own question. Your snippet is exactly how you would do it. Note that Stream.generate(lambda) only works for endless streams (you can't mark that your stream has ended), hence why the javadoc of Stream.generate start with the text: "Returns an infinite sequential...".
You can then use limit to limit this. For example:
AtomicInteger counter = new AtomicInteger();
Stream<String> stream = Stream
.generate(() -> "String-" + count.getAndIncrement())
.limit(5)
;
Note that takeWhile can be useful so that your limiter can itself also be a lambda, e.g:
AtomicInteger counter = new AtomicInteger();
Stream<String> stream = Stream
.generate(() -> "String-" + count.getAndIncrement())
.takeWhile(count.get() < 5)
;
but takeWhile isn't in 8 (it is in 11 and up).
Another other alternative is to make your own spliterator but that's rather involved.
A third alternative is to make a custom collection and rely on its iteration/stream abilities:
class StringGenerator extends AbstractList<String> {
private final int size;
public StringGenerator(int size) { this.size = size; }
public int size() { return size; }
public String get(int idx) { return "String-" + idx; }
}
...
new StringGenerator(5).stream().forEach(System.out::println);
The more functional way to write this is:
IntStream.iterate(0, i -> i < 5, i -> i + 1)
.mapToObj(i -> "String-" + i)
.forEach(System.out::println);
Start at zero, keep producing elements while i < 5. For each step, add 1: i -> i + 1

Sort Array list of objects based on object attributes

I have list which contains a property class object, In the list i have 3 status
not_paid
paid
part_paid
I want to sort my list below mentioned order.
First - not_paid
second- part_paid
third -paid
How can I sort my list using Comparator class.?
public static Comparator<OrderHistoryItemData> COMPARE_BY_PAYMENT = new Comparator<OrderHistoryItemData>() {
public int compare(OrderHistoryItemData one, OrderHistoryItemData other) {
String p1 = one.getAttributes().getFieldPaymentStatus();
String p2 = other.getAttributes().getFieldPaymentStatus();
if (p1.equals(p2)) {
return 0;
}
if (p1.equals("not_paid") && (p2.equals("part_paid") || p2.equals("not_paid"))) {
return -1;
}
if (p1.equals("not_paid") && p2.equals("not_paid")) {
return -1;
}
return 1;
}
};
This is my Code. i am getting below order using this code.
paid-->not_paid-->part_paid
This is my Update Code. I got my result.
public static Comparator<OrderHistoryItemData> COMPARE_BY_PAYMENT = new Comparator<OrderHistoryItemData>() {
public int compare(OrderHistoryItemData one, OrderHistoryItemData other) {
String p1 = one.getAttributes().getFieldPaymentStatus();
String p2 = other.getAttributes().getFieldPaymentStatus();
if (p1.equals(p2)) {
return 0;
}
if (p1.equals("not_paid") && (p2.equals("part_paid") || p2.equals("paid"))) {
return -1;
}
if (p1.equals("part_paid") && p2.equals("paid")) {
return -1;
}
return 1;
}
};
To avoid complex comparator, I encourage you to export your statuses to an enum. (Plus this will work if you will add more statuses in the future, without the need to change logic in your comparator):
enum PaymentStatus { // Write them in order you want to be sorted
NOT_PAID,
PART_PAID,
PAID
}
Then sorting will be as simple as :
list.sort(Comparator.comparing(item ->item.getAttributes().getFieldPaymentStatus()));
What you can do is first mapping the strings to integers in the desired order, and then simply subtracting them from eachother.
private static Comparator<Payments> comparator = new Comparator<Payments>() {
// Use this mapping function to map the statuses to ints.
// The lowest number comes first
private int map(String str) {
switch (str) {
case "not_paid":
return 0;
case "part_paid":
return 1;
case "paid":
return 2;
default:
return 3;
}
}
// Alternatively, you can use the Map interface to define the sorting
// order.
#Override
public int compare(Payments o1, Payments o2) {
return map(o1.status) - map(o2.status);
}
};
I suggest – Schidu Luca already mentioned it in his answer – that you use enums to define a fixed set of known values, like payment statuses. This provides compile-time safety.
Note: I wouldn't, however, suggest to bind the enum declaration order to the sorting order.

Refactoring a nested foreach

private List getEnumFromType(List vars, List enums) {
List enumList = new ArrayList<>();
for (Bean.Var var : vars) {
String typeWithoutTypeIdentifierPrefix = var.getType().substring(1,var.getType().length());
for (Enum enumVal : enums) {
if (typeWithoutTypeIdentifierPrefix.equals(enumVal.getName())) {
if (!enumList.contains(enumVal)) {
enumList.add(enumVal);
}
}
}
}
return enumList;
}
You have chained two terminal stream operators.
.forEach() returns void, hence the second .forEach() complains that it can't find a stream to work with.
You may want to read some of the Java 8 Stream documentation before continuing.
Don't do this.
Don't get the idea that the Java 8 Stream API should be used every time you are looping through a collection. It's not a wildcard that you can use to replace all enhanced for loops, especially nested ones.
Your error occurs because you are trying to call forEach on the return value of forEach. Since your for loops are nested, the calls to forEach should also be nested in the stream version. The second for loop should be put in a place like this:
.forEach(countries -> countries.getFromZone().getCountries().stream().filter(country ->country.getCode().equals(selectedCountry).forEach(...))
But seriously, Don't do this.
Your code is very messy in the stream version. It is far less readable than the for loops, mainly because you have a nested for loop. Instead of trying to rewrite your code using streams, you should try to abstract out the logic of your current code. Extract some methods for example:
for (Rate rate : product.getrates()) {
if (rateMatches(value)) { // I tried to guess what you are trying to do here. If you have better names please use yours
for (Countrys countrys : rate.getFromCountry().getCountries()) {
if (countrys.getCode().equals(selectedCountry)) {
updateDisplay(value);
break;
}
}
}
}
This way it's much more clearer.
Don't complicate too much, think of it on simple terms. Keep in mind streams are also about making easier to follow code:
find all Rate/Countrys pairs that match your criteria
For each of them, update value accordingly.
Java streams approach (there are more alternatives):
public void yourMethod() {
X product = ...;
Y value = ...;
Z selectedCountry = ...;
if (product.getRates() == null || product.getRates().isEmpty()) {
return;
}
product.getRates().stream()
.filter(r -> matchesValueRate(r, value))
.flatMap(this::rateCountrysPairStream)
.filter(p -> matchesSelectedCountry(p, selectedCountry))
.forEach(p -> updateValue(p, v));
}
public boolean matchesValueRate(Rate candidate, Y value) {
return value.getAtrribute().getRateType().getCode().equalsIgnoreCase(candidate.getRateType().getCode()) && ...; // add your tzone filter also
}
public Stream<Pair<Rate, Countrys>> rateCountrysPairStream(Rate rate) {
return rate.getFromCountry().getCountries().stream().map(c -> Pair.of(rate, c));
}
public boolean matchesSelectedCountry(Pair<Rate, Countrys> candidate, Z selectedCountry) {
return selectedCountry.equals(candidate.second().getCode());
}
public void updateValue(Pair<Rate, Countrys> rateCountry, Y value) {
Rate rate = rateCountry.first();
Countrys country = rateCountry.second();
// do your display stuff here
}
public static class Pair<K, V> {
private final K first;
private final V second;
private Pair(K first, V second) {
this.first = first;
this.second = second;
}
public static <K, V> Pair<K, V> of(K first, V second) {
return new Pair<>(first, second);
}
public K first() {
return first;
}
public V second() {
return second;
}
}

using java streams in parallel with collect(supplier, accumulator, combiner) not giving expected results

I'm trying to find number of words in given string. Below is sequential algorithm for it which works fine.
public int getWordcount() {
boolean lastSpace = true;
int result = 0;
for(char c : str.toCharArray()){
if(Character.isWhitespace(c)){
lastSpace = true;
}else{
if(lastSpace){
lastSpace = false;
++result;
}
}
}
return result;
}
But, when i tried to 'parallelize' this with Stream.collect(supplier, accumulator, combiner) method, i am getting wordCount = 0. I am using an immutable class (WordCountState) just to maintain the state of word count.
Code :
public class WordCounter {
private final String str = "Java8 parallelism helps if you know how to use it properly.";
public int getWordCountInParallel() {
Stream<Character> charStream = IntStream.range(0, str.length())
.mapToObj(i -> str.charAt(i));
WordCountState finalState = charStream.parallel()
.collect(WordCountState::new,
WordCountState::accumulate,
WordCountState::combine);
return finalState.getCounter();
}
}
public class WordCountState {
private final boolean lastSpace;
private final int counter;
private static int numberOfInstances = 0;
public WordCountState(){
this.lastSpace = true;
this.counter = 0;
//numberOfInstances++;
}
public WordCountState(boolean lastSpace, int counter){
this.lastSpace = lastSpace;
this.counter = counter;
//numberOfInstances++;
}
//accumulator
public WordCountState accumulate(Character c) {
if(Character.isWhitespace(c)){
return lastSpace ? this : new WordCountState(true, counter);
}else{
return lastSpace ? new WordCountState(false, counter + 1) : this;
}
}
//combiner
public WordCountState combine(WordCountState wordCountState) {
//System.out.println("Returning new obj with count : " + (counter + wordCountState.getCounter()));
return new WordCountState(this.isLastSpace(),
(counter + wordCountState.getCounter()));
}
I've observed two issues with above code :
1. Number of objects (WordCountState) created are greater than number of characters in the string.
2. Result is always 0.
3. As per accumulator/consumer documentation, shouldn't the accumulator return void? Even though my accumulator method is returning an object, compiler doesn't complain.
Any clue where i might have gone off track?
UPDATE :
Used solution as below -
public int getWordCountInParallel() {
Stream<Character> charStream = IntStream.range(0, str.length())
.mapToObj(i -> str.charAt(i));
WordCountState finalState = charStream.parallel()
.reduce(new WordCountState(),
WordCountState::accumulate,
WordCountState::combine);
return finalState.getCounter();
}
You can always invoke a method and ignore its return value, so it’s logical to allow the same when using method references. Therefore, it’s no problem creating a method reference to a non-void method when a consumer is required, as long as the parameters match.
What you have created with your immutable WordCountState class, is a reduction operation, i.e. it would support a use case like
Stream<Character> charStream = IntStream.range(0, str.length())
.mapToObj(i -> str.charAt(i));
WordCountState finalState = charStream.parallel()
.map(ch -> new WordCountState().accumulate(ch))
.reduce(new WordCountState(), WordCountState::combine);
whereas the collect method supports the mutable reduction, where a container instance (may be identical to the result) gets modified.
There is still a logical error in your solution as each WordCountState instance starts with assuming to have a preceding space character, without knowing the actual situation and no attempt to fix this in the combiner.
A way to fix and simplify this, still using reduction, would be:
public int getWordCountInParallel() {
return str.codePoints().parallel()
.mapToObj(WordCountState::new)
.reduce(WordCountState::new)
.map(WordCountState::getResult).orElse(0);
}
public class WordCountState {
private final boolean firstSpace, lastSpace;
private final int counter;
public WordCountState(int character){
firstSpace = lastSpace = Character.isWhitespace(character);
this.counter = 0;
}
public WordCountState(WordCountState a, WordCountState b) {
this.firstSpace = a.firstSpace;
this.lastSpace = b.lastSpace;
this.counter = a.counter + b.counter + (a.lastSpace && !b.firstSpace? 1: 0);
}
public int getResult() {
return counter+(firstSpace? 0: 1);
}
}
If you are worrying about the number of WordCountState instances, note how many Character instances this solution does not create, compared to your initial approach.
However, this task is indeed suitable for mutable reduction, if you rewrite your WordCountState to a mutable result container:
public int getWordCountInParallel() {
return str.codePoints().parallel()
.collect(WordCountState::new, WordCountState::accumulate, WordCountState::combine)
.getResult();
}
public class WordCountState {
private boolean firstSpace, lastSpace=true, initial=true;
private int counter;
public void accumulate(int character) {
boolean white=Character.isWhitespace(character);
if(lastSpace && !white) counter++;
lastSpace=white;
if(initial) {
firstSpace=white;
initial=false;
}
}
public void combine(WordCountState b) {
if(initial) {
this.initial=b.initial;
this.counter=b.counter;
this.firstSpace=b.firstSpace;
this.lastSpace=b.lastSpace;
}
else if(!b.initial) {
this.counter += b.counter;
if(!lastSpace && !b.firstSpace) counter--;
this.lastSpace = b.lastSpace;
}
}
public int getResult() {
return counter;
}
}
Note how using int to represent unicode characters consistently, allows to use the codePoint() stream of a CharSequence, which is not only simpler, but also handles characters outside the Basic Multilingual Plane and is potentially more efficient, as it doesn’t need boxing to Character instances.
When you implemented stream().collect(supplier, accumulator, combiner) they do return void (combiner and accumulator). The problem is that this:
collect(WordCountState::new,
WordCountState::accumulate,
WordCountState::combine)
In your case actually means (just the accumulator, but same goes for the combiner):
(wordCounter, character) -> {
WordCountState state = wc.accumulate(c);
return;
}
And this is not trivial to get indeed. Let's say we have two methods:
public void accumulate(Character c) {
if (!Character.isWhitespace(c)) {
counter++;
}
}
public WordCountState accumulate2(Character c) {
if (Character.isWhitespace(c)) {
return lastSpace ? this : new WordCountState(true, counter);
} else {
return lastSpace ? new WordCountState(false, counter + 1) : this;
}
}
For the them the below code will work just fine, BUT only for a method reference, not for lambda expressions.
BiConsumer<WordCountState, Character> cons = WordCountState::accumulate;
BiConsumer<WordCountState, Character> cons2 = WordCountState::accumulate2;
You can imagine it slightly different, via an class that implementes BiConsumer for example:
BiConsumer<WordCountState, Character> clazz = new BiConsumer<WordCountState, Character>() {
#Override
public void accept(WordCountState state, Character character) {
WordCountState newState = state.accumulate2(character);
return;
}
};
As such your combine and accumulate methods needs to change to:
public void combine(WordCountState wordCountState) {
counter = counter + wordCountState.getCounter();
}
public void accumulate(Character c) {
if (!Character.isWhitespace(c)) {
counter++;
}
}
First of all, would it not be easier to just use something like input.split("\\s+").length to get the word count?
In case this is an exercise in streams and collectors, let's discuss your implementation. The biggest mistake was pointed out by you already: Your accumulator and combiner should not return new instances. The signature of collect tells you that it expects BiConsumer, which do not return anything. Because you create new object in the accumulator, you never increase the count of the WordCountState objects your collector actually uses. And by creating a new object in the combiner you would discard any progress you would have made. This is also why you create more objects than characters in your input: one per character, and then some for the return values.
See this adapted implementation:
public static class WordCountState
{
private boolean lastSpace = true;
private int counter = 0;
public void accumulate(Character character)
{
if (!Character.isWhitespace(character))
{
if (lastSpace)
{
counter++;
}
lastSpace = false;
}
else
{
lastSpace = true;
}
}
public void combine(WordCountState wordCountState)
{
counter += wordCountState.counter;
}
}
Here, we do not create new objects in every step, but change the state of the ones we have. I think you tried to create new objects because your Elvis operators forced you to return something and/or you couldn't change the instance fields as they are final. They do not need to be final, though, and you can easily change them.
Running this adapted implementation sequentially now works fine, as we nicely look at the chars one by one and end up with 11 words.
In parallel, though, it fails. It seems it creates a new WordCountState for every char, but does not count all of them, and ends up at 29 (at least for me). This shows a basic flaw with your algorithm: Splitting on every character doesn't work in parallel. Imagine the input abc abc, which should result in 2. If you do it in parallel and do not specify how to split the input, you might end up with these chunks: ab, c a, bc, which would add up to 4.
The problem is that by parallelizing between characters (i.e. in the middle of words), you make your separate WordCountStates dependent on each other (because they would need to know which one come before them and whether it ended with a whitespace char). This defeats the parallelism and results in errors.
Aside from all that, it might be easier to implement the Collector interface instead of providing the three methods:
public static class WordCountCollector
implements Collector<Character, SimpleEntry<AtomicInteger, Boolean>, Integer>
{
#Override
public Supplier<SimpleEntry<AtomicInteger, Boolean>> supplier()
{
return () -> new SimpleEntry<>(new AtomicInteger(0), true);
}
#Override
public BiConsumer<SimpleEntry<AtomicInteger, Boolean>, Character> accumulator()
{
return (count, character) -> {
if (!Character.isWhitespace(character))
{
if (count.getValue())
{
String before = count.getKey().get() + " -> ";
count.getKey().incrementAndGet();
System.out.println(before + count.getKey().get());
}
count.setValue(false);
}
else
{
count.setValue(true);
}
};
}
#Override
public BinaryOperator<SimpleEntry<AtomicInteger, Boolean>> combiner()
{
return (c1, c2) -> new SimpleEntry<>(new AtomicInteger(c1.getKey().get() + c2.getKey().get()), false);
}
#Override
public Function<SimpleEntry<AtomicInteger, Boolean>, Integer> finisher()
{
return count -> count.getKey().get();
}
#Override
public Set<java.util.stream.Collector.Characteristics> characteristics()
{
return new HashSet<>(Arrays.asList(Characteristics.CONCURRENT, Characteristics.UNORDERED));
}
}
We use a pair (SimpleEntry) to keep the count and the knowledge about the last space. This way, we do not need to implement the state in the collector itself or write a param object for it. You can use this collector like this:
return charStream.parallel().collect(new WordCountCollector());
This collector parallelizes nicer than the initial implementation, but still varies in results (mostly between 14 and 16) because of the mentioned weaknesses in your approach.

do...while() using Java 8 stream?

I want to convert this java do...while() to a Java 8.
private static final Integer PAGE_SIZE = 200;
int offset = 0;
Page page = null;
do {
// Get all items.
page = apiService.get(selector);
// Display items.
if (page.getEntries() != null) {
for (Item item : page.getEntries()) {
System.out.printf("Item with name '%s' and ID %d was found.%n", item.getName(),
item.getId());
}
} else {
System.out.println("No items were found.");
}
offset += PAGE_SIZE;
selector = builder.increaseOffsetBy(PAGE_SIZE).build();
} while (offset < page.getTotalNumEntries());
This code makes api call to apiService and retrieves data. Then, I want to loop until offset is less than totalNumberEntries.
What is prohibiting me from using while() or foreach with step or any other kind of loop loop is I don't know the totalNumberEntries without making API call (which is done inside the loop).
One option I can think of is making the API call just to get the totalNumberEntries and proceed with the loop.
If you really want/need a stream api for retrieving pages, you could create your own streams by implementing a Spliterator to retrieve each page in its tryAdvance() method.
It would look something like this
public class PageSpliterator implements Spliterator<Page> {
private static final Integer PAGE_SIZE = 200;
int offset;
ApiService apiService;
int selector;
Builder builder;
Page page;
public PageSpliterator(ApiService apiService) {
// initialize Builder?
}
#Override
public boolean tryAdvance(Consumer<? super Page> action) {
if (page == null || offset < page.getTotalNumEntries()) {
Objects.requireNonNull(action);
page = apiService.get(selector);
action.accept(page);
offset += PAGE_SIZE;
selector = builder.increaseOffsetBy(PAGE_SIZE).build();
return true;
} else {
// Maybe close/cleanup apiService?
return false;
}
}
#Override
public Spliterator<Page> trySplit() {
return null; // can't split
}
#Override
public long estimateSize() {
return Long.MAX_VALUE; // don't know in advance
}
#Override
public int characteristics() {
return IMMUTABLE; // return appropriate
}
}
Then you could use the it like this:
StreamSupport.stream(new PageSpliterator(apiService), false)
.flatMap(page -> page.getEntries()
.stream())
.forEach(item -> System.out.printf("Item with name '%s' and ID %d was found.%n", item.getName(), item.getId()));
In my opinion there are not many scenarios where a do...while loop would be the best choice. This however is such a scenario.
Just because there is new stuff in Java8, does not mean you have to use it.
If you still want to implement it with a foreach loop, for whatever reason, then I would go for the option you mentioned. Do the API call at the beginning and then start the foreach.

Categories