Java 存储整数对的O(1)搜索内存高效数据结构是什么?

Java 存储整数对的O(1)搜索内存高效数据结构是什么?,java,data-structures,Java,Data Structures,考虑这个接口: public interface CoordinateSet { boolean contains(int x, int y); default boolean contains(Coordinate coord) { return contains(coord.x, coord.y); } } 它表示一组二维整数坐标,每个可能的坐标可以在集合内(包含返回真),也可以在集合外(包含返回假) 有很多方法可以实现这样的接口。计算效率最高的是由

考虑这个接口:

public interface CoordinateSet {
    boolean contains(int x, int y);
    default boolean contains(Coordinate coord) {
        return contains(coord.x, coord.y);
    }
}
它表示一组二维整数坐标,每个可能的坐标可以在集合内(
包含
返回真),也可以在集合外(
包含
返回假)

有很多方法可以实现这样的接口。计算效率最高的是由数组备份的实现:

public class ArrayCoordinateSet implements CoordinateSet {
    private final boolean[][] coords = new boolean[SIZE][SIZE];
    // ...
    @Override
    public boolean contains(int x, int y) {
        return coords[x][y];
    }
    public void add(int x,  int y) {
        coords[x][y] = true;
    }
    // ...

}
但是,如果
SIZE
很大,比如说1000,并且在1000×10000矩形的四个角上只有4个属于该集合的cordinate,这意味着
单元格
空间的绝大多数被
false
值消耗。对于这样一个稀疏的协调集,我们最好使用基于
哈希集的
协调集

public final class Coordinate {
    public final int x;
    public final int y;
    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }
    // .equals() and hashCode()
}
public class HashBasedCoordinateSet implements CoordinateSet {
    private final Set<Coordinate> coords = new HashSet<>();
    @Override
    public boolean contains(int x, int y) {
        return coords.contains(new Coordinate(x, y));
    }
    @Override
    public boolean contains(Coordinate coord) {
         return coords.contains(coord);
    }
    public void add(Coordinate coord) {
        coords.add(coord);
    }
}
当我们有值
x
y
并且想要检查
hashBasedCoordinateSet.contains(x,y)
时,那就需要在每次方法调用时创建一个新对象(因为我们总是需要一个对象来搜索
HashSet
,仅仅拥有对象的数据是不够的)。这将是对CPU时间的真正浪费(它需要创建所有这些
坐标
对象,然后抓取并收集它们,因为似乎无法对这些代码执行逃逸分析优化)

最后,我的问题是:

存储一组稀疏坐标的数据结构是什么:

  • Has O(1)
    包含(intx,inty)
    操作
  • 高效利用空间(与基于阵列的实现不同)
  • 包含(int x,int y)
    期间不必创建额外的对象

  • 没有测量的优化当然总是危险的。 你可能应该分析一下你的应用程序,看看这是否真的是一个瓶颈

    您还生成了两个用例

  • 在集合中查找单个坐标
  • 在给定的范围内查找作为集合一部分的所有坐标
  • 通过遍历集合的迭代器,过滤掉您不想要的迭代器,第2步可能会更加高效。这可能会以任意顺序返回数据。性能在很大程度上取决于数据集的大小

    也许一个简单的表数据结构,如提供的,可以为您提供一个更好的界面——将X和Y坐标索引为int——同时为您提供O(1)访问

    Table index=HashBasedTable.create();
    
    另一个建议是研究位置敏感哈希。 基本上,您可以创建一个新的哈希函数,将您的X-Y坐标映射到一个易于查询的公共一维空间。
    但这可能超出了范围。

    在没有测量的情况下进行优化当然总是危险的。 你可能应该分析一下你的应用程序,看看这是否真的是一个瓶颈

    您还生成了两个用例

  • 在集合中查找单个坐标
  • 在给定的范围内查找作为集合一部分的所有坐标
  • 通过遍历集合的迭代器,过滤掉您不想要的迭代器,第2步可能会更加高效。这可能会以任意顺序返回数据。性能在很大程度上取决于数据集的大小

    也许一个简单的表数据结构,如提供的,可以为您提供一个更好的界面——将X和Y坐标索引为int——同时为您提供O(1)访问

    Table index=HashBasedTable.create();
    
    另一个建议是研究位置敏感哈希。 基本上,您可以创建一个新的哈希函数,将您的X-Y坐标映射到一个易于查询的公共一维空间。
    但是这可能超出了范围。

    在Java中,long的大小是整数的两倍,因此可以在一个long中存储两个int。这个怎么样

    public class CoordinateSet {
        private HashSet<Long> coordinates = new HashSet<>();
    
        public void add(int x, int y) {
            coordinates.add((x | (long) y << 32));
        }
    
        public boolean contains(int x, int y) {
            return coordinates.contains((x | (long) y << 32));
        }
    }
    
    公共类协调集{
    私有HashSet坐标=新HashSet();
    公共无效添加(整数x,整数y){
    
    add((x |(long)yA long是Java中整数大小的两倍,因此可以在一个long中存储两个int。那么这个呢

    public class CoordinateSet {
        private HashSet<Long> coordinates = new HashSet<>();
    
        public void add(int x, int y) {
            coordinates.add((x | (long) y << 32));
        }
    
        public boolean contains(int x, int y) {
            return coordinates.contains((x | (long) y << 32));
        }
    }
    
    公共类协调集{
    私有HashSet坐标=新HashSet();
    公共无效添加(整数x,整数y){
    
    coordinates.add((x |(long)y您可以尝试使用组成x和y值的位作为键的二叉树。例如,如果x和y是32位整数,则树的总深度为64。因此,您可以循环x和y的位,最多作出64个决定,以得到包含/不包含的答案

    更新以回应评论:诚然,如果您想要O(1),树不是您通常所想的,但请记住原始问题中基于数组的方法只是O(1)对可用内存的实现限制。我所做的只是假设一个整数的位长度是一个固定的实现约束,这通常是一个安全的假设。换句话说,如果您真的希望contains()调用在恒定时间内运行,您可以将其编码为始终执行64个比较操作,然后返回


    诚然,CS教授可能不会相信这个论点。自从我们去掉家庭作业标签后,我就很难知道有人想要的是真实世界的答案还是理论上的CS答案。你可以试试二叉树,使用组成x和y值的位作为键。例如,如果x和y是32位整数,则树的总深度是64。所以你循环通过x和y的位,最多做出64个决定,以得到包含/不包含的答案

    更新以回应评论:当然,如果你想要O(1),树不是你通常所想的,但是请记住,原始问题中基于数组的方法只有O(1)达到可用内存的实现限制。我所做的只是假设位长度
    public class CoordinateSet {
        private HashSet<Long> coordinates = new HashSet<>();
    
        public void add(int x, int y) {
            coordinates.add((x | (long) y << 32));
        }
    
        public boolean contains(int x, int y) {
            return coordinates.contains((x | (long) y << 32));
        }
    }