ccpizza ccpizza - 7 months ago 14
Java Question

Java: how to check for boolean conditions in two-dimensional lists/arrays?

Given a table-like data structure, e.g. an arraylist of arraylists (a 2D array, or some other iterable data structure), what would be the cleanest way to check the elements against specific rules?

For example given the following data:

[true, false, false, true]
[false, false, false, false]
[true, false, false, false]


How can I enforce that either of the three conditions are satisfied:


  • all elements in all rows are
    true

  • all elements in all rows are
    false

  • if a row contains mixed values, then only the first element can be
    true
    , otherwise the validation must fail.



For example, the data above must not pass validation because of a
true
value in position [0, 3].

UPDATE:
Apparently one way to avoid looping in Java 8 is using
myBoolArrayList.stream().allMatch(Boolean::booleanValue);
and
myBoolArrayList.stream().noneMatch(Boolean::booleanValue)
; — this would cover the first two conditions, not very clear yet about the third condition.

Answer

Note that by looking at your three rules together, you don’t need to do individual checks:

A list can only contain all-true or all-false, but if either is valid, the combined rule is “all (boolean) values must be the same” which can be tested in a single expression like !booleanList.contains(!booleanList.get(0)). But there’s the third alternative:

if a row contains mixed values, then only the first element can be true, otherwise the validation must fail.

This basically says: if there is a false value, all but the first element must be false as well. This rule makes the other two obsolete:

  • if there is no false value, then all values are true
  • if there is a false value, the first element might be true, so all values being false is a special case of this rule
  • in other words, if there is a false value, the value of the first element is irrelevant

Therefore, we can short-cut the test by looking at any other element than the first one, e.g. at index 1 and selecting one rule to test, based on that value:

  • if the value is true, all values are required to be true
  • if the value is false, all but the first values are required to be false
  • as a corner case, if the list is smaller than two elements, there is no contradicting element, so the list is valid

So the entire condition can be expressed as

list.size()<2 ||
     list.get(1)? // has a TRUE at a position other than the first
     !list.contains(false): // all are TRUE
     !list.subList(1, list.size()).contains(true); // all except the first are FALSE

Regarding the Stream API,

  • !list.contains(x) can be expressed as list.stream().noneMatch(Predicate.isEqual(x)) and
  • !list.subList(1, list.size()).contains(x) can be expressed as list.stream().skip(1).noneMatch(Predicate.isEqual(x)),

but there’s no reason to use the Stream API here.

However, for validating a List of Lists, you can use the Stream API to check whether all sublists fulfill the condition:

static boolean validate(List<List<Boolean>> list2D) {
    return list2D.stream().allMatch(list-> list.size()<2 ||
        list.get(1)? !list.contains(false): !list.subList(1, list.size()).contains(true)
    );
}