Java Apache Commons Lang HashCodeBuilder冲突

Java Apache Commons Lang HashCodeBuilder冲突,java,hash,apache-commons,hash-collision,apache-commons-lang,Java,Hash,Apache Commons,Hash Collision,Apache Commons Lang,我在使用3.4版的Apache Commons Lang时遇到了冲突。我正在散列一个Route对象,它包含两个Cell对象,start和end。最后,我将提供一个发生碰撞的示例。这两个类都重写了hashCode和equals方法。首先是单元类: import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; public class C

我在使用3.4版的Apache Commons Lang时遇到了冲突。我正在散列一个Route对象,它包含两个Cell对象,start和end。最后,我将提供一个发生碰撞的示例。这两个类都重写了hashCodeequals方法。首先是单元类:

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Cell {
    private int east;
    private int south;

    public Cell(int east, int south) {
        this.east = east;
        this.south = south;
    }

    public int getEast() {
        return east;
    }

    public void setEast(int east) {
        this.east = east;
    }

    public int getSouth() {
        return south;
    }

    public void setSouth(int south) {
        this.south = south;
    }

    @Override
    /**
     * Compute hash code by using Apache Commons Lang HashCodeBuilder.
     */
    public int hashCode() {
        return new HashCodeBuilder(17, 31)
                .append(this.south)
                .append(this.east)
                .toHashCode();
    }

    @Override
    /**
     * Compute equals by using Apache Commons Lang EqualsBuilder.
     */
    public boolean equals(Object obj) {
        if (!(obj instanceof Cell))
            return false;
        if (obj == this)
            return true;

        Cell cell = (Cell) obj;
        return new EqualsBuilder()
                .append(this.south, cell.south)
                .append(this.east, cell.east)
                .isEquals();
    }
}
路线类别:

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.util.*;

public class Route {
    private Cell startCell;
    private Cell endCell;

    public Route(Cell startCell, Cell endCell) {
        this.startCell = startCell;
        this.endCell = endCell;
    }

    public Cell getStartCell() {
        return startCell;
    }

    public void setStartCell(Cell startCell) {
        this.startCell = startCell;
    }

    public Cell getEndCell() {
        return endCell;
    }

    public void setEndCell(Cell endCell) {
        this.endCell = endCell;
    }


    @Override
    public int hashCode() {
        return new HashCodeBuilder(43, 59)
                .append(this.startCell)
                .append(this.endCell)
                .toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Route))
            return false;
        if (obj == this)
            return true;

        Route route = (Route) obj;
        return new EqualsBuilder()
                .append(this.startCell, route.startCell)
                .append(this.endCell, route.endCell)
                .isEquals();
    }
}
碰撞示例:

public class Collision {
    public static void main(String[] args) {
        Route route1 = new Route(new Cell(154, 156), new Cell(154, 156));
        Route route2 = new Route(new Cell(153, 156), new Cell(151, 158));

        System.out.println(route1.hashCode() + " " + route2.hashCode());
    }
}
输出为1429303 1429303。现在,如果我将两个类的初始奇数和乘法器奇数都更改为相同,那么这个示例不会发生冲突。但在HashCodeBuilder的文档中,它明确规定:

必须传入两个随机选择的奇数。理想情况下,这些 对于每个类都应该不同,但这并不重要


理想情况下,如果可能的话,我希望使用完美散列函数(内射函数)作为我的示例。

在java中,散列代码绑定到整数范围(32位),因此这意味着如果有超过2^62个对象,就会发生冲突(如果有理想分布,则会发生事件)。但在实践中,由于hashcode没有提供完美的分布,冲突发生得更频繁

通过在生成哈希代码时添加更多参数,您可能能够更优化地分发生成的哈希代码(这与Apache commons库无关)。在本例中,您可以预先计算
Route
类的一个或多个属性,并在生成哈希代码时使用此属性。例如,计算两个
单元
对象之间的直线斜率:

double slope = (startCell.getEast() - endCell.getEast());
if ( slope == 0 ){//prevent division by 0
    slope = startCell.getSouth() - endCell.getSouth();
}else{
    slope = (startCell.getSouth() - endCell.getSouth()) / slope;
}

return new HashCodeBuilder(43, 59)
   .append(this.startCell)
   .append(this.endCell)
   .append(slope)
   .toHashCode();
double length = Math.sqrt(Math.pow(startCell.getSouth() - endCell.getSouth(), 2) + Math.pow(startCell.getEast() - endCell.getEast(), 2));
return new HashCodeBuilder(43, 59)
   .append(this.startCell)
   .append(this.endCell)
   .append(length)
   .toHashCode();
使用您的示例生成8309191183088489。或者(或与一起)使用两个
单元格
对象之间的距离:

double slope = (startCell.getEast() - endCell.getEast());
if ( slope == 0 ){//prevent division by 0
    slope = startCell.getSouth() - endCell.getSouth();
}else{
    slope = (startCell.getSouth() - endCell.getSouth()) / slope;
}

return new HashCodeBuilder(43, 59)
   .append(this.startCell)
   .append(this.endCell)
   .append(slope)
   .toHashCode();
double length = Math.sqrt(Math.pow(startCell.getSouth() - endCell.getSouth(), 2) + Math.pow(startCell.getEast() - endCell.getEast(), 2));
return new HashCodeBuilder(43, 59)
   .append(this.startCell)
   .append(this.endCell)
   .append(length)
   .toHashCode();
与您的示例一起使用的结果是83091911-486891382

为了测试这是否可以防止碰撞:

List<Cell> cells = new ArrayList<Cell>();
for ( int i = 0; i < 50; i++ ){
    for ( int j = 0; j < 50; j++ ){
        Cell c = new Cell(i,j);
        cells.add(c);

    }
}
System.out.println(cells.size() + " cells generated");
System.out.println("Testing " + (cells.size()*cells.size()) + " number of Routes");
Set<Integer> set = new HashSet<Integer>();
int collisions = 0;
for ( int i = 0; i < cells.size(); i++ ){
    for ( int j = 0; j < cells.size(); j++ ){
        Route r = new Route(cells.get(i), cells.get(j));
        if ( set.contains(r.hashCode() ) ){
            collisions++;
        }
        set.add(r.hashCode());
    }
}
System.out.println(collisions);
List cells=new ArrayList();
对于(int i=0;i<50;i++){
对于(int j=0;j<50;j++){
单元c=新单元(i,j);
添加(c);
}
}
System.out.println(cells.size()+“生成的单元格”);
System.out.println(“测试”+(cells.size()*cells.size())+“路由数”);
Set=newhashset();
int=0;
对于(int i=0;i
在生成的6250000条路线中:

  • 无长度和坡度:6155919
  • 长度和坡度:873047

  • 通过添加其他参数(如坡度或距离),可以获得有趣的解决方案。但添加更多参数是否会降低碰撞概率?
    但添加更多参数是否会降低碰撞概率
    是-请参见编辑中的示例。(如其他答案中所述,您无法消除冲突,但可以改进分布以减少冲突)实际上,在使用了50%的可用哈希代码后,您可能会得到冲突,因为有50%的几率碰到已经使用过的哈希。@rossum实际上,由于生日悖论,你会更早地遇到冲突。