简体   繁体   中英

Java stream to validate int in a map

I have the below code, It just checks the given number against a map that has a number ranges. The method isWithinAnyRange would check if the number is in the range including the start but excluding the end.

This works fine but I wanted to simplify that method with a stream.

public static void main(String [] args)
{
    Map<Integer,Integer> intMap = new HashMap<>();

    intMap.put(0,2);
    intMap.put(3,4);
    intMap.put(6,9);
    System.out.println(isWithinAnyRange(intMap,2)); //false
    System.out.println(isWithinAnyRange(intMap,3)); //true
    System.out.println(isWithinAnyRange(intMap,4)); //false
    System.out.println(isWithinAnyRange(intMap,6)); //true
    System.out.println(isWithinAnyRange(intMap,7)); //true
}

public static boolean isWithinAnyRange(Map<Integer,Integer> intMap, Integer num){
   for(Map.Entry<Integer,Integer> entry: intMap.entrySet()){
       if(num>=entry.getKey() && num<entry.getValue()){
           return true;
       }
   }
   return false;
}

When all your ranges are non-overlapping, like in your example, you are better off using a TreeMap :

public static void main(String [] args) {
    TreeMap<Integer,Integer> intMap = new TreeMap<>();
    intMap.put(0,2);
    intMap.put(3,4);
    intMap.put(6,9);
    System.out.println(isWithinAnyRange(intMap,2)); //false
    System.out.println(isWithinAnyRange(intMap,3)); //true
    System.out.println(isWithinAnyRange(intMap,4)); //false
    System.out.println(isWithinAnyRange(intMap,6)); //true
    System.out.println(isWithinAnyRange(intMap,7)); //true
}

public static boolean isWithinAnyRange(NavigableMap<Integer,Integer> intMap, Integer num) {
    Map.Entry<Integer,Integer> e = intMap.floorEntry(num);
    return e != null && num < e.getValue();
}

This is not only simpler, it is more efficient than a linear search. More formally, the time complexity is O(log n) rather than O(n).

Another option for small to medium sized ranges is a BitSet :

BitSet bitSet = new BitSet();
bitSet.set(0,2);
bitSet.set(3,4);
bitSet.set(6,9);
System.out.println(bitSet.get(2)); //false
System.out.println(bitSet.get(3)); //true
System.out.println(bitSet.get(4)); //false
System.out.println(bitSet.get(6)); //true
System.out.println(bitSet.get(7)); //true

This even has O(1) time complexity. It stores for all values between zero and the largest number whether they are contained or not, but it only uses one bit per number, eg for this example where all values are in within a 0 .. 64 range, a single long value will be used behind the scenes. But it's not suitable when you have very large ranges or distances. Also, when the distances are small but there are negative values or all values far away from zero, an offset would be needed to adjust the values stored in the bitset.

If you just want to know if an entry matches:

boolean isMatch = 
    intMap.entrySet().stream()
        .anyMatch(e -> num >= e.getKey() && num < e.getValue());

If you want to find the matching entry:

Optional<Map.Entry<Integer, Integer>> match =
    intMap.entrySet().stream()
        .filter(e -> num >= e.getKey() && num < e.getValue())
        .findAny();

If there could be multiple matching entries you can collect them back into a map:

Map<Integer, Integer> matches =
    intMap.entrySet().stream()
        .filter(e -> num >= e.getKey() && num < e.getValue())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

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