Just kicking the tires of Java 8, and noticed Lamdas and streams functional programing.
Was wondering if a simple command line args consumer could use streams.
Cant figure out howto get at two stream elements at once however...
Plus below I'd need to handle args that do and dont take values, so couldnt do any odd/even trickery. Would have consider args to always start with a dash, and optional values never do.
String[] args = ("-v", "-c", "myconfigfile", "-o", "outputfile");
Arrays.toList(args).stream().map( a,v -> evalArg(a,v));
public static void evalArg(String arg, String val) {
switch(arg) {
case "-v":
verbose = true;
break;
case "-c":
config_file = val;
break;
case "-o":
output_file = val;
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
If you have key-value pairs then you can use following:
public static void main(final String[] args) {
String[] args = {"-v", "value", "-c", "myconfigfile", "-o", "outputfile"};
pairStream(Arrays.asList(args), (param, value) -> param + ": " + value)
.forEach(System.out::println);
}
public static <X, Y> Stream<Y> pairStream(List<X> list, BiFunction<X, X, Y> mapper) {
Supplier<X> s = list.iterator()::next;
return Stream.generate(() -> mapper.apply(s.get(), s.get()))
.limit(list.size() / 2);
}
// Result:
// -v: value
// -c: myconfigfile
// -o: outputfile
Stream API is not really built to process values which depent on the state of another value. The used lambda function should be stateless (see also the Java documentation: Stream#map()). However it is possible to use sequential() on the stream to ensure the correct order and allow using a state inside the function used to process the elements, but that is not really recommended.
Better use a library for parameter parsing like Apache Commons CLI.
After writing my original answer I realised this is possible using reduce, for example like this:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).reduce((arg, val) -> {
switch (arg) {
case "-v":
verbose = true;
break;
case "-c":
configFile = val;
break;
case "-o":
outputFile = val;
break;
// Non-exhaustive
}
return val;
});
My original answer, using state and function objects:
Remember that Java does not actually support first class functions. When you are using a lambda, you're actually passing an object containing a function. These objects can have state. You can use this to your advantage, so you should be able to do something like this:
String[] foo = {"-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String last;
public void accept(String t) {
if (last == null) {
last = t;
} else {
System.out.println(last + " " + t);
last = null;
}
}
});
Whether this is a good idea is a different consideration though. Note the use of forEachOrdered as plain old forEach is not guaranteed to go through the list in a specific order. Also note that map would not work for doing one thing with two elements, as a mapping function has to take one argument and return one result, resulting in a one-to-one relationship between the input stream and the output stream.
For your specific example you would have to do something like this though:
String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};
Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
String arg;
public void accept(String val) {
if (arg == null) {
arg = val;
} else if (t.startsWith("-")) {
System.out.println(arg);
arg = val;
} else {
System.out.println(arg + " " + val);
arg = null;
}
}
});
A third alternative is of course to do as yshavit suggests and not use streams at all:
LinkedList<String> argList = LinkedList<>(Arrays.asList(args));
while(!argList.isEmpty()) {
switch (argList.pop()) {
case "-v":
verbose = true;
break;
case "-c":
configFile = argList.pop();
break;
case "-o":
outputFile = argList.pop();
break;
default:
System.err.println("unknown argument " + arg + " " + val);
break;
}
}
There's no easy way to do this with lambdas/mapping. Not all forms of iteration lend themselves to mapping a single lambda.
For instance, the functional approach to this probably wouldn't use lambdas; it would use recursion, with each step optionally popping more args if it needs to. Something like (in pseudo-code, and ignoring error checking, etc):
Options[] parse(args : String[]):
if args.isEmpty:
return []
currentOpt, remainingArgs = args.pop()
case currentOpt of:
"-v":
return [ Verbose ] ++ parse(remainingArgs)
"-c":
configPath, remainingArgs = remainingArgs.pop()
return [ Config(configPath) ] ++ parse(remainingArgs)
"-o":
outputPath, remainingArgs = remainingArgs.pop()
return [ Output(outputPath) ] ++ parse(remainingArgs)
_:
return [ UnknownOption(currentOpt) ] ++ parse(remainingArgs)
You seem to have the idea, but I thought I would expand on it with a little additional data.
The concept of a data stream is probably older than Java; processing them goes back at least to processor architectures. CPUs are built with something called Von Neumann Architecture; which allows the processor to maintain pointers to different parts of memory and, basically, have access to all of it all the time. Some other processors in common use (such as GPUs) are stream processors. They handle one operation at a time, and outside of an occasional (and often dubious) clever trick, have no knowledge of any other part of the stream. This allows for optimal parallel processing, among other things, and is precisely why GPGPU can be blisteringly effective for some tasks, but is woefully ineffective at running an entire machine.
The Streams API allows you to run operations under the same circumstances. The only way to operate on more than one item at once is through a reduce; which is basically the construction of another stream. Through it, in theory, you can certainly scan arguments and launch options; but due to the potential redundancy, that doesn't mean it's the best in practice.
Related
I couldn't wrap my head around writing the below condition using Java Streams. Let's assume that I have a list of elements from the periodic table. I've to write a method that returns a String by checking whether the list has Silicon or Radium or Both. If it has only Silicon, method has to return Silicon. If it has only Radium, method has to return Radium. If it has both, method has to return Both. If none of them are available, method returns "" (default value).
Currently, the code that I've written is below.
String resolve(List<Element> elements) {
AtomicReference<String> value = new AtomicReference<>("");
elements.stream()
.map(Element::getName)
.forEach(name -> {
if (name.equalsIgnoreCase("RADIUM")) {
if (value.get().equals("")) {
value.set("RADIUM");
} else {
value.set("BOTH");
}
} else if (name.equalsIgnoreCase("SILICON")) {
if (value.get().equals("")) {
value.set("SILICON");
} else {
value.set("BOTH");
}
}
});
return value.get();
}
I understand the code looks messier and looks more imperative than functional. But I don't know how to write it in a better manner using streams. I've also considered the possibility of going through the list couple of times to filter elements Silicon and Radium and finalizing based on that. But it doesn't seem efficient going through a list twice.
NOTE : I also understand that this could be written in an imperative manner rather than complicating with streams and atomic variables. I just want to know how to write the same logic using streams.
Please share your suggestions on better ways to achieve the same goal using Java Streams.
It could be done with Stream IPA in a single statement and without multiline lambdas, nested conditions and impure function that changes the state outside the lambda.
My approach is to introduce an enum which elements correspond to all possible outcomes with its constants EMPTY, SILICON, RADIUM, BOTH.
All the return values apart from empty string can be obtained by invoking the method name() derived from the java.lang.Enum. And only to caver the case with empty string, I've added getName() method.
Note that since Java 16 enums can be declared locally inside a method.
The logic of the stream pipeline is the following:
stream elements turns into a stream of string;
gets filtered and transformed into a stream of enum constants;
reduction is done on the enum members;
optional of enum turs into an optional of string.
Implementation can look like this:
public static String resolve(List<Element> elements) {
return elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.equals("SILICON") || str.equals("RADIUM"))
.map(Elements::valueOf)
.reduce((result, next) -> result == Elements.BOTH || result != next ? Elements.BOTH : next)
.map(Elements::getName)
.orElse("");
}
enum
enum Elements {EMPTY, SILICON, RADIUM, BOTH;
String getName() {
return this == EMPTY ? "" : name(); // note name() declared in the java.lang.Enum as final and can't be overridden
}
}
main
public static void main(String[] args) {
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Lithium"))));
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Radium"))));
System.out.println(resolve(List.of(new Element("Ferrum"), new Element("Oxygen"), new Element("Aurum")))
.isEmpty() + " - no target elements"); // output is an empty string
}
output
SILICON
BOTH
true - no target elements
Note:
Although with streams you can produce the result in O(n) time iterative approach might be better for this task. Think about it this way: if you have a list of 10.000 elements in the list and it starts with "SILICON" and "RADIUM". You could easily break the loop and return "BOTH".
Stateful operations in the streams has to be avoided according to the documentation, also to understand why javadoc warns against stateful streams you might take a look at this question. If you want to play around with AtomicReference it's totally fine, just keep in mind that this approach is not considered to be good practice.
I guess if I had implemented such a method with streams, the overall logic would be the same as above, but without utilizing an enum. Since only a single object is needed it's a reduction, so I'll apply reduce() on a stream of strings, extract the reduction logic with all the conditions to a separate method. Normally, lambdas have to be well-readable one-liners.
Collect the strings to a unique set. Then check containment in constant time.
Set<String> names = elements.stream().map(Element::getName).map(String::toLowerCase).collect(toSet());
boolean hasSilicon = names.contains("silicon");
boolean hasRadium = names.contains("radium");
String result = "";
if (hasSilicon && hasRadium) {
result = "BOTH";
} else if (hasSilicon) {
result = "SILICON";
} else if (hasRadium) {
result = "RADIUM";
}
return result;
i have used predicate in filter to for radium and silicon and using the resulted set i am printing the result.
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Element> elementss = new ArrayList<>();
Set<String> stringSet = elementss.stream().map(e -> e.getName())
.filter(string -> (string.equals("Radium") || string.equals("Silicon")))
.collect(Collectors.toSet());
if(stringSet.size()==2){
System.out.println("both");
}else if(stringSet.size()==1){
System.out.println(stringSet);
}else{
System.out.println(" ");
}
}
}
You could save a few lines if you use regex, but I doubt if it is better than the other answers:
String resolve(List<Element> elements) {
String result = elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.matches("RADIUM|SILICON"))
.sorted()
.collect(Collectors.joining());
return result.matches("RADIUMSILICON") ? "BOTH" : result;
}
Okay so essentially, I have some code that uses the contains() method to detect the presence of specific characters in two strings. For extra context, this question is a good resource as to what kind of problem I'm having (and the third solution is also something I've looked into for this). Regardless, here is some of my code:
// code up here basically just concatenates different
// characters to Strings: stringX and stringY
if (stringX.contains("!\"#")) {
} else if (stringX.contains("$%&")) {
} else if (stringX.contains("\'()")) {
} else if (stringX.contains("!$\'")) {
} else if (stringX.contains("\"%(")) {
// literally 70+ more else-if statements
}
if (stringY.contains("!\"#")) {
} else if (stringY.contains("$%&")) {
} else if (stringY.contains("\'()")) {
} else if (stringY.contains("!$\'")) {
} else if (stringY.contains("\"%(")) {
// literally 70+ more else-if statements, all of which are
// exactly the same as those working with stringX
}
I'm still pretty new to Java programming, so I'm not sure how I should go about this. Maybe it is a non-issue? Also, if I can remedy this without using RegEx, that would be preferable; I am not very knowledgeable in it at this point it time. But if the only rational solution would be to utilize it, I will obviously do so.
Edit: The code within all of these else-if statements will not be very different from each other at all; basically just a System.out.println() with some information about what characters stringX/stringY contains.
Writing the same code more than once should immediately set off alarm bells in your head to move that code into a function so it can be reused.
As for simplifying the expression, the best approach is probably storing the patterns you're looking for as an array and iterating over the array with your condition.
private static final String[] patterns = new String[] {"!\"#", "$%&", "\'()", "!$\'", "\"%(", ...};
private static void findPatterns(String input) {
for (String pattern : patterns) {
if (input.contains(pattern) {
System.out.println("Found pattern: " + pattern);
}
}
}
// Elsewhere...
findPatterns(stringX);
findPatterns(stringY);
This pattern is especially common in functional and functional-style languages. Java 8 streams are a good example, so you could equivalently do
List<String> patterns = Arrays.asList("!\"#", "$%&", "\'()", "!$\'", "\"%(", ...);
patterns.stream()
.filter(pattern -> stringX.contains(pattern))
.forEach(pattern -> System.out.println("Found pattern: " + pattern));
can simply by make a list of your case. then using java 8 stream filter
List<String> pattems = Arrays.asList("!\"#", "$%&", ...);
Optional<String> matched = pattems.stream().filter(p -> stringX.contains(p));
if(matched.isPresent()) {
System.console().printf(matched.get())
}
java stream could make your peformance slower but not too much
I'm looking for suggestions on how to go about validating input from a user. My assignment is to execute commands based on a textual input from the user. My only concern is that there can be many variations of commands that are acceptable.
For example these commands are all acceptable and do the same thing, "show the game board"
sh board,
sho board,
show board,
show bo,
sho bo,
sh bo
There are about 10 other commands that share this similar property so I was wondering what would be the best practice of going about validating a users input?
Should I store all the different combinations in a hashmap?
Look into regex (regular expressions). These are great for when you want to use values that are not necessarily complete.
For example:
Say I type "shutdo"
With regex you can make your program understand that anything after the string "shutd" means to powerOff()
It looks like the minimum command allowed length is 2.
So first you check if the length of the term is at least 2.
Next, you can loop over the available commands,
and stop at the first that starts with the term, for example:
List<String> commands = Arrays.asList("show", "create", "delete");
for (String command : commands) {
if (command.startsWith(term)) {
// found a match, command is: command
break;
}
}
If the commands are very specific and limited, I would just add all of them into some data structure (hash being one of them).
If the problem was that you're supposed to understand what the user input is supposed to do, then I would say find the pattern using either regex or a simple pattern validation (looks like they're all two words, first starting with "sh" and second starting with "bo").
But honestly, ~15 commands aren't that big of deal in terms of space/efficiency.
Edit:
There are about 10 other commands that share this similar property
If this means 10 more commands like "show board", then I would say store it in hash. But if I misunderstood you and you mean that there are 10 other commands that do similar things ("set piece", "set pie", "se pi", etc), then RegEx is the way to go.
If I understood you correctly, there are N distinct commands, which can be combined. It shall be allowed to abbreviate each command as long it stays unambiguous.
If this is the case, the following methods expandCommands(String) and expandCommand(String) will normalize each command part.
public class Main {
static Set<String> availableCommands = new HashSet<>(Arrays.asList(
"show",
"board",
"btest"
));
public static void main(String[] args) throws Exception {
List<String> testData = Arrays.asList(
"sh board",
"sho board",
"show board",
"show bo",
"sho bo",
"sh bo"
);
String expected = "show board";
for (String test : testData) {
String actual = expandCommands(test);
if (!expected.equals(actual)) {
System.out.println(test + "\t"+ actual);
}
}
try {
expandCommands("sh b");
throw new IllegalStateException();
} catch (Exception e) {
if (!"not unique command: b".equals(e.getMessage())) {
throw new Exception();
}
}
try {
expandCommands("sh asd");
throw new IllegalStateException();
} catch (Exception e) {
if (!"unknown command: asd".equals(e.getMessage())) {
throw new Exception();
}
}
}
private static String expandCommands(String aInput) throws Exception {
final String[] commandParts = aInput.split("\\s+");
StringBuilder result = new StringBuilder();
for (String commandPart : commandParts) {
String command = expandCommand(commandPart);
result.append(command).append(" ");
}
return result.toString().trim();
}
private static String expandCommand(final String aCommandPart) throws Exception {
String match = null;
for (String candidate : availableCommands) {
if (candidate.startsWith(aCommandPart)) {
if (match != null) {
throw new Exception("not unique command: " + aCommandPart);
}
match = candidate;
}
}
if (match == null) {
throw new Exception("unknown command: " + aCommandPart);
}
return match;
}
}
The Set<String> availableCommands contains all possible commands.
Every part of the input command is checked, if it is the start of exactly one available command.
You can use reg-ex matching to validate input. E.g., the pattern below will match anything that starts with sh followed by 0 or more characters, then a space and then bo followed by 0 or more chars.
public class Validator {
public static void main (String[] args) {
String pattern = "sh[\\w]* bo[\\w]*";
System.out.println(args[0].matches(pattern));
}
}
I wanted to know if there was a way to shorten this if statement with the ".equals" so that I can test things in one line, instead of multiple if statements.
This is an excerpt my current long winded code. (This is what I want to shorten)
if (queryArray[1].equals("+")) {
System.out.println("Got +");
} else if (queryArray[1].equals("-")) {
System.out.println("Got -");
} else if (queryArray[1].equals("*")) {
System.out.println("Got *");
}
I tried doing this (Does not work) to reduce the number of lines needed.
if (queryArray[1].equals("+","-","*")) {
System.out.println("Got +");
}
And even (Does not work):
if (queryArray[1].equals("+" || "-" || "*")) {
System.out.println("Got +");
}
Also, I know about the or syntax "||" within if statements, however I'm looking to shorten it within the ".equals()" method.
Is there any way to shorten this code? Thank you.
Since you're only doing single-character comparisons, you can do a switch on queryArray[1].charAt(0).
switch (queryArray[1].charAt(0)) {
case '+':
// plus thing
break;
case '-':
// minus thing
break
// ... and so on
}
Or if you're using Java 7, you can switch directly on the string.
With Java 7, you can do a switch on strings:
switch(queryArray[1]) {
case "+":
case "*":
case "-":
System.out.println("Got " + queryArray[1]);
break;
default:
// do nothing
}
you can even do it in this way
List<String> list = Arrays.asList("+","-","*");
if(list.contains(queryArray[1]))
System.out.println("Got "+queryArray[1]);
First off your alternative syntax inside the .equals() isn't valid Java.
Unless you have way more than a few tests and each one of them has lots of cyclomatic complexity in each condition, there isn't any compelling reason to do what you are asking.
That said, you need to flip the problem on its head and do something like the following:
interface Handler { public void handle(); }
final Map<String, Handler> symbols = new HashMap<String, Handler>();
symbols.put("+", new Handler() {
public void handle() { System.out.println("Got +"); }
};
symbols.put("-", new Handler() {
public void handle() { System.out.println("Got -"); }
};
symbols.put("*", new Handler() {
public void handle() { System.out.println("Got *"); }
};
Then the logic tests are reduced to:
symbols.get(queryArray[1]).handle();
This won't be any faster than the individual if/elseif/else construct, but it does something like you are looking for to reduce the lines of code.
This is a common Object Oriented Design pattern, it is a variation on the Chain of Responsibility Pattern.
It is very useful when there are many alternatives in an if/elseif/else construct and the logic in each alternative is complicated.
It makes adding alternatives simple as implementing the interface and adding the alternative to the Map.
It also makes maintenance a very easy as well. Because it promotes Encapsulation of the rules and Cohesion of the logic. Something that is gets completely lost in very large if/elseif/else blocks.
You don't have to use Anonymous Inner Classes as in my example, they can be regular classes that are in their own files or regular Inner Classes.
Try this
Map<String,String> resultMap = new HashMap<String,String>();
resultMap.put("+","Got +");
resultMap.put("-","Got -");
resultMap.put("*","Got *");
System.out.println(resultMap.get(queryArray[1]));
The code block you provided is the most effecient and more readable. and if considered scalabilty and maintenance, it shouldn't be refactore if logic doesn't change.
if (queryArray[1].equals("+"))
{
System.out.println("Got +");
}
else if (queryArray[1].equals("-"))
{
System.out.println("Got -");
}
else if (queryArray[1].equals("*"))
{
System.out.println("Got *");
}
However Borealid has given switch-case construct but a little bit of logic change will initiate a lot of changes and probably bugs-crawling also.
Well, I'm too providing a solution on same lines, but it's also not better than the code you provided:
System.out.println(queryArray[1].equals("+")?"Got +"
:queryArray[1].equals("-")?"Got -"
:queryArray[1].equals("*")?"Got *"
:"");
if your problem is that method size is increasing, try creating a separate method which returns a string (to be printed), so the equals-comparison method can be moved to a separate block.
And, one more thing to say, the || and && operators should be used with boolean operands. and, before calling an API check it's javadoc: equals
On a single line ...
if (Arrays.asList("+", "-", "*").contains(queryArray[1])) {
System.out.println("BINGO!");
}
This works because asList has a varargs parameter.
However, this code involves creating and initializing a new String[], wrapping it in a new List and then iterating over the list. So don't do it if performance is likely to be a concern.
Something even more obscure:
char a = queryArray[1].charAt(0);
if ((a - '*') * (a - '+') * (a - '-') == 0) {
/* process here. */
}
Rather useless if you want to compare more than one character, though.
The shortest Java-solution I can think of is:
System.out.println (Arrays.asList ("+", "-", "*").contains ("-"));
For readability reasons I'm trying to avoid using Char based case constructs, using Java 6. I cannot switch to 7 jet...
Map<String, String> map = new HashMap<String, String>() {
{
put("foo", "--foo");
put("bar), "--bar");
...
}
private static final long serialVersionUID = 1L; // java problem
};
The serialVersionUID - as far as I know, maybe part of the problem. Currently I'm working with if constructs:
if (!map.containsValue(args[0])) {
logger.error("Unknown parameter: " + args[0]);
...
I handle ~ 30 parameters. In any case a growing number.
Is it even possible to define switch constructs with enums or HashMaps In Java 6?
If you're handling over 30 parameters in the same way, then you need some kind of loop. For example:
for (int i=0; i<args.length; i++)
{
String param = args[i];
if (!map.containsValue(param))
logger.error("Unknown parameter: " + param);
.. handle argument
}
It looks like you are parsing command line arguments. There are some good libraries available that offer flexible command line parsing, for example args4j. With args4j, you create your data model, and let it map fields in the data to command line arguments.
Using Strings in a switch statement will be available in Java 7.
For moderate or complex parsing of command line arguments I strongly recommend using Commons-CLI, it provides a great API to make this much easier for you to handle. An example of it's usage:
// create Options object
Options options = new Options();
// add t option
options.addOption("t", false, "display current time");
...
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse( options, args);
if(cmd.hasOption("t")) {
// print the date and time
}
else {
// print the date
}