我最近需要对一组整数对进行多次查找,以解决编程竞赛中的问题。我用Java编程。通常我会使用数组作为整数对,但由于Java中的集合不适用于数组,我这次决定使用列表。我后来发现我的解决方案很慢,但使用Point
的另一个参赛者的解决方案要快得多。
我实施了一个小基准来比较两者。我发现Set.contains()
Set<List<Integer>>
(每个List<Integer>
的大小为2)平均约为Set<Point>
的4.5倍,无论该对是{}实际上包含在集合中。我认为原因可能源于hashCode()
的不同实现,并且还实现了使用Point
的自定义List.hashCode()
类。这似乎是原因的一部分,但与实际List<Integer>
的差异仍然很大。
这是我的基准代码:
import java.awt.Point;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
public class IntPairTiming {
public static final int SIZE = 1_000_000;
public static final int TRIES = 100_000_000;
public static void main(String[] args) {
Random random = new Random();
// Pairs to store
Set<Point> pointSet = new HashSet<>();
Set<List<Integer>> listSet = new HashSet<>();
Set<MyPoint> myPointSet = new HashSet<>();
for (int i = 0; i < SIZE; i++) {
Point point = new Point(random.nextInt(), random.nextInt());
pointSet.add(point);
List<Integer> list = Arrays.asList(point.x, point.y);
listSet.add(list);
myPointSet.add(new MyPoint(point.x, point.y, list));
}
// Pair to lookup
Point point = new Point(random.nextInt(), random.nextInt());
List<Integer> list = Arrays.asList(point.x, point.y);
MyPoint myPoint = new MyPoint(point.x, point.y, list);
pointSet.add(point);
listSet.add(list);
myPointSet.add(myPoint);
// Time set of points
timeLookup(pointSet, point, "Set of Points");
// Time set of lists
timeLookup(listSet, list, "Set of Lists");
// Time set of mypoints
timeLookup(myPointSet, myPoint, "Set of MyPoints");
}
private static <T> void timeLookup(Set<T> set, T obj, String what) {
System.out.println("-----------");
System.out.printf("%s:%n", what);
System.out.printf("Contains = %s%n", set.contains(obj));
long s = System.nanoTime();
for (int i = 0; i < TRIES; i++) set.contains(obj);
long e = System.nanoTime();
System.out.printf("Average lookup = %sns%n", (e - s) / TRIES);
}
private static class MyPoint extends Point {
private final List<Integer> list;
public MyPoint(int x, int y, List<Integer> list) {
super(x, y);
this.list = list;
}
@Override
public int hashCode() {
return this.list.hashCode();
}
}
}
输出:
-----------
Set of Points:
Contains = true
Average lookup = 12ns
-----------
Set of Lists:
Contains = true
Average lookup = 55ns
-----------
Set of MyPoints:
Contains = true
Average lookup = 25ns
为什么会出现如此显着的差异?根据我的理解,hashCode()
和equals()
在Point
和List<Integer>
的大小为2时应该或多或少地等效。如果我们需要查找超过两个元素,类似于Point
的自定义类总是比列表更有效吗?
答案 0 :(得分:3)
这是基于expense for equals()
on any sort of AbstractList
大于人们想象的事实。
实现需要构造一个迭代器,迭代它,并比较所有元素(以及hashCode
也需要),而专用的Point
类有两个比较,非常容易通过JIT(由于字段偏移量没有变化那么多),而列表需要更多的间接。
在你的字段编号为十位的某一点(没有双关语),创建一个单独的元组类变得过早优化,除非该元组类本身有一些用作POJO本身。
答案 1 :(得分:1)
Point
包含原始字段,而List<Integer>
包含对象,但非常简单。
Point
的哈希码计算将直接使用这些字段,而松散地说,List<Integer>
的哈希码计算需要两次以上的参数操作才能获得实际值。