views:

66

answers:

3

this piece of code is supposed to consider flows in both direction as one flow. for example:

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

should be the same as

217.174.16.1, 192.168.1.65,80,123456

Another Example:

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

I want to keep i t like this:

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)()()...]

how should i change it in order to get the result? [im using a hashMap and this class of Flows are my keys]

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


}
+2  A: 

Probably the cleanest way to do this is to define these methods:

  • Flows reverse() that returns the reversed direction Flows of a given Flows
  • Flows canon() which returns a canonicalized form of a Flows
    • You can define e.g. a Flows is canon if srcAddr.compareTo(dstAddr) <= 0
    • Otherwise, its reverse() is canon by definition

Then for non-directional comparison, you can simply compare the canonical forms of the two flows. Having these methods makes the rest of the logic very clean and readable (see code below).


On Comparator, Comparable, and consistency with equals

Using the reverse() concept above, if you want f.equals(f.reverse()) always, then perhaps there shouldn't be any concept of directionality in the first place. If this is the case, then canonicalization is the best approach.

If f is generally not equals(f.reverse()), and yet you may want f and f.reverse() to compare to 0, then Comparable should not be used, because doing so would impose a natural ordering that is not consistent with equals.

From the documentation:

The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C.

It is strongly recommended (though not required) that natural orderings be consistent with equals.

That is, instead of imposing a natural ordering in Comparable that is inconsistent with equals, you should instead provide a non-directional Comparator instead.

As an analogy, compare this situation with String, which provides Comparator<String> CASE_INSENSITIVE_ORDER, which allows two strings that are not equals to compare to 0 by case-insensitivity.

So here you'd write a Comparator<Flows> that allows two Flows that are not equals to compare to 0 by directional-insensitivity.

See also

Related questions


Example implementation

Here's an example implementation of an Edge class that has a from and to, with a directional natural ordering that is consistent with equals, which also provides a non-directional Comparator.

It's then tested with 3 kinds of Set:

  • A HashSet, to test equals and hashCode
  • A TreeSet, to test natural ordering
  • A TreeSet with the custom Comparator, to test non-directionality

The implementation is concise and clear, and should be instructive.

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

The output is (as seen on ideone.com) below, annotated:

// 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]

Note that in the non-directional TreeSet, Z->A is canonicalized to A->Z, which is why it appears before C->D in this order. Similarly, B->A is canonicalized to A->B, which is already in the set, which explains why there are only 3 Edge there.

Key points

  • Edge is immutable
  • Arrays.hashCode(Object[]) is used for convenience; no need to code all that formulas
  • If the natural ordering is consistent with equals, you can use compareTo == 0 in equals
  • Use the multistep return logic in compareTo for conciseness and clarity
  • Having reverse() and canon() greatly simplifies the non-directional comparison
    • Simply compare their canonicalized forms in their natural ordering

See also

  • Effective Java 2nd Edition
    • Item 8: Obey the general contract when overriding equals
    • Item 9: Always override hashCode when you override equals
    • Item 10: Always override toString
    • Item 12: Consider implementing Comparable
    • Item 15: Minimize mutability
    • Item 36: Consistently use @Override annotation
    • Item 47: Know and use libraries
polygenelubricants
i dont need to have the reverse of the flows. what i need is to keep a record of records and a birectional flow is considered as one record.
Red Lion
@Red Lion - you **do** need to consider the reverse of the flows, that's entirely what your question is about. Read the first few lines of your own question again; reversing the direction of the Flows is *exactly* what you're describing there.
Andrzej Doyle
@Red Lion: A lot of very smart people have tried to help you several times, and you still seems to be having problems. I suggest you make it absolutely clear what is it that you're trying to do, so we can help you effectively instead of repeating the same information over and over again, information which apparently one way or another is useless to you.
polygenelubricants
@Andrzej: in the first line im saying: "this piece of code is supposed to consider flows in both direction as one flow". then provided two examples to say what i mean by two flows. im getting packets with those information, so i need to decide if they belong to a flow or not.
Red Lion
@Poly: yes your are right. im repeating my question in different words to make it more clear, because none of those explanations applied to my case. they were all right but not exactly what i want.
Red Lion
@Red: if they are all right, then learn from them and apply the knowledge to your situation. A lot of your questions are very localized and have a very narrow scope. We shouldn't be giving answers that would benefit only you; it should benefit the entire community. All the answers you've received so far have all the bits and pieces that you need, you just need to put it all together for your specific need.
polygenelubricants
@Poly: I dont understand why you are so offensive. And reminding me everytime that why am I asking! I am here to learn, and I am so new to programming. I read all those stuffs you providing , but when I come here again it means I had a problem with them that is why I am asking my problem in more details. If you dont want to answer then don't do that instead of making me upset everytime. I appreciate your help, but you are making me upset at the same time.
Red Lion
@Red: It's not my intent to make you upset. I genuinely want to help, although I'm not sure how, because I'm still not sure what you want. I will try to come up with a generic example and see if that helps you. I'll revise in an hour or so.
polygenelubricants
@Poly: thanks for your answer. it really is comprehensive. i hope others find it useful, as you are more concerned to give generic answers.but im not going to reverse anything.
Red Lion
A: 

The key is to have a correct implementation of equals method. In your equals method, you are returning false, the moment destination addresses mismatch. This is where you need to add additional logic to check for equality since you want to have bi-directional equality. The first pass of equality should be a check of equality of source, destination, port. The second pass should be reverse equality of source and destination. You also need to have special provision for default values, like in your example, an exclusion of portno(80) defaults to true.

Tushar Tarkas
thanks for your useful point.
Red Lion
A: 

I don't know if this will help you.But it works in both the directions as you say

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

I have used hashset to test.So it should work fine for hashmap too.

Emil
Your `compareTo` is broken. If `f1.compareTo(f2)` returns a positive number, `f2.compareTo(f1)` must return a negative number. See http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo%28T%29
polygenelubricants
How to correct this?I just followed the code that was given as question.
Emil
Actually f1 and f2 are both equal since f2 is the reverse flow of f1 and therefore they return 0.As far as returning -1 and +1,i have no idea on what grounds should I consider one Flow object greater than or lesser than the other.
Emil
@Poly: my point is, since im not really comparing in the matter of length so i cant get positive or negative values. i need to compare two packets and i dont mind if the source and destinations' port and addresses are reveresed. I will update my question with another example. and btw thanks for your consideration, i really appreciate that.
Red Lion
@Red:Was the above code of any use to you.If it is not according to your needs,tell me,i'll remove it.
Emil