views:

376

answers:

5

In Java, i have an arrayList of ip address. how do i find the min and max ?

I have used the Collection.min() but it doesnt work given a case like :

192.168.0.1  <--min 
192.168.0.250
192.168.0.9  <--max

how do i return

192.168.0.1  <--min
192.168.0.250 <--max

instead?


ArrayList is retrieve from the database. I would need to do this operation per tick (each tick is at 5 sec interval). The number of IP address would hit a max of probably 300.

+11  A: 

Convert the IP address into a long integer, then sort it. 192.168.0.1 can be converted to an integer using binary arithmetic/operators:

( 192 << 24 ) + ( 168 << 16 ) + ( 0 << 8 ) + ( 1 << 0 )

and so on. Do read the comments below about using the correct data type.

Salman A
Almost: this will fail for IP addresses whose top octet is at least 128, since the resulting int will be negative. Use a `long` instead. Also he probably wants the original IP, so best bet is to create a `TreeMap` from `long` IP value to `String` representation. Iterating in order gives IP addresses in order then.
Sean Owen
I am not a Java guru, so I suggest that you use the correct data types. I believe Java integers are 8 bytes, if that's the case then the above calculations are OK.
Salman A
@Salman: No Java `int` is 32-bit signed value. Java `long` is a 64-bit (8 byte) signed and would be required here as Sean states.
Kevin Brock
@sean - agreeing with use of long. I disagree with working with strings. I have a subnet palnner / calcualtor and I found out a long time ago it was better to manipulate the longs and reproduce the strings when needed. In this case, converting one IP, it won't be noticeable. In my case where there could be millions of addresses it was important.
dbasnett
+6  A: 

Are you storing the IP addresses as String instances? This is likely the case, because String are sorted lexicographically, meaning "10" < "2".

If you want to sort them numerically, there are several ways:

  • Instead of putting them into a List<String>, put them into a List<Integer>
    • or maybe even a SortedSet<Integer>
  • Keep the List<String>, but provide a custom comparator that convert the String to numerical value for comparison
    • may not be most efficient, but works without major changes to existing infrastructure
      • although perhaps major changes isn't a bad idea to begin with...

Here's an example that combines a bit of both into one:

import java.util.*;

public class IPSorter {
    static Long toNumeric(String ip) {
        Scanner sc = new Scanner(ip).useDelimiter("\\.");
        return 
            (sc.nextLong() << 24) + 
            (sc.nextLong() << 16) + 
            (sc.nextLong() << 8) + 
            (sc.nextLong()); 
    }
    public static void main(String[] args) {
        Comparator<String> ipComparator = new Comparator<String>() {
            @Override public int compare(String ip1, String ip2) {
                return toNumeric(ip1).compareTo(toNumeric(ip2));
            }       
        };
        SortedSet<String> ips = new TreeSet<String>(ipComparator);
        ips.addAll(Arrays.asList(
            "192.168.0.1", "192.168.0.250", "192.168.0.9", "9.9.9.9"
        ));
        System.out.println(ips);
        // "[9.9.9.9, 192.168.0.1, 192.168.0.9, 192.168.0.250]"
    }
}

API links

polygenelubricants
+2  A: 

If you treat the IP addresses as an integer (long), you can then sort on it. Write a custom comparer that can split the IP address into an array of ints, and then create a total int value by doing the following.

//Split and convert the address into an array of ints...

addressIntValue =  address[0] * 255 * 255 * 255
addressIntValue += address[1] * 255 * 255
addressIntValue += address[2] * 255
addressIntValue += address[3]

You can then sort on "addressIntValue".

chibacity
if you only multiply with 255, the values for "0.0.0.255" and "0.0.1.0" are identical -> you have to multiply with 2^8 = 256 (of better: use bit shifts, a*2^b = a<<b).-> see Salman's answer, with Sean Owen's addition (long instead of int)
Peter Walser
Ah yes, you are quite right. Well spotted.
chibacity
+1  A: 

In case you don't want to parse the IP by hand, you can use InetAddress class

InetAddress ia1 = InetAddress.getByName("192.168.0.9");
InetAddress ia2 = InetAddress.getByName("192.168.0.234");

System.out.println(ia1.hashCode() < ia2.hashCode());

I'm using the hashCode() method because it returns the address as a number for ipv4. You can also compare arrays returned by InetAddress.getAddress()

EDIT Using hashCode() is an undocumented feature and there may be problems if you have both ipv6 and ipv4 addresses. So it is best to compare arrays of bytes or convert them to a number by hand.

tulskiy
I would have preferred this approach were it not for its use of the (undocumented) implementation details of `InetAddress#hashCode()` (as it is, I can't even tell if this code is correct without running it, or looking at InetAddress's source, there's also the concern of the implementation changing in a future release).
Jack Leow
@jack-leow While the javadoc doesn't explicitly mention that the address is also the hashCode, it makes perfect sense and there's absolutely _no_ reason to expect it to ever change in a future release (not to mention Java traditionally maintains backwards compatibility, almost to a fault). An address is, by its very nature, the definition of a perfect hashCode.
nicerobot
@jack-leow OK, I have edited my answer.
tulskiy
A: 

Referring to Java – IP Address to Integer and back

public static String intToIp(int i) {
    return ((i >> 24 ) & 0xFF) + "." +
           ((i >> 16 ) & 0xFF) + "." +
           ((i >>  8 ) & 0xFF) + "." +
           ( i        & 0xFF);
}

public static Long ipToInt(String addr) {
    String[] addrArray = addr.split("\\.");

    long num = 0;
    for (int i=0;i<addrArray.length;i++) {
        int power = 3-i;

        num += ((Integer.parseInt(addrArray[i])%256 * Math.pow(256,power)));
    }
    return num;
}

Retrieving from the database the Ip address String, I converted all to a ArrayList and then apply the Collection.min() Then I convert back the long to int and then back to String. To obtain a sorted String of ip addresses.

Thanks

zeroin23