реализовать сопоставимый интерфейс для двунаправленных потоков - PullRequest
0 голосов
/ 17 августа 2010

этот фрагмент кода должен рассматривать потоки в обоих направлениях как один поток. например:

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, и этот класс потоков - мои ключи]

 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 ]

2 голосов
/ 17 августа 2010

Вероятно, самый чистый способ сделать это - определить следующие методы:

  • Flows reverse(), которое возвращает обратное направление Flows данного Flows
  • Flows canon(), который возвращает канонизированную форму Flows
    • Вы можете определить, например, Flows является каноном, если srcAddr.compareTo(dstAddr) <= 0
    • В противном случае его reverse() является каноном по определению

Тогда для ненаправленного сравнения вы можете просто сравнить канонические формы двух потоков. Наличие этих методов делает остальную логику очень чистой и удобочитаемой (см. Код ниже).


Вкл. Comparator, Comparable и согласованность с equals

Используя приведенную выше концепцию reverse(), если вы хотите f.equals(f.reverse()) всегда, то, возможно, вообще не должно быть понятия направленности. Если это так, то каноникализация - лучший подход.

Если f обычно не equals(f.reverse()), и все же вы можете захотеть, чтобы f и f.reverse() сравнивались с 0, тогда Comparable не следует использовать, потому что при этом наложить естественный порядок, который не соответствует равным.

Из документации:

Естественный порядок для класса C называется в соответствии с equals тогда и только тогда, когда e1.compareTo(e2) == 0 имеет то же логическое значение, что и e1.equals(e2) для каждых e1 и e2 класса C.

Настоятельно рекомендуется (хотя и не обязательно), чтобы естественный порядок соответствовал equals.

То есть вместо наложения естественного порядка в Comparable, который не согласуется с equals, вместо этого следует указать ненаправленный Comparator.

В качестве аналогии, сравните эту ситуацию с String, который предоставляет Comparator<String> CASE_INSENSITIVE_ORDER, который позволяет двум строкам, которые не equals, сравниваться с 0 в случае нечувствительность.

Здесь вы должны написать Comparator<Flows>, которое позволяет двум Flows, которые не equals, сравниваться с 0 по нечувствительности к направлению.

Смотри также

Смежные вопросы


Пример реализации

Вот пример реализации класса Edge, который имеет from и to, с направленным естественным упорядочением, соответствующим equals, что также обеспечивает ненаправленный Comparator.

Затем тестируется с 3 видами Set:

  • A HashSet, для проверки equals и hashCode
  • A TreeSet, для проверки естественного порядка
  • A TreeSet с пользовательским 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();
    }
}

Вывод (, как видно на ideone.com ) ниже, с комментариями:

// 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 в этом порядке. Точно так же B->A канонизируется к A->B, который уже находится в наборе, что объясняет, почему там только 3 Edge.

Ключевые моменты

  • Edge является неизменным
  • Arrays.hashCode(Object[]) используется для удобства; не нужно кодировать все эти формулы
  • Если естественный порядок соответствует equals, вы можете использовать compareTo == 0 в equals
  • Используйте многошаговую логику return в compareTo для краткости и ясности
  • Наличие reverse() и canon() значительно упрощает ненаправленное сравнение
    • Просто сравните их канонизированные формы в их естественном порядке

См. Также

  • Effective Java 2nd Edition
    • Пункт 8: Соблюдайте общий контракт при переопределении equals
    • Пункт 9: Всегда переопределять hashCode при переопределении equals
    • Пункт 10: Всегда переопределять toString
    • Пункт 12: Рассмотреть возможность реализации Comparable
    • Пункт 15: свернутьизменчивость
    • Элемент 36: последовательно использовать @Override аннотация
    • Элемент 47: Знать и использовать библиотеки
0 голосов
/ 17 августа 2010

Я не знаю, поможет ли это вам. Но это работает в обоих направлениях, как вы говорите.

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.

0 голосов
/ 17 августа 2010

Ключ должен иметь правильную реализацию метода equals. В вашем методе equals вы возвращаете false, когда адрес получателя не совпадает. Здесь вам нужно добавить дополнительную логику для проверки на равенство, так как вы хотите иметь двунаправленное равенство. Первый проход равенства должен быть проверкой равенства источника, назначения, порта. Второй проход должен быть обратным равенством источника и назначения. Вы также должны иметь специальное условие для значений по умолчанию, как в вашем примере, исключение portno (80) по умолчанию true.

...