简体   繁体   中英

Check if int is in list of ints in Java (with some constraints)

I'm looking for a simple and elegant one line way to check if the int result of an expression is contained in a list of integers.

Example: Check if foo(…) produces either 1, 2 or 7.

Constraints:

  • Do not use external libraries, only the JRE.
  • Java 8 can be assumed.
  • Solution does not involve (auto)boxing. I personally don't like boxing :-P
  • foo(…) should only be evaluated once.
  • No additional method should be required.
  • No mutable variables should be required.
  • One or two final variables may be used, including lambdas. But foo(…) (and its result) and the list of int s should not be part of this variables.
  • The list of integers to check against is known at compile time and may be up to 5 numbers.

To illustrate: java.util.Arrays.asList(1, 2, 7).contains(foo(…)) meets all criteria except it requires boxing.


Note: I'm only interested what possible solutions could look like. In practice I would probably use my illustration example, ArrayUtils.contains(…) from Commons Lang or Ints.contains(…) from Guava .

I'll add three possible solutions later if they don't come up anyway. But I wonder if there are any more elegant solutions, as mine are not really that elegant and I'm a bit surprised Java does not seem to provide something like this out of the box.

二分查找呢?

Arrays.binarySearch(new int[] {1, 2, 7}, foo(...)) >= 0

You could filter out the results using a lambda instead, if you're guaranteed a List . If you're not, you can wrap it inside of a Arrays#asList operation.

This will short-circuit on the first value that evaluates to true .

List<Integer> intList = Arrays.asList(foo());
boolean exists = intList.stream().anyMatch(x -> x == 1 || x == 2 || x == 7);

There is going to be a level of boxing due to the way that the stream has to be created; if you really want to avoid it for whatever reason, you've got a couple of options.

  • Create an int[] , then create an IntStream from it:

     boolean exists = Arrays.stream(new int[](foo())) .anyMatch(x -> x == 1 || x == 2 || x == 7);
  • Convert to an IntStream as an intermediate step (does involve boxing to a degree)

     boolean exists = intList.stream() .mapToInt(Integer::intValue) .anyMatch(x -> x == 1 || x == 2 || x == 7);

As promised in the question, here are my solutions:

Besides the one of Tagir Valeev using Arrays.binarySearch(…) I did some variations using lambdas:

  • Building an IntPredicate by joining multiple predicates:

     final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2; intEquals.apply(1).or(intEquals.apply(2)).or(intEquals.apply(7)).test(foo(…));

    This makes adding a new number to the list very verbose.

    First building a IntPredicate avoids the need to encapsulate the result of foo(…) somehow to prevent evaluating it more than once.

  • This one does the same as the previous solution, but adding a new number is less verbose (at the cost of a more complex lambda):

     final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2; final Function<Function<IntStream.Builder, IntStream.Builder>, IntPredicate> pred = c -> c.apply(IntStream.builder()).build().mapToObj(intEquals).reduce(IntPredicate::or).get(); pred.apply(b -> b.add(1).add(2).add(7)).test(foo(…));
  • Building a IntPredicate from an array:

     final Function<int[], IntPredicate> oneOf = ints -> testInt -> IntStream.of(ints).anyMatch(arrayInt -> testInt == arrayInt); oneOf.apply(new int[] { 1, 2, 7 }).test(foo(…));

    This is in principe similar to the answer provided by Makoto but the other way around (he put the single value in front of the stream).

  • Using IntStream.of(…) instead of writing new int[] {…} :

     final Function<IntStream, IntPredicate> oneOf = ints -> testInt -> ints.anyMatch(streamInt -> testInt == streamInt); oneOf.apply(IntStream.of(1, 2, 7)).test(foo(…));

    This could be even shortened using a static import of IntStream.of .

  • With the use of BiPredicate the call can be made even shorter:

     final IntFunction<IntPredicate> intEquals = v1 -> v2 -> v1 == v2; final BiPredicate<IntStream, IntSupplier> oneOf = (list, value) -> list.anyMatch(intEquals.apply(value.getAsInt())); oneOf.test(of(1, 2, 7), () -> foo(…));

    Here a IntSupplier is used to avoid the recalculation of foo(…) .


So the bottom line for me is: Java does not provide a concise, ready to use function that meets the requirements. The binarySearch method works but requires a sorted list of integers and does not express the intend very well. The lambda variants are all a bit complex. So apparently I'll have to drop some of the constraints :-P

But it was an interesting challenge to look at, in my opinion…

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM