Create a series of SQL JOIN statements using two operands: primary and secondary. The generic form of the JOIN statement is:
JOIN primary primary ON (secondary.id == primary.id)
The code currently iterates over a list of primary and secondary operands, as follows:
for( Bundle primaryOperand : bundleComparators ) {
for( Bundle secondaryOperand : sortedBundles ) {
The problem is that the nested loop generates the following:
JOIN primary primary ON (secondary.id == primary.id)
JOIN secondary secondary ON (primary.id == secondary.id)
The second join is superfluous and, in this case, causes an error. The duplication can be eliminated with the following hypothetical data structure:
if( !interchangeableMap.contains( primaryOperand, secondaryOperand ) ) {
interchangeableMap.put( primaryOperand, secondaryOperand );
outputJoin( primaryOperand, secondaryOperand );
}
Where interchangeableMap.contains(...)
will return true if primaryOperand
is mapped to secondaryOperand
or secondaryOperand
is mapped to primaryOperand
.
My first thought is to create a class that contains two HashMap
s. Checking for containment queries the two HashMap
s to see if one map contains the primary and secondary operands or the other map contains the secondary and primary operands. Insertions put the two operand combinations into their respective HashMap
s.
Thank you!
The set() method adds or updates an entry in a Map object with a specified key and a value.
You should be able to make a copy of a HashMap using its clone method. Note that while this does get you two different maps, the actual values in the two maps are the same. This means that if the copied map's contents are mutable and you change them, they will still change in both places.
In these cases, we can use Collections such as list, set, etc. to insert multiple values into the same key in HashMap.
As we can see, if we try to insert two values for the same key, the second value will be stored, while the first one will be dropped.
Here is a solution based on @roland's suggestion:
public final class Pair {
private final Object a;
private final Object b;
public Pair(Object a, Object b) {
this.a = a;
this.b = b;
}
@Override public boolean equals(Object o) {
if(o == null || !(o instanceof Pair))
return false;
Pair that = (Pair) o;
return this.a.equals(that.a) && this.b.equals(that.b)
|| this.a.equals(that.b) && this.b.equals(that.a);
}
@Override public int hashCode() {
return a.hashCode() ^ b.hashCode();
}
}
Then:
Set<Pair> set = new HashSet<Pair>();
for(Bundle primaryOperand : bundleComparators) {
for(Bundle secondaryOperand : sortedBundles) {
Pair p = new Pair(primaryOperand.id, secondaryOperand.id);
if(set.contains(p))
continue;
set.add(p);
outputJoin(primaryOperand, secondaryOperand);
}
}
A subtle point about the solution: you must also override the hashCode()
method (hash value must reflect the equality relation), but you must do it in a symmetric way, that is: the hash value of <a,b>
must be == to that of <b,a>
Another idea would be to define a new Class OperatorPair
that overrides equals()
and use a Set
of those, again to be checked with contains()
. But your solution should work well, too.
(Also, if you do this, you should override hashCode()
as well, for example you could calculate it based on the names of the operands... see e.g. this question for details.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With