我该如何改进这个Point类

时间:2014-10-30 05:24:04

标签: java immutability

我正在编写一个程序,需要使用一组有限的点来处理图像。我想我会将它实现为一个不可变/单例样式类。在继续构建更多关于类的复杂逻辑之前,我想得到关于核心类的意见。

import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.HashMap;

public class Point {
    private final int x,y;
    private final int hashCode;

    private static final HashMap<int[],Point> points = new HashMap<>();

    private Point(int x,int y){
        this.x = x;
        this.y = y;

        this.hashCode = new HashCodeBuilder().append(x).append(y).toHashCode();
    }
    public static Point getPoint(int x,int y){
        int [] candidate = new int[]{x,y};
        if(points.containsKey((candidate))){
            return points.get(candidate);
        }

        Point newPoint = new Point(x,y);
        points.put(candidate, newPoint);
        return newPoint;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public int hashCode(){
        return hashCode;
    }

    @Override
    public boolean equals(Object p){
        return this == p;
    }
}

我将使用该类至少执行以下功能:

  • 通过此Point
  • 映射图像中的不同频道
  • 定义一些静态缓存的自定义NavigableSets以便于遍历
  • 各种助手功能。例如,获取Point
  • 周围的所有点数

鉴于所提供的信息,所提供的实施有哪些缺点?

注意:将项目符号列表放在代码块之前会破坏代码块的显示。错误?

3 个答案:

答案 0 :(得分:2)

我认为你的缓存&#34;单身&#34;事情可能毫无意义。

它不会节省对象创建,因为每次请求Point时总是会创建candidate[]。并且,除非重用Point,否则在HashMap中使用内存作为候选 Map.Entry。因此,非常粗略地说,除非每个Point重复使用三次次,否则您的缓存会占用更多内存。它总是使用更多的时间。

如果您没有缓存,请更改您的equals

P.S。其余的看起来很好,而且一成不变。您可以考虑使x和y public final与其他Point实现更兼容。

答案 1 :(得分:1)

  1. 如果您担心被占用的内存,还有另一种方法可以解决它。

    我认为你的积分尺寸有限,我建议将你的x&amp; y在一个长的变量中(如果你的尺寸是32英尺长),或者甚至是int(如果你可以在16位中适合一个尺寸),这样你就可以获得性能提升&amp;存储器中。

    其他选项是使用坐标的int []数组作为一个点,虽然这会占用更多空间(因为你必须保留额外的指针引用)。

  2. 实现中的工厂方法仅适用于单线程应用程序,如果有多个线程创建点,则需要进行一些并发控制。这会占用你的资源,因为你实际上需要锁定每个点的创建。

  3. 另一个原因,为什么这个Factory方法是一个坏主意,是int []数组不重写equals和hashCode,你可以尝试创建2个具有相同内容的数组,以检查。所以你的HashMap不会简单地工作,而不是保存内存,你不仅每次都会创建一个新的Point,而且还要在你的HashMap中添加一个条目并执行不必要的计算。

  4. 因此,无论是使用Java原语,还是每次只需要创建一个新的不可变点,并使用它,不要过多地使用factoryMethods。

答案 2 :(得分:1)

你有一个正确的想法,让Point类不可变,但后来你用实例缓存复杂化了。它不是线程安全的,它会泄漏内存,因为一旦创建了一个Point,它将永远保留在hashmap中,永远不会被垃圾回收。

为什么不保持简单并定期进行无聊的价值观?您的IDE甚至会为您生成它。

然后,如果你真的真的希望拥有一个实例缓存,请使用Guava的Interner类,而不是自己滚动。结果将如下所示:

public class Point {
    final int x;
    final int y;

    private Point(int x, int y) {
        this.x = x; 
        this.y = y;
    }

    static Interner<Point> instanceCache = Interners.newWeakInterner();

    public static Point of(int x, int y) {
        return instanceCache.intern(new Point(x,y));
    }

    public int getX() { return x; }

    public int getY() { return y; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Point other = (Point) o;
        return this.x == other.x && this.y == other.y;
    }

    @Override
    public int hashCode() { return x * 31 + y; }
}