实现双向流的可比较接口

时间:2010-08-17 09:34:17

标签: java network-programming override compare comparable

这段代码应该将两个方向的流量视为一个流程。 例如:

srcAddr,dstAddr,srcPort,dstPort
192.168.1.65, 217.174.16.1, 123456,80

应与

相同
217.174.16.1, 192.168.1.65,80,123456

另一个例子:

192.168.1.65, 217.174.16.1, 12345, 80, TCP
217.174.16.1, 192.168.1.65, 80, 12345, TCP
192.168.1.65, 217.174.16.1, 12345, 80, TCP
217.174.16.1, 192.168.1.65, 80, 12345, TCP

我想保持这样:

Flow 1: key---> value (keeps statistics about each packet, like length and timeArrival)

[192.168.1.65, 217.174.16.1, 12345, 80] ----> [(outgoing, 1,2)(incoming,3,4)()()...]

192.168.1.65, 69.100.70.80, 98521, 80 69.100.70.80, 192.168.1.65, 80, 98521 192.168.1.65, 69.100.70.80, 98521, 80 69.100.70.80, 192.168.1.65, 80, 98521 192.168.1.65, 69.100.70.80, 98521, 80 69.100.70.80, 192.168.1.65, 80, 98521

Flow 2: [192.168.1.65, 69.100.70.80, 98521, 80] --> [(outgoing, 1,2)(incoming,3,4)()()...]

我应该如何改变才能获得结果? [即时通讯使用hashMap,这类Flow是我的关键]

 package myclassifier;
 public class Flows implements Comparable<Flows> {

String srcAddr = "", dstAddr = "", protocol = "";
int srcPort = 0, dstPort = 0;

public Flows(String sIP, String dIP, int sPort, int dPort){
    this.srcAddr = sIP;
    this.dstAddr = dIP;
    this.srcPort = sPort;
    this.dstPort = dPort;
    //this.protocol = protocol;

}
public Flows(){

}

public int compareTo(Flows other) {
    int res = 1;
    if(this.equals(other)){
        return res=0;
    }else
        return 1;
}



 @Override
public int hashCode() {

    final int prime = 31;
    int result = 1;
    result = prime * result + ((dstAddr == null) ? 0 : dstAddr.hashCode());
    result = prime * result + dstPort;
    result = prime * result + ((srcAddr == null) ? 0 : srcAddr.hashCode());
    result = prime * result + srcPort;
    return result;

}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;

    if (getClass() != obj.getClass())
        return false;

    Flows other = (Flows) obj;

    if (dstAddr == null) {
        if (other.dstAddr != null)
            return false;
    } else if (!dstAddr.equals(other.dstAddr))
        return false;
    if (dstPort != other.dstPort)
        return false;
    if (srcAddr == null) {
        if (other.srcAddr != null)
            return false;
    } else if (!srcAddr.equals(other.srcAddr))
        return false;
    if (srcPort != other.srcPort)
        return false;
    return true;

}

 @Override
public String toString() {
return String.format("[%s, %s, %s, %s, %s]", srcAddr, dstAddr, srcPort, dstPort, protocol);
}


}

3 个答案:

答案 0 :(得分:2)

最简洁的方法可能是定义这些方法:

  • Flows reverse()返回给定Flows的反方向Flows
  • Flows canon()返回Flows的规范化形式
    • 您可以定义,例如如果Flows
    • ,则srcAddr.compareTo(dstAddr) <= 0是正典
    • 否则,根据定义,reverse()是正典

然后,对于非定向比较,您可以简单地比较两个流的规范形式。使用这些方法可以使其余逻辑非常干净和可读(参见下面的代码)。


ComparatorComparable上,与equals

的一致性

使用上面的reverse()概念,如果你总是希望f.equals(f.reverse()),那么也许首先不应该有任何方向性的概念。如果是这种情况,那么规范化是最好的方法。

如果f通常不是equals(f.reverse()),但您可能希望ff.reverse()与0进行比较,则不应使用Comparable,因为这样做会强加一种与平等不一致的自然顺序。

来自文档:

  

当且仅当C具有与{{1}相同的布尔值时,类equals的自然顺序被认为 e1.compareTo(e2) == 0一致对于e1.equals(e2)的每个e1e2

     

强烈建议(尽管不是必需的)自然排序与C一致。

也就是说,不应在equals中强加与Comparable不一致的自然排序,而应改为提供非定向Comparator

作为类比,请将此情况与提供StringComparator<String> CASE_INSENSITIVE_ORDER进行比较,该Java Tutorials/Collections/Object Ordering允许两个非equals的字符串通过不区分大小写与0进行比较。

所以,在这里你写了一个equals,它允许两个Comparator<Flows>不是Flows,通过方向不敏感来与0进行比较。

另见

相关问题


示例实现

以下是具有equalsEdge的{​​{1}}类的示例实现,其方向自然顺序与from一致,也提供了非to -directional equals

然后使用3种Comparator进行测试:

  • Set,用于测试HashSetequals
  • A hashCode,用于测试自然排序
  • 使用自定义TreeSet的{​​{1}}来测试非方向性

实施简洁明了,应具有指导意义。

TreeSet

输出为(Arrays.hashCode(Object[])),注释:

Comparator

请注意,在非定向import java.util.*; class Edge implements Comparable<Edge> { final String from, to; public Edge(String from, String to) { this.from = from; this.to = to; } @Override public String toString() { return String.format("%s->%s", from, to); } public Edge reverse() { return new Edge(to, from); } public Edge canon() { return (from.compareTo(to) <= 0) ? this : this.reverse(); } @Override public int hashCode() { return Arrays.hashCode(new Object[] { from, to }); } @Override public boolean equals(Object o) { return (o instanceof Edge) && (this.compareTo((Edge) o) == 0); } @Override public int compareTo(Edge other) { int v; v = from.compareTo(other.from); if (v != 0) return v; v = to.compareTo(other.to); if (v != 0) return v; return 0; } public static Comparator<Edge> NON_DIRECTIONAL = new Comparator<Edge>() { @Override public int compare(Edge e1, Edge e2) { return e1.canon().compareTo(e2.canon()); } }; } public class Main { public static void main(String[] args) { testWith(new HashSet<Edge>()); testWith(new TreeSet<Edge>()); testWith(new TreeSet<Edge>(Edge.NON_DIRECTIONAL)); } public static void testWith(Set<Edge> set) { set.clear(); set.add(new Edge("A", "B")); set.add(new Edge("C", "D")); System.out.println(set.contains(new Edge("A", "B"))); System.out.println(set.contains(new Edge("B", "A"))); System.out.println(set.contains(new Edge("X", "Y"))); System.out.println(set); set.add(new Edge("B", "A")); set.add(new Edge("Z", "A")); System.out.println(set); System.out.println(); } } 中,// HashSet // add(A->B), add(C->D) true // has A->B? false // has B->A? false // has X->Y? [C->D, A->B] // add(B->A), add(Z->A) [B->A, C->D, Z->A, A->B] // TreeSet, natural ordering (directional) // add(A->B), add(C->D) true // has A->B? false // has B->A? false // has X->Y [A->B, C->D] // add(B->A), add(Z->A) [A->B, B->A, C->D, Z->A] // TreeSet, custom comparator (non-directional) // add(A->B), add(C->D) true // has A->B? true // has B->A? false // has X->Y? [A->B, C->D] // add(B->A), add(Z->A) [A->B, Z->A, C->D] 被规范化为TreeSet,这就是它按此顺序出现在Z->A之前的原因。同样地,A->Z被规范化为C->D,这已经在集合中,这解释了为什么那里只有3 B->A

要点

  • A->B是不可变的
  • {{3}}用于方便;无需编写所有公式
  • 如果自然排序与Edge一致,您可以在Edge
  • 中使用equals
  • 使用compareTo == 0中的多步equals逻辑来简洁明了
  • returncompareTo大大简化了无方向性比较
    • 只需按照自然顺序比较他们的规范化形式

另见

  • Effective Java 2nd Edition
    • 第8项:在覆盖reverse()
    • 时遵守一般合同
    • 第9项:覆盖canon()
    • 时始终覆盖equals
    • 第10项:始终覆盖hashCode
    • 第12项:考虑实施equals
    • 第15项:尽量减少可变性
    • 第36项:始终使用toString注释
    • 第47项:了解和使用库

答案 1 :(得分:0)

关键是要正确实现equals方法。在您的equals方法中,您返回false,目标地址不匹配。这是您需要添加其他逻辑以检查相等性的位置,因为您希望具有双向相等性。第一轮的平等应该是对源,目的地,港口的平等的检查。第二遍应该是源和目的地的反向相等。您还需要对默认值进行特殊设置,例如在您的示例中,将portno(80)的默认值排除为true。

答案 2 :(得分:0)

我不知道这是否会对你有所帮助。但它可以按照你说的方向工作

import java.util.HashSet;
 public class Flows implements Comparable<Flows> {

String srcAddr = "", dstAddr = "", protocol = "";
int srcPort = 0, dstPort = 0;

public Flows(String sIP, String dIP, int sPort, int dPort){
    this.srcAddr = sIP;
    this.dstAddr = dIP;
    this.srcPort = sPort;
    this.dstPort = dPort;
    //this.protocol = protocol;

}
public Flows(){

}

public int compareTo(Flows other) {

    if(this.equals(other)){
        return 0;
    }else
        return 1;
}



 @Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((dstAddr == null) ? 0 : dstAddr.hashCode())+((srcAddr == null) ? 0 : srcAddr.hashCode());
    result = prime * result + dstPort+srcPort;
    return result;

}

@Override
public boolean equals(Object obj) {
   if (this == obj)
        return true;

   if(obj instanceof Flows)
   {
    Flows c=(Flows)obj;
    if(srcAddr.equals(c.dstAddr) && dstAddr.equals(c.srcAddr) &&srcPort==c.dstPort && dstPort==c.srcPort)
      return true;

    if(srcAddr.equals(c.srcAddr) && dstAddr.equals(c.dstAddr) && srcPort==c.srcPort && dstPort==c.dstPort)
        return true;
   }
    return false;

}

 @Override
public String toString() {
return String.format("[%s, %s, %s, %s, %s]", srcAddr, dstAddr, srcPort, dstPort, protocol);
}

public static void main(String[] args) {
    Flows f1=new Flows("192.168.1.65","217.174.16.1", 123456,80);
    Flows f2=new Flows("217.174.16.1","192.168.1.65",80,123456);

    Flows f3=new Flows("192.168.1.66","217.174.16.1", 123456,80);
    Flows f4=new Flows("217.174.16.1","192.168.1.66",80, 123456);
    System.out.println(f1.hashCode()+ " "+f2.hashCode());
    HashSet<Flows> hh=new HashSet<Flows>();
    hh.add(f1);
    hh.add(f2);
    hh.add(f3);
    hh.add(f4);
    System.out.println(f1.compareTo(f2));
    System.out.println(hh);
}
}

我已经使用hashset来测试。所以它也适用于hashmap。