continue doesn't work when using streams and the map - java

I have this simple code where I use a stream and a .map() function.
I do a null check for the id, and inside it a add a continue
The continue gives me an error: Continue outside of loop
When I remove the continue I don't get an error, but I don't know if the behaviour is the same?
public List<Long> getIds(final Long[][] value){
List<Long> list = Arrays.stream(value).map(result ->{
final Long id = result[1];
if(id == null){
continue; // This part doesn't work (error: Continue outside of loop)
}
return id;
}).collect(Collectors.toList());
}
Any suggestion on why this happens with .streams? Whereas, when I don't use the stream I can use continue.
The question has been marked as duplicate, but it's not the case. Using return surely works in forEach, where no return type is requested, but not in map.

continue works in a for loop. You can use flatMap as a workaround:
List<Long> list = Arrays.stream(value).flatMap(result ->{
final Long id = result[1];
return Stream.ofNullable(id);
}).collect(Collectors.toList());
You can also make it more concise by using Stream.ofNullable directly as #Naman suggests:
List<Long> list = Arrays.stream(value)
.flatMap(result -> Stream.ofNullable(result[1]))
.collect(Collectors.toList());
The other, more elegant version of my firstly proposed method by #Holger would be to use the predicate in the filter:
List<Long> list = Arrays.stream(value)
.map(result -> result[1])
.filter(Objects::nonNull)
.collect(Collectors.toList());

Related

How to create a Supplier from another Supplier and filter out some items?

Suppose Supplier<Optional<Item>> is the source and Supplier<Item> is what we want: the same items without Optional.Empty cases.
Inspired by Boris and khelwood:
Supplier<Item> filtered = Stream.generate(optSupplier)
.flatMap(Optional::stream)
.iterator()::next;
If you're pre-Java 9, replace flatMap with .filter(Optional::isPresent).map(Optional::get).
Given Supplier<Optional<Item>> optSupplier:
Supplier<Item> itemSupplier = () -> {
Optional<Item> opt = optSupplier.get();
while (!opt.isPresent()) {
opt = optSupplier.get();
}
return opt.get();
};
When you try to get from the item supplier, it will keep pulling from the optional supplier until it gets a result that isn't empty, and then return the item.
(Caveat: If the optional supplier always returns an empty optional, then the item supplier will be stuck in an infinite loop.)

Java 8 Filter List of POJO based on a condition

I need to extract a sublist where one of the attribute in list of POJO matches with the input at first occurrence.
class POJO {
String name;
#Nullable
String location;
}
Given a list of POJO, I need to get the List where location matches to input location. Once we find the first occurrence of the location, We need to extract the list from that point to end of list.
List<POJO> ans = Lists.newArrayList();
Iterator<POJO> iterator = input
.iterator();
while (iterator.hasNext()) {
POJO pojo = iterator.next();
if (Objects.nonNull(pojo.location)) {
String location = pojo.location
//Got the first location matching with input, From here, Get all the elements from List
if (inputLocation.equals(location) {
ans.add(pojo);
break;
}
}
}
while (iterator.hasNext()) {
POJO pojo = iterator.next();
if (Objects.nonNull(pojo.location)) {
ans.add(pojo);
}
}
Can anyone suggest any better apporach? (if possible using Streams)?
If you're on Java 9+, you can use dropWhile:
List<POJO> ans = input.stream().dropWhile(pojo -> pojo.location == null
|| !inputLocation.equals(pojo.location))
.filter(loc -> loc.location != null)
.collect(Collectors.toList());
On Java 8, you'd have to do it in two steps: find the index of your element and then pull the sub-list. This combines the two in one statement:
List<POJO> ans = input.subList(IntStream.range(0, input.size())
.filter(i -> input.get(i).location != null &&
input.get(i).location.equals("inputLocation"))
.findFirst()
.orElseGet(input::size), input.size());
Just be aware of subList's behavior. If you want to break the link from input, you may want to construct a new list from the result.
You can get the index of element using IntStream like:
int index = IntStream.range(0, answers.size())
.filter(i -> inputLocation.equals(answers.get(i).getLocation())).findFirst().orElse(-1);
Then extract a sub list using above index like below if index is greater than -1 :
answers.subList(index, answers.size())
You are looking for filter and dropWhile operations:
List<Pojo> list = List.of(new Pojo("a","USA"),new Pojo("b","UK"),
new Pojo("c","USA"), new Pojo("d","UK"));
List<Pojo> remaining = list.stream()
.filter(p->p.location()!=null)
.dropWhile(p1->!p1.location().equals("UK"))
.collect(Collectors.toList());
System.out.println(remaining);
Output:
[Pojo[name=b, location=UK], Pojo[name=c, location=USA], Pojo[name=d, location=UK]]
In Java9 dropWhile was added in streams that will drop elements until the condition is true, and then return the subsequent elements
https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#dropWhile-java.util.function.Predicate-
input.stream().dropWhile(item -> ...).collect(...);
This is not an elegant solution compared to the rest of the answer but just a different flavor
Picked input data from this answer
Logic is once we encounter the first matching condition, set the flag, and use it in the stream filter stage.
Since we need variable to be effective final in lambda, final Boolean[] isFirstEncounter = {false}; one of the bad workaround.
Also following code should be use in sequential stream and not in parallel stream.
final Boolean[] isFirstEncounter = {false};
List<POJO> input = List.of(new POJO("a", "USA"), new POJO("b", "UK"), new POJO("c", "USA"), new POJO("d", "UK"));
List<POJO> ans = input.stream()
.filter(Objects::nonNull)
.filter(p -> {
if (!isFirstEncounter[0] && "UK".equals(p.location) {
isFirstEncounter[0] = true; // true means accept all the elements
}
return isFirstEncounter[0]; // false means filter all the elements
}
).collect(Collectors.toList());
System.out.println(ans);
//output
// [Solution.POJO(name=b, location=UK), Solution.POJO(name=c, location=USA), Solution.POJO(name=d, location=UK)]

Use Java 8 stream to create and a Collection<Stream>

I have a class
class ColumnTags {
String Name;
Collection<String> columnSemanticTags;
// constructor and getter and setters and other relevant attributes
}
I want to get the columnSemanticTags from a list of ColumnTags for a given name.
The corresponding method is as follows
public Collection<String> getTags(String colName, List<ColumnTags> colList)
{
Collection<String> tags = new ArrayList();
for(ColumnTag col:colList){
if(colName.equals(col.getName())){
tags = col.getColumnSemanticTags();
break;
}
}
return tags;
}
Want to convert the for loop to a java stream . I have tried
tags = colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.collect(Collectors.toCollection());
I am getting compilation error. I am not aware what should be the Supplier . Have tried ArrayList::new . I have also tried casting it to ArrayList , but no success.
Can someone advice me what am I assuming wrong or what should be the expected way to handle this scenario.
With the solution , can someone explain as to why .collect() is a wrong way of tackling this solution.
public Collection<String> getTags(String colName, List<ColumnTags> colList) {
return colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.findFirst().orElse(new ArrayList<>());
}
An easier way of going about this would be to simply filter a Stream to find exactly what you're looking for. If it is found, then return it, otherwise return an empty ArrayList:
return colList.stream()
.filter(c -> colName.equals(c.getName()))
.map(ColumnTag::getColumnSemanticTags)
.findFirst()
.orElseGet(ArrayList::new);
If you really want to use collect, you must call flatMap. That merges all of the lists (which are come from map(col -> col.getColumnSemanticTags())) into a single stream which contains all of the items.
List<String> tags = colList.stream()
.filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.flatMap(collection -> collection.stream())
.collect(Collectors.toList());

Getting filtered records from streams using lambdas in java

I have an entity Employee
class Employee{
private String name;
private String addr;
private String sal;
}
Now i have list of these employees. I want to filter out those objects which has name = null and set addr = 'A'. I was able to achieve like below :
List<Employee> list2= list.stream()
.filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
Now list2 will have all those employees whose name is not null and then set addr as A for those employees.
What i also want to find is those employees which are filtered( name == null) and save them in DB.One way i achieved is like below :
List<Employee> list2= list.stream()
.filter(l -> filter(l))
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
private static boolean filter(Employee l){
boolean j = l.getName() != null;
if(!j)
// save in db
return j;
}
1) Is this the right way?
2) Can we do this directly in lambda expression instead of writing separate method?
Generally, you should not use side effect in behavioral parameters. See the sections “Stateless behaviors” and “Side-effects” of the package documentation. Also, it’s not recommended to use peek for non-debugging purposes, see “In Java streams is peek really only for debugging?”
There’s not much advantage in trying to squeeze all these different operations into a single Stream pipeline. Consider the clean alternative:
Map<Boolean,List<Employee>> m = list.stream()
.collect(Collectors.partitioningBy(l -> l.getName() != null));
m.get(false).forEach(l -> {
// save in db
});
List<Employee> list2 = m.get(true);
list2.forEach(l -> l.setAddr("A"));
Regarding your second question, a lambda expression allows almost everything, a method does. The differences are on the declaration, i.e. you can’t declare additional type parameters nor annotate the return type. Still, you should avoid writing too much code into a lambda expression, as, of course, you can’t create test cases directly calling that code. But that’s a matter of programming style, not a technical limitation.
If you are okay in using peek for implementing your logic (though it is not recommended unless for learning), you can do the following:
List<Employee> list2= list.stream()
.peek(l -> { // add this peek to do persistence
if(l.getName()==null){
persistInDB(l);
}
}).filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
You can also do something like this:
List<Employee> list2 = list.stream()
.filter(l->{
boolean condition = l.getName()!=null;
if(condition){
l.setAddr("A");
} else {
persistInDB(l);
}
return condition;
})
.collect(Collectors.toList());
Hope this helps!

Mutate elements in a Stream

Is there a 'best practice' for mutating elements within a Stream? I'm specifically referring to elements within the stream pipeline, not outside of it.
For example, consider the case where I want to get a list of Users, set a default value for a null property and print it to the console.
Assuming the User class:
class User {
String name;
static User next(int i) {
User u = new User();
if (i % 3 != 0) {
u.name = "user " + i;
}
return u;
}
}
In java 7 it'd be something along the lines of:
for (int i = 0; i < 7; i++) {
User user = User.next(i);
if(user.name == null) {
user.name = "defaultName";
}
System.out.println(user.name);
}
In java 8 it would seem like I'd use .map() and return a reference to the mutated object:
IntStream.range(0, 7)
.mapToObj(User::next)
.map(user -> {
if (user.name == null) {
user.name = "defaultName";
}
return user;
})
//other non-terminal operations
//before a terminal such as .forEach or .collect
.forEach(it -> System.out.println(it.name));
Is there a better way to achieve this? Perhaps using .filter() to handle the null mutation and then concat the unfiltered stream and the filtered stream? Some clever use of Optional? The goal being the ability to use other non-terminal operations before the terminal .forEach().
In the 'spirit' of streams I'm trying to do this without intermediary collections and simple 'pure' operations that don't depend on side effects outside the pipeline.
Edit: The official Stream java doc states 'A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care.' Given that this would be a non-interfering operation, what specifically makes it dangerous? The examples I've seen reach outside the pipeline, which is clearly sketchy.
Don't mutate the object, map to the name directly:
IntStream.range(0, 7)
.mapToObj(User::next)
.map(user -> user.name)
.map(name -> name == null ? "defaultName" : name)
.forEach(System.out::println);
It sounds like you're looking for peek:
.peek(user -> {
if (user.name == null) {
user.name = "defaultName";
}
})
...though it's not clear that your operation actually requires modifying the stream elements instead of just passing through the field you want:
.map(user -> (user.name == null) ? "defaultName" : user.name)
It would seem that Streams can't handle this in one pipeline. The 'best practice' would be to create multiple streams:
List<User> users = IntStream.range(0, 7)
.mapToObj(User::next)
.collect(Collectors.toList());
users.stream()
.filter(it -> it.name == null)
.forEach(it -> it.name = "defaultValue");
users.stream()
//other non-terminal operations
//before terminal operation
.forEach(it -> System.out.println(it.name));

Categories

Resources