3

I need to write Comparator for InetSocketAddress so I can use this class in a TreeSet. They need to be comparable by address and port.

The code would look something like this, but the problem is I don't know how to compare addresses and ports by <(-1),>(1),=(0)

TreeSet<InetSocketAddress> _tree = new TreeSet<InetSocketAddress> 
    (new Comparator<InetSocketAddress>() {

    public int compare(InetSocketAddress o1, InetSocketAddress o2) {

        ///?????
        return 0;
    }
});

Edit... the actual question. How to compare InetSocketAddress.

BlackCat
  • 329
  • 5
  • 17
  • 2
    *"the problem is.."* You forgot to ask a question? (And no, `///?????` is *not* a question). – Andrew Thompson Jul 11 '11 at 01:12
  • In order to be consistent with the [`InetSocketAddress.equals(Object)`](http://docs.oracle.com/javase/7/docs/api/java/net/InetSocketAddress.html#equals%28java.lang.Object%29) method, I suggest you take a look at how the `InetSocketAddress.equals(Object)` method is implemented in [OpenJDK 7](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/net/InetSocketAddress.java#InetSocketAddress.equals%28java.lang.Object%29). Be sure to check each answer on this post if they adhere to the `equals - compareTo` convention. – mucaho Nov 02 '14 at 14:17
  • If TreeSet is not a hard constraint, I would run with HashMap – Sam Ginrich Feb 25 '23 at 15:10

5 Answers5

5

Codes with InetSocketAddress#getHostName comparation is incorrect because when hostname is resolved it can be null. Look at constructor:

public InetSocketAddress(String hostname, int port) {
if (port < 0 || port > 0xFFFF) {
    throw new IllegalArgumentException("port out of range:" + port);
}
if (hostname == null) {
    throw new IllegalArgumentException("hostname can't be null");
}
try {
    addr = InetAddress.getByName(hostname);
} catch(UnknownHostException e) {
    this.hostname = hostname;
    addr = null;
}
this.port = port;
}

The code wchich uses only IP is incorrect too - hostname can be unresolved. This should be quite efficient:

Integer getIp(InetSocketAddress addr) {
    byte[] a = addr.getAddress().getAddress();
    return ((a[0] & 0xff) << 24) | ((a[1] & 0xff) << 16) | ((a[2] & 0xff) << 8) | (a[3] & 0xff);
}

public int compare(InetSocketAddress o1, InetSocketAddress o2) {
    //TODO deal with nulls
    if (o1 == o2) {
        return 0;
    } else if(o1.isUnresolved() || o2.isUnresolved()){
        return o1.toString().compareTo(o2.toString());
    } else {
        int compare = getIp(o1).compareTo(getIp(o2));
        if (compare == 0) {
            compare = Integer.valueOf(o1.getPort()).compareTo(o2.getPort());
        }
        return compare;
    }
}
zacheusz
  • 8,750
  • 3
  • 36
  • 60
  • 1
    There are 2 possibilities above: comparative order of numbers does not match the comparative order of strings. or, that it does. In first case (which is not true due to ascii table layout) the above is potentially broken if considering a1, a2, and a3 where only a2 is not resolved. In the second case, they are equivalent, in which case the added complexity is not really buying you anything substantial (beyond the ~ cost of .toString) – alphazero Jul 11 '11 at 19:49
  • Hostname is resolved in constructor. When address is resolved the hostname remains null. InetSocketAddress with resolved host is not equal to InetSocketAddress with same unresolved host. So conflicting situation never happen. – zacheusz Jul 11 '11 at 19:58
1

<(-1),>(1),=(0) is needed for sorting I think you can assume ordering - for example:

public int compare(InetSocketAddress o1, InetSocketAddress o2) {
    //TODO deal with nulls
    if(o1 == o2){
        return 0;
    } else {
        return o1.toString().compareTo(o2.toString());
    }
}

This is not very efficient but it ilustrates the idea. Comparing IP (when avaliable, resolved) can be faster.

zacheusz
  • 8,750
  • 3
  • 36
  • 60
1

Depending on whether you need a particular order, or some kind of resolution, this may be correct:

class ISC implements Comparator<InetSocketAddress>
{

@Override
    public int compare(InetSocketAddress o1, InetSocketAddress o2)
    {
        return o1.toString().compareTo(o2.toString());
    }
}
Ed Staub
  • 15,480
  • 3
  • 61
  • 91
1

Try using CompareToBuilder, and pass in getAddress().getHostAddress() and getPort().

jkraybill
  • 3,339
  • 27
  • 32
1

You just need to pick a convention.

e.g.

  1. choose an arbitrary ordering scheme for IP addresses. It just needs to be consistently applied.

    Obviously the dot notation suggests a natural way of doing this, so you can break down e.g. 127.0.0.1 into {127, 0, 0, 1} and compare it against another e.g. {84, 23, 10, 2} to be explicit.

    Another option is to convert the address part to long number and just compare those numbers. This is basic hashing.

  2. choose an arbitrary ordering scheme for port numbers. Seems sensible to just use the numeric semantics and e.g. treat port 55 as less than port 999 (though as far as IP protocol is concerned, such a semantic view is meaningless.)

pseudo-code:

compare (addr1, addr2)
   if addr1.host > addr2.host return 1;
   else if addr1.host < addr2.host return -1;

   if addr1.port > addr2.port return 1;
   else if addr1.port < addr2.port return -1;

   return 0;
user207421
  • 305,947
  • 44
  • 307
  • 483
alphazero
  • 27,094
  • 3
  • 30
  • 26
  • The pseudo-code is perfect. ad point 1: IMHO you can't use ordering scheme for IP addresses only - the hostname can be unresolved. – zacheusz Jul 11 '11 at 09:41
  • 1
    @zacheusz: I agree re unresolved. thus "Another option is to convert the address part to long number and just compare those numbers. This is basic hashing." e.g take first 8 bytes of SHA_1("www.the-host-name.com" and use that to compare addresses. – alphazero Jul 11 '11 at 19:26
  • You are right. Converting to a number is OK. However SHA1 is a bad example here - it is hihgly inefficient in this case. – zacheusz Jul 11 '11 at 19:51
  • @zacheusz: Agreed. I would shop for a faster hash algo ;) – alphazero Jul 11 '11 at 20:04