28

In C# there is a way of reducing the length of an if-statement by using Enumerable.Any to check if elements in a sequence satisfy a condition (https://msdn.microsoft.com/en-us/library/vstudio/bb534972%28v=vs.100%29.aspx).

For example Instead of:

If ( string.Contains(">") || string.Contains("<") || string.Contains("&") || string.Contains("l") || string.Contains("p") )

We can use

if (new [] { ">", "<", "&", "l", "p"}.Any(w => string.Contains(w)))

Is there an equivalent, if not better, way of doing this in Java?

Mirodinho
  • 1,171
  • 1
  • 13
  • 25
  • 1
    Just for completeness: you can also use `.Any(string.Contains)` rather than the more verbose `.Any(w => string.Contains(w))` in C#. – Bob Jun 20 '15 at 02:27

4 Answers4

26

With Java 8 you can write something like:

if (Stream.of(">", "<", "&", "l", "p").anyMatch(string::contains)) {
  ...
}

Out of curiosity I ran a benchmark to compare this method vs a regex. Code and results below (lower score = faster). Streams perform an order of magnitude better than regex.

Benchmark                                    (s)  Mode  Samples     Score    Error  Units
c.a.p.SO30940682.stream   >aaaaaaaaaaaaaaaaaaaaa  avgt       10    49.942 ±  1.936  ns/op
c.a.p.SO30940682.stream   aaaaaaaaaaaaaaaaaaaaa>  avgt       10    54.263 ±  1.927  ns/op
c.a.p.SO30940682.stream   aaaaaaaaaaaaaaaaaaaaap  avgt       10   131.537 ±  4.908  ns/op
c.a.p.SO30940682.stream   paaaaaaaaaaaaaaaaaaaaa  avgt       10   129.528 ±  7.352  ns/op
c.a.p.SO30940682.regex    >aaaaaaaaaaaaaaaaaaaaa  avgt       10   649.867 ± 27.142  ns/op
c.a.p.SO30940682.regex    aaaaaaaaaaaaaaaaaaaaa>  avgt       10  1047.122 ± 89.230  ns/op
c.a.p.SO30940682.regex    aaaaaaaaaaaaaaaaaaaaap  avgt       10  1029.710 ± 61.055  ns/op
c.a.p.SO30940682.regex    paaaaaaaaaaaaaaaaaaaaa  avgt       10   694.309 ± 32.675  ns/op

Code:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
public class SO30940682 {

  @Param({">aaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaa>",
          "aaaaaaaaaaaaaaaaaaaaap", "paaaaaaaaaaaaaaaaaaaaa"}) String s;

  @Benchmark public boolean stream() {
    return Stream.of(">", "<", "&", "l", "p").anyMatch(s::contains);
  }

  @Benchmark public boolean regex() {
    return s.matches("^.*?(>|<|&|l|p).*$");
  }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • @MarcoAcierno String::contains make reference to a static method, which does not exist. https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html – Ezequiel Jun 19 '15 at 14:43
  • 2
    My bad, I removed the comment. I missed the fact that `string` is a variable – Marco Acierno Jun 19 '15 at 14:46
  • Thanks for clarifying. I wonder if Regex would be faster for checking substrings instead of single characters. I'm not sure if `contains()` performs well in those cases. – Srini Jun 19 '15 at 15:52
  • 1
    Thank you. This is a perfect solution. Bruce Wayne's solution using regex can be useful if people are not working with JAVA 8. – Mirodinho Jun 19 '15 at 17:29
  • If you really want speed and check for single characters only, you'll want to check all of the few chars in a single loop on all characters in the string. I wonder how much faster that is compared to the other methods... – AlexR Jun 19 '15 at 21:38
  • Why you write this regex like this? Of course it is slow. It's one of the worst way to write this regex. Why you not simply write s.matches(">|<|&|l|p");? – abc667 Jun 21 '15 at 09:21
  • @abc667 `String.matches` matches the entire string - so unless the string is only one character long your regex won't work. There may be other, better options though (for example compiling the Pattern and using `return pattern.matcher(string).find()` would probably be more efficient). – assylias Jun 21 '15 at 09:24
  • Oh, java :\. Sory, i forgot what that method does. But if you see two .* in regex it almost certain that this regex is wrong and will be horrible slow. – abc667 Jun 21 '15 at 09:31
9

With Java 8, this is possible with the anyMatch method:

if (Stream.of(">", "<", "&", "l", "p").anyMatch(w -> string.contains(w))) {

}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
7

Looks like org.apache.commons.lang3.StringUtils.containsAny() does what you want.

Srini
  • 1,626
  • 2
  • 15
  • 25
6

If you don't have Java 8, you could just use a regular expression to accomplish your task.

string.matches("^.*?(<|>|p|1|&).*$") should do the trick.

EDIT:

I wonder which solution performs faster. Eventhough JIT can inline all the lambda goodiness, Stream(s) might be slower than using a regular expression. I could be completely wrong and I'd appreciate any insight into this from Java 8 stalwarts.

Srini
  • 1,626
  • 2
  • 15
  • 25