Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
i m playing around with Java Streams and I wonder if there is any way to create a code Block like this ->
if(givenString.equals("productA")) {
return new productA();
} else if(givenString.equals("productB") {
return new productB();
} .....
into a Java Stream like this ->
Stream.of(givenString)
.filter(e -> e.equal("productA)")
.map(e -> new productA())
i came across with this solution which works but i m not convinced...
Stream.of(givenString)
.map(e -> e -> e.equals("productA)" ? new productA() : new productB())
.findAny()
.get()
You don't want to do that inline in a stream. Instead, write a helper method that does just that:
private static Product createByString(String name) {
// I assume Product is a common superclass
// TODO: implement
}
Now the question is: How should this method be implemented?
Use a big switch statement.
private static Product createByString(String name) {
switch (name) {
case "productA": new productA();
case "productB": new productB();
// ... maybe more?
default: throw new IllegalArgumentException("name " + name + " is not a valid Product");
}
}
Pro: a switch on a string is compiled into a jumptable, so you won't have n string comparisons.
Con: You can't extend it at runtime, and you have to keep this method in sync.
Use a HashMap<String,Supplier<Product>>.
private static final Map<String,Supplier<Product>> productConstructors = new HashMap<>();
static {
productConstructors.put("productA", productA::new);
productConstructors.put("productB", productB::new);
}
private static Product createByString(String name) {
Supplier<Product> constructor = productConstructors.get(name);
if (constructor == null) {
// Handle this?
throw new IllegalArgumentException("name " + name + " is not a valid Product");
}
return constructor.get();
}
Pro: with some easy modifications you can add new products to this implementation, or even replace them.
Con: has some moderate overhead, and you still need to maintain a the mapping between "productA" and it's type.
Use reflection.
The good old hammer where every problem looks like a nail.
private static Product createByString(String name) {
try {
return Class.forName("your.pkgname. " + name).asSubclass(Product.class).getConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
Pro: You don't need to do the binding.
Con: It's slow.
In your first example below:
if(givenString.equals("productA")) {
return new productA();
} else if(givenString.equals("productB") {
return new productB();
}
You are returning an instance of some object specified via a string. It seems to me that if you know the string, you can just create the object right away without using an intervening method call to do so.
Another possibility is that the class name was provided via some user input. In this case you might want to consider reflection to accomplish this so you can reference the methods and fields of the newly created class.
In either case I doubt streams is a reasonable approach for this sort of requirement.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Optional<String> myData;
Set<String> mySet;
if(myData.isPresent()) {
if(myData.get().contains(“testValue”)) {
mySet.add(“somedata”);
}
}
if(!myData.isPresent()) {
mySet.add(“someotherdata”);
}
I have a scenario like this. Where I have nested if-else block and I need to convert that into Java8 map using lambda functions. How can I do that?
This will reproduce your earlier results but has been slightly altered. I believe that a stream solution is not necessary nor desirable. The following will:
result in an empty set if data is present but not testValue
result in the set containing someotherdata if the data is not present
result in the set containing somedata if the data is present and matches testValue
String result = myData.isPresent() ?
(myData.get().contains("testValue") ? "somedata" : "") :
"someotherdata";
if (!result.isBlank()) {
mySet.add(result);
}
You could rework your Optional use (this only apply to your code, as originally presented [in case you edit it]):
Optional<String> myData = ...;
myData.ifPresent(value -> {
if (value.contains("testValue")) {
mySet.add("somedata"),
}
});
if (!myData.isPresent()) {
mySet.add("someotherdata");
}
If you are using Java 11, you should use:
myData.ifPresentOrElse(
value -> {
if (value.contains("testValue")) {
mySet.add("somedata");
}
},
() -> mySet.add("someotherdata")
);
It is up to you to decide which is better (the first avoid doing mySet.add twice in two part).
[edit] fixed the answer and compilation issues per comment remark.
You can use filter to avoid the nested logic in the present case. Your second case has no nested logic, seems fine as is.
myData.filter(d -> d.contains("testValue"))
.ifPresent(mySet::add);
if(!myData.isPresent()) {
mySet.add(“someotherdata”);
}
Well, if you insist:
Optional<String> myData = Optional.of("x");
Set<String> mySet = myData
.<Set<String>>map(s -> s.contains("testValue") ? Set.of("somedata") : Set.of())
.orElse(Set.of("someotherdata"));
System.out.println(mySet);
Output in this case:
[]
Testing other optionals:
Optional<String> myData = Optional.of("x testValue x");
[somedata]
Optional<String> myData = Optional.empty();
[someotherdata]
My own preference is for the use of ifPresentOrElse() shown in the last half of the answer by NoDataFound.
How can I beautify the below java query using java stream filter and predicate?
I have this piece of code where its trying to throw some exceptions based on some condition which needs to be handled at UI level. How can I shorten the below code into more precise using java stream filter and predicate.
public Sort resolveArgument() {
Sort sort = sortHandlerMethodArgumentResolver.resolveArgument();
List<Sort.Order> orders = sort.stream().collect(Collectors.toList());
if (orders.isEmpty()) {
sort = Sort.by(Sort.Direction.DESC, default_sort_param);
} else {
if (orders.size() > sort_max_count) {
throw new InvalidSortException("Please provide only 3 valid sort parameters.");
}
}
orders.forEach(order -> {
if (!allowed_sort_params.contains(order.getProperty())) {
throw new InvalidSortException(order.getProperty() + " is not a valid sort parameter");
}
});
return sort;
}
The forEach call can be replaced with:
orders.stream().map(Sort.Order::getProperty).filter(x -> !allowed_sort_params.contains(x))
.findFirst().ifPresent(x -> {
throw new InvalidSortException(x + " is not a valid sort parameter");
});
However, IMO this is not much of an improvement.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
The question is more general and is not related to pros and cons of both styles.
The question is should I prefer whenever it is possible to use Stream instead of for loops because it is declarative with a good readability?
I was arguing with my colleague about pros and cons of using streams and for loop. I agree that we should prefer streams in 90% of time but I believe there are some cases when it is better to use for loop instead of stream.
For example I needed to perform several operations on collection of elements and these operations could throw Checked Exception. During operating if exception occurres for any element I wanted to quit the execution at all so I used for loop for it and wrapped it in try/catch block. My colleague was not satisfied because result took in two times more lines than If I would use stream instead. I rewrote it by creating own custom functional interfaces that throws checked exception and static methods to convert them into throwing unchecked exception(examples here) and finally it looked like this:
try {
Map<String, String> someResult= elements.stream()
.filter(throwingPredicateWrapper(element-> client.hasValue(element)))
.collect(
Collectors.toMap(Function.identity(),
throwingFunctionWrapper(element -> client.getValue(element))));
return someResult;
} catch (Exception e) {
LOGGER.error("Error while processing", e);
}
He was happy because it took lines of code in two time less.
It is simple example and it does not look so bad but old loop here is more simple and faster way to deal with that case I believe.
Should we tend to use Streams everywhere it is possible?
Joshua Bloch, author of "Effective Java", has a good talk which touches on when to use streams. Start watching around 30:30 for his section on "Use streams judiciously".
Although this is largely opinion based, he argues that you do not want to immediately begin turning all of your procedural loops into streams, but you really want a balanced approach. He provides at least one example method where doing so creates code that is more difficult to understand. He also argues that there is no right answer in many cases whether to write it procedural or in a more functional manner, and it is dependent on the context (and I would argue what the team has decided to do corporately might play a role). He has the examples on GitHub, and all the examples below are from his GitHub repository.
Here is the example he provides of his iterative anagram method,
// Prints all large anagram groups in a dictionary iteratively (Page 204)
public class IterativeAnagrams {
public static void main(String[] args) throws IOException {
File dictionary = new File(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
Map<String, Set<String>> groups = new HashMap<>();
try (Scanner s = new Scanner(dictionary)) {
while (s.hasNext()) {
String word = s.next();
groups.computeIfAbsent(alphabetize(word),
(unused) -> new TreeSet<>()).add(word);
}
}
for (Set<String> group : groups.values())
if (group.size() >= minGroupSize)
System.out.println(group.size() + ": " + group);
}
private static String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
}
And here it is using Streams,
// Overuse of streams - don't do this! (page 205)
public class StreamAnagrams {
public static void main(String[] args) throws IOException {
Path dictionary = Paths.get(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try (Stream<String> words = Files.lines(dictionary)) {
words.collect(
groupingBy(word -> word.chars().sorted()
.collect(StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append).toString()))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.map(group -> group.size() + ": " + group)
.forEach(System.out::println);
}
}
}
He argues for a balanced, third approach that uses both,
// Tasteful use of streams enhances clarity and conciseness (Page 205)
public class HybridAnagrams {
public static void main(String[] args) throws IOException {
Path dictionary = Paths.get(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try (Stream<String> words = Files.lines(dictionary)) {
words.collect(groupingBy(word -> alphabetize(word)))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.forEach(g -> System.out.println(g.size() + ": " + g));
}
}
private static String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
}
I'm working on a problem for school. A lot of this method was already implemented and I'm not allowed to make too many changes.
Actually I can only make changes in specific spots.
Here's the code of the method I'm working on, although some words are in Dutch, it should be readable.
It should read lines of a file, create addresses from the text (which is saved as (street+" "+number+" "+place)) and add them to a list which is returned. The file ends with an empty line.
#Override
public List<Adres> query(ISpecification specification) {
if (specification instanceof FileSpecification) {
if (((FileSpecification) specification).toFileQuery().equals("ALL")) {
ArrayList<Adres> adressen = new ArrayList<>();
/*---start of my code*/
File studentF = new File(fsConnection.getStudentConnection());
try {
FileReader fr = new FileReader(studentF);
BufferedReader br = new BufferedReader(fr);
br.lines().forEach(new Consumer<String>(){
#Override
public void accept(String line) {
String[] words = line.split("\\s");
if(words.length == 3){
/*line i'm having trouble with*/adressen.add(new Adres(words[0], Integer.parseInt(words[1]), words[2]);
}
}
});
} catch (FileNotFoundException ex) {
Logger.getLogger(AdresFile.class.getName()).log(Level.SEVERE, null, ex);//Don't mind this
}
/*---end of my code*/
//System.out.println("query: Nog niet geimplementeerd!");
return adressen;
} else {
return null;
}
} else {
return null;
}
}
As you see I wanted to access a list outside of a consumer block. I know now this is not possible. I thought of creating a different method or so, but that is not allowed. I have to use the foreach method. Any help is appreciated.
When you are working with Java7, then the compiler would need
final ArrayList<Adres> adressen = new ArrayList<>();
there. The point is: you want to use that local variable within an anoymous inner class; in other words: in a context that is somehow decoupled from the class you put the source code. And in order for the decoupled class to able to use adressen needs to be final (so that the compiler knows: that reference will not change later on). And given your comment: no, this doesn't magically turn an object into something that is immutable. It just prevents that the reference changes the "target" it is pointing to!
But as you are not allowed to change that line, you could go for:
ArrayList<Adres> adressen = new ArrayList<>();
final ArrayList<Adres> tempAdressen = adressen;
and then have your code use tempAdressen.
Alternatively, I assume you are using Java7. With Java8, the compiler should be able to understand that adressen is effectively final; and thus it should accept the source code as is.
It seems you are already using Java8, because of the call to lines() in BufferedReader.
So my suggestion is to do a map then a collect to list, instead of a forEach. This way you will not need to access the list from within your Consumer.
adressen.addAll(
br.lines().map(line -> {
String[] words = line.split("\\s");
if (words.length == 3) {
return new Adres(words[0], Integer.parseInt(words[1]), words[2]);
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
With Java8, you can use java.util.stream.Collectors to return a list of elements directly from a stream. The use of collectors help you to avoid side effects (in your case, the need to use an external array to put the elements parsed).
I would personally write it using the following lambda:
List<Adres> adressen = br.lines().stream()
.map(line -> line.split("\\s"))
.filter(words -> words.length == 3)
.map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
.collect(Collectors.toList());
This will work, yet it won't handle the case in which the data is malformed. To solve that problem, one can change the code above to handle if a line is not composed of 3 elements by modifying the lambda (even if this is not very elegant):
List<Adres> adressen = br.lines().stream()
.map(line -> line.split("\\s"))
.filter(words -> {
if (words.length == 3)
return true;
else {
throw new IllegalArgumentException();
}
})
.map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
.collect(Collectors.toList());
This question already has answers here:
Multiple if statements with single else statement
(2 answers)
Closed 6 years ago.
I want to fill the acts array with values of some enum. While iterating I want to input commands from console, but my if statements don't find any match and I always get the output "Incorrect".
My code:
Action[] acts = new Action[n];
for(int i = 0; i < n; i++) {
System.out.println("Enter act: ");
Scanner in1 = new Scanner(System.in);
String s = in1.next();
acts[i] = new Action();
if (s.equals("rotate_forw"))
acts[i].type = ActionType.RotF;
if (s.equals("rotate_back"))
acts[i].type = ActionType.RotB;
if (s.equals("shift_forw"))
acts[i].type = ActionType.ShiftF;
if (s.equals("shift_back"))
acts[i].type = ActionType.ShiftB;
else
System.out.println("Incorrect");
}
Your else clause applies only to the last if statement, so you get the "Incorrect" output whenever s.equals("shift_back") is false.
Your statements should be replaced with a single if-else-if...-else statement, so that "Incorrect" is only printed if all the conditions are false :
Action[] acts = new Action[n];
for(int i = 0; i < n; i++) {
if (s.equals("rotate_forw"))
acts[i].type = ActionType.RotF;
else if (s.equals("rotate_back"))
acts[i].type = ActionType.RotB;
else if (s.equals("shift_forw"))
acts[i].type = ActionType.ShiftF;
else if (s.equals("shift_back"))
acts[i].type = ActionType.ShiftB;
else
System.out.println("Incorrect");
}
You should also consider what you want to assign to acts[i].type when the input is incorrect. Perhaps you should throw an exception in this case.
While #Eran's answer is correct, I'd like to suggest a different approach that encapsulates the enum with the translation from the external coding. Consider this:
public class EnumDemo
{
public static enum ActionType
{
Incorrect(""),
RotF("rotate_forw"),
RotB("rotate_back"),
ShiftF("shift_forw"),
ShiftB("shift_back");
private String code;
private ActionType(String code)
{
this.code = code;
}
public static ActionType fromString(String code)
{
return Arrays.stream(ActionType.values())
.filter(v->v.code.equals(code))
.findFirst()
.orElse(ActionType.Incorrect);
}
}
public static void main(String[] args)
{
String[] testData = {
"rotate_forw",
"rotate_back",
"shift_forw",
"shift_back",
"junk",
null };
Arrays.stream(testData)
.forEach(t->System.out.printf("\"%s\" -> ActionType.%s\n", t, ActionType.fromString(t)));
}
}
This uses the fact that enum constants can have associated data. I've added an instance variable code to hold the external encoding of each enum value. Then I added a static fromString(String code) method to the enum that looks up the provided code in the list of values. For 4 possibilities a simple linear search, equivalent to your if-then-else cascade, works fine. If there were dozens or more I'd set up a Map<String,ActionType> to handle the conversion.
The search using streams bears some explanation.
First create a Stream of enum values
Filter it to contain only enum values whose code matches the desired code (there should be only one)
Pick off the first entry, which comes back in a Optional. If nothing was found (i.e. the code is invalid) the Optional will be empty.
Use the orElse method to return the value if it exists or ActionType.Incorrect if not.
At first glance this might look inefficient since one expects that the filter() predicate has to scan the entire stream even if the desired element occurs early. This is a really nifty feature of Streams -- all intermediate streams are "lazy", so the filter won't iterate over the entire list if it finds the desired entry early. See this question for details.
Output:
"rotate_forw" -> ActionType.RotF
"rotate_back" -> ActionType.RotB
"shift_forw" -> ActionType.ShiftF
"shift_back" -> ActionType.ShiftB
"junk" -> ActionType.Incorrect
"null" -> ActionType.Incorrect
The last testcase shows the code is null-safe.
The biggest advantage is that the mapping is in the same place as the enum itself, so you won't have to hunt for the code when you add or remove an enum value. Also you can't forget to define the mapping since it's required by the enum's constructor.