1

我承认这听起来有点疯狂,但要解释一下我的意思:

我有一个Collection(例如 HashSet)包含几个相当慢的初始化对象,我想看看是否Collection已经包含一个特定的对象。让我们Vector3d作为一个例子(我知道初始化并不昂贵)。

所以Collection包含:

Vector3d(1,1,1)
Vector3d(2,1,1)
Vector3d(3,1,1)

我想问一个Collection问题“是否Collection包含 a Vector3dwith x=2, y=1and z=1(即我已经知道该.contains()方法将针对的数据)。所以我可以创建一个新的Vector3d(2,1,1)然后使用.contains()它但是正如我所说的对象初始化很慢,或者我可以运行整个Collection手动检查(这就是我现在正在做的事情)但是那(据我所知)比.contains()它不使用哈希要慢。有没有更好的方法来做到这一点?

有问题的对象是可变的,但该equals方法所基于的数据不是。(在我的情况下,它们是 x、y、z 坐标处的块,块的内容可能会改变,但 x、y、z 坐标不会)

4

6 回答 6

2

在 an 上使用该.contains()方法ArrayList将导致equalsArrayList.

虽然这对你有用,但它可能对非常大ArrayList的 s 没有好处。如果性能是一个问题,您可能希望持有一个HashSet包含对Vector3d对象的引用。调用containsa HashSet(或 any Set)要快得多。

于 2013-04-20T22:10:49.623 回答
2

如果您只需要遍历所有元素或按位置访问元素,则 ArrayList 是正确的数据结构。对于其他任何东西来说,它都是错误的数据结构。

您要做的是快速回答包含问题,这就是 Sets 和 Maps 的用途。Vector3dKey使用您想要的简单散列函数创建一个单独的、更便宜的类并将昂贵的对象插入到 aMap< Vector3dKey, Vector3d >同时或代替 an会更有意义ArrayList< Vector3d >。Java 显然不会保留您昂贵向量的两个副本,而只是引用的副本。当然,如果您的 Vectors 是mutable ,整个方案就会失效。

于 2013-04-21T04:03:42.890 回答
1

如果您真的必须使用列表(而不是哈希),您不妨遍历列表,检索每个对象并手动检查其属性——我的意思是这将与“包含”一样快。

如果您打算使用哈希而不是列表,那么您应该使用不同的对象进行比较。例如,如果您在上面的示例中使用 HashMap,则您的键可能是以下字符串:

"1,1,1","2,1,1","3,1,1"

这将使查找变得即时而简单。如果列表可以包含其他类型的对象,那么“Vector3d(1,1,1)”可能是一个更好的字符串。无需昂贵或增加代码复杂性即可轻松重新创建。

如果您因为需要保留顺序而使用列表,请查看 LinkedHashMap。

另外我建议您创建一个函数来从对象(插入时)或参数(搜索时)派生字符串,而不是在代码周围分发功能,这是您可能需要更改或扩展的事情稍后。

于 2013-04-21T03:51:26.773 回答
1

基于 Mental 法官回答的代码

package mygame;

import java.util.HashMap;
import java.util.Map;


public class Main{


    public Main(){
        Map<CheapKey,ExpensiveClass> map=new HashMap< CheapKey, ExpensiveClass>();

        for(int i=0;i<100;i++){
            ExpensiveClass newExpensiveClass;
            newExpensiveClass=new ExpensiveClass(i,0,0);
            map.put(newExpensiveClass.getKey(), newExpensiveClass);
        }

        CheapKey testKey1=new CheapKey(1,0,0);
        CheapKey testKey2=new CheapKey(1,0,1);

        System.out.println(map.containsKey(testKey1)); //there is an object under key1
        System.out.println(map.containsKey(testKey2)); //there isn't an object under key2
        ExpensiveClass  retrievedExpensiveClass=map.get(testKey1);
    }

    public static void main(String[] args) {
        Main main=new Main();
    }

    protected class ExpensiveClass{
        int x;
        int y;
        int z;
        public ExpensiveClass(int x, int y, int z){
            this.x=x;
            this.y=y;
            this.z=z;
            for(int i=0;i<10000;i++){
                //slow initilisation
            }

        }
        public CheapKey getKey(){
            return new CheapKey(x,y,z);
        }

    }

    protected class CheapKey{
        int x;
        int y;
        int z;
        public CheapKey(int x, int y, int z){
            this.x=x;
            this.y=y;
            this.z=z;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final CheapKey other = (CheapKey) obj;
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 79 * hash + this.x;
            hash = 79 * hash + this.y;
            hash = 79 * hash + this.z;
            return hash;
        }


    }



}
于 2013-04-21T14:24:41.827 回答
0

contains 方法将调用对象的 .equals 方法,因此只要该类的 .equals 实现比较对象中包含的值而不是它们的指针,那么使用 contains 就可以了。

http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html#contains(java.lang.Object)

编辑误读了您的问题。我认为这取决于列表有多大与初始化需要多长时间。如果列表很短,请遍历它并手动检查。但是,如果列表可能很长,那么创建对象并使用 .contains 可能会更有效。

于 2013-04-20T22:06:24.413 回答
0

ArrayList.contains不使用散列;和人工检查的速度完全一样。两种方式都没有区别。

使用假对象类是可行的,但几乎可以肯定是代码异味。

于 2013-04-20T22:10:29.177 回答