Java比较器用于InetSocketAddress

3

我需要为InetSocketAddress编写一个Comparator,以便可以在TreeSet中使用该类。它们需要按地址和端口进行比较。

代码可能看起来像这样,但问题是我不知道如何通过<(-1)>(1),=(0)来比较地址和端口。

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

    public int compare(InetSocketAddress o1, InetSocketAddress o2) {

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

编辑... 实际问题是如何比较InetSocketAddress。


2
"问题是..." 你忘记提出问题了吗?(不,///????? 不是一个问题)。 - Andrew Thompson
为了与InetSocketAddress.equals(Object)方法保持一致,我建议您查看OpenJDK 7InetSocketAddress.equals(Object)方法的实现。请确保检查此帖子中的每个答案是否符合equals - compareTo约定。 - mucaho
如果TreeSet不是硬性限制,我会选择HashMap<InetSocketAddress,RelatedInfo>。 - Sam Ginrich
5个回答

5

InetSocketAddress#getHostName比较的代码是不正确的,因为当主机名被解析时它可能为空。请看构造函数:

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;
}

仅使用IP的代码也是不正确的 - 主机名可能无法解析。以下代码应该相当有效:

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;
    }
}

1
以上有两种可能性:数字的比较顺序与字符串的比较顺序不匹配,或者它们是匹配的。在第一种情况下(由于ASCII表布局而不成立),如果考虑a1、a2和a3,其中只有a2未解决,则上述情况可能会出现问题。在第二种情况下,它们是等效的,此时增加的复杂性实际上并没有为您带来任何实质性的收益(除了.toString的成本)。 - alphazero
主机名在构造函数中被解析。当地址被解析时,主机名仍然为null。具有已解析主机的InetSocketAddress与具有相同未解析主机的InetSocketAddress不相等。因此,冲突情况永远不会发生。 - zacheusz

1

<(-1),>(1),=(0) 是用于排序的必需元素。 我认为你可以假设有序 - 例如:

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

这并不是非常高效,但它阐明了这个想法。比较IP地址(如果可用,则解析)可能更快。


1
compareTo,不是compareWith,我想。 - Ed Staub

1

根据您需要的是特定顺序还是某种解决方案,这可能是正确的:

class ISC implements Comparator<InetSocketAddress>
{

@Override
    public int compare(InetSocketAddress o1, InetSocketAddress o2)
    {
        return o1.toString().compareTo(o2.toString());
    }
}

1
尝试使用CompareToBuilder,并传入getAddress().getHostAddress()getPort()

1
你不能仅使用getAddress()方法 - 如果主机名未解析,它将返回null。 - zacheusz

1

你只需要选择一个约定。

例如:

  1. 为IP地址选择任意排序方案。它只需要被一致地应用。

    显然,点表示法建议采用自然的方式进行比较,因此您可以将127.0.0.1分解为{127, 0, 0, 1}并将其与另一个如{84, 23, 10, 2}进行比较以明确表示。

    另一种选择是将地址部分转换为长数字,然后仅比较这些数字。这是基本哈希。

  2. 为端口号选择任意排序方案。似乎合理的做法是仅使用数字语义,例如将端口55视为小于端口999(尽管就IP协议而言,这样的语义观点是无意义的)。

伪代码:

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;

伪代码很完美。 附加点1:在我看来,你不能仅针对IP地址使用排序方案 - 主机名可能无法解析。 - zacheusz
1
@zacheusz:我同意未解决的问题。因此,“另一个选择是将地址部分转换为长数字,然后仅比较这些数字。这是基本散列。”例如,取SHA_1(“www.the-host-name.com”的前8个字节,并使用它来比较地址。 - alphazero
你是对的。将其转换为数字没有问题。然而,在这里使用SHA1是一个很糟糕的例子 - 在这种情况下它非常低效。 - zacheusz
@zacheusz:同意。我会寻找更快的哈希算法 ;) - alphazero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接