[Enhancement] Improve list comparing performance (#4880)

The function equalSets is not efficient enough currently, the time complexity is O(n^2).
To improve the performance of comparing two lists, this patch tries to use hash map structure
to make the time complexity to be O(n).
This commit is contained in:
xinghuayu007
2020-11-22 20:35:12 +08:00
committed by GitHub
parent f445ed5b8a
commit e507fcc3b3
2 changed files with 55 additions and 2 deletions

View File

@ -50,6 +50,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
/**
* Root of the expr node hierarchy.
@ -482,13 +483,40 @@ abstract public class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl
/**
* Return true if l1 equals l2 when both lists are interpreted as sets.
* TODO: come up with something better than O(n^2)?
*/
public static <C extends Expr> boolean equalSets(List<C> l1, List<C> l2) {
if (l1.size() != l2.size()) {
return false;
}
return l1.containsAll(l2) && l2.containsAll(l1);
Map cMap1 = toCountMap(l1);
Map cMap2 = toCountMap(l2);
if (cMap1.size() != cMap2.size()) {
return false;
}
Iterator it = cMap1.keySet().iterator();
while (it.hasNext()) {
C obj = (C) it.next();
Integer count1 = (Integer) cMap1.get(obj);
Integer count2 = (Integer) cMap2.get(obj);
if (count2 == null || count1 != count2) {
return false;
}
}
return true;
}
public static <C extends Expr> HashMap<C, Integer> toCountMap(List<C> list) {
HashMap countMap = new HashMap<C,Integer>();
for (int i = 0; i < list.size(); i++) {
C obj = list.get(i);
Integer count = (Integer) countMap.get(obj);
if (count == null) {
countMap.put(obj, 1);
} else {
countMap.put(obj, count+1);
}
}
return countMap;
}
public void analyzeNoThrow(Analyzer analyzer) {

View File

@ -29,6 +29,8 @@ import org.junit.Test;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import mockit.Expectations;
import mockit.Injectable;
@ -159,4 +161,27 @@ public class ExprTest {
StringLiteral castStringLiteral2 = (StringLiteral) stringLiteral.uncheckedCastTo(Type.VARCHAR);
Assert.assertTrue(stringLiteral == castStringLiteral2);
}
@Test
public void testEqualSets() {
Expr r1 = new DateLiteral(2020, 10, 20);
Expr r2 = new DateLiteral(2020, 10, 21);
Expr r3 = new DateLiteral(2020, 10, 22);
Expr r4 = new DateLiteral(2020, 10, 23);
//list1 equal list2
List<Expr> list1 = new ArrayList<>();
List<Expr> list2 = new ArrayList<>();
list1.add(r1);
list1.add(r2);
list1.add(r3);
list2.add(r1);
list2.add(r2);
list2.add(r3);
Assert.assertTrue(Expr.equalSets(list1, list2));
//list3 not equal list4
list2.add(r4);
Assert.assertFalse(Expr.equalSets(list1, list2));
}
}