Java equivalent of C#'s 'Enumerable.Any'
Asked Answered
S

4

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?

Scoggins answered 19/6, 2015 at 14:28 Comment(1)
Just for completeness: you can also use .Any(string.Contains) rather than the more verbose .Any(w => string.Contains(w)) in C#.Moneymaking
B
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).*$");
  }
}
Bomb answered 19/6, 2015 at 14:34 Comment(8)
@MarcoAcierno String::contains make reference to a static method, which does not exist. docs.oracle.com/javase/tutorial/java/javaOO/…Retroaction
My bad, I removed the comment. I missed the fact that string is a variableMolarity
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.Amr
Thank you. This is a perfect solution. Bruce Wayne's solution using regex can be useful if people are not working with JAVA 8.Scoggins
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...Lituus
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");?Singhalese
@Singhalese 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).Bomb
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.Singhalese
O
9

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

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

}
Occasional answered 19/6, 2015 at 14:34 Comment(0)
S
7

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

Shirk answered 19/6, 2015 at 14:41 Comment(0)
A
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.

Amr answered 19/6, 2015 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.