[英]java Map removal unexpected behavior
碼:
public class FeatureBuilder implements IFeatureBuilder {
private final Map< java.awt.Shape, ShapeAttributes > shapes = new HashMap<>();
[...]
@Override
public void addToContainer( java.awt.Shape shape ) {
System.err.println( shape.getClass());
shapes.put( shape, new ShapeAttributes( ... ));
}
@Override
public void removeFromContainer( double x, double y ) {
final Set< Shape > rm = new HashSet<>();
final Point2D loc = new Point2D.Double( x, y );
for( final Shape shape : shapes.keySet()) {
if( shape.contains( loc )) {
System.err.println( "shapes.containsKey( shape ): " +
shapes.containsKey( shape ));
rm.add( shape );
}
}
System.err.println( "cardinality before removal : " + shapes.size());
shapes.keySet().removeAll( rm );
System.err.println( "cardinality after removal : " + shapes.size());
}
輸出:
class java.awt.geom.Rectangle2D$Double
shapes.containsKey( shape ): false <<<<<<< This is unexpected!
cardinality before removal : 1
rm cardinality : 1
cardinality after removal : 1
我很驚訝:在Map.keySet()
上由for
迭代器檢索的實例不是Map
的鍵!
這怎么可能?
此方法的主要缺陷是放置在rm
中的Shape的選定實例不會從shapes
刪除。
閱讀完答案后,代碼變為:
public class DecoratedShape {
public final Shape _shape;
public /* */ Color _stroke = Color.BLACK;
public /* */ float _strokeWidth = 3.0f;
public /* */ Color _fill = Color.BLACK;
public DecoratedShape( Shape shape, Color stroke, float strokeWidth, Color fill ) {
_shape = shape;
_stroke = stroke;
_strokeWidth = strokeWidth;
_fill = fill;
}
public boolean contains( Point2D loc ) {
return _shape.contains( loc );
}
public void paint( Graphics2D g ) {
g.setStroke( new BasicStroke( _strokeWidth ));
g.setColor( _fill );
g.fill( _shape );
g.setColor( _stroke );
g.draw( _shape );
}
}
public class FeatureBuilder implements IFeatureBuilder {
private final List< DecoratedShape > _shapes = new LinkedList<>();
[...]
@Override
public void addToContainer( Object o ) {
_shapes.add( new DecoratedShape((Shape)o, _stroke, _strokeWidth, _fill ));
}
@Override
public void removeFromContainer( double x, double y ) {
final Set<DecoratedShape> rm = new HashSet<>();
final Point2D loc = new Point2D.Double( x, y );
for( final DecoratedShape shape : _shapes ){
if( shape.contains( loc ) ){
rm.add( shape );
}
}
_shapes.removeAll( rm );
}
public void paint( Graphics2D g ) {
for( final DecoratedShape shape : _shapes ) {
shape.paint( g );
}
}
}
現在,它按預期工作......非常感謝!
跟進@ RohitJain的評論,這是一個重現行為的簡單示例:
Map<Shape, Object> map = new HashMap<>();
java.awt.geom.Rectangle2D.Double shape = new java.awt.geom.Rectangle2D.Double(1, 1, 1, 1);
map.put(shape, null);
System.out.println(map.size()); //1
shape.setRect(2, 2, 2, 2); //mutate
System.out.println(map.size()); //still 1
map.keySet().remove(shape);
System.out.println(map.size()); //still 1
根本問題是Shape在地圖中發生了變異。
這並不能解釋為什么您的代碼不起作用,但它會解決您的問題並使方法更有效,因為沒有分配臨時集:
public void removeFromContainer(double x, double y) {
final Point2D loc = new Point2D.Double(x, y);
Iterator<Shape> iter = shapes.keySet().iterator();
while (iter.hasNext()) {
if (iter.next().contains(loc))
iter.remove();
}
}
編輯:我懷疑你在將它們添加到地圖后修改了形狀,因此檢查containsKey
時的哈希碼與將Shape添加到地圖時使用的哈希碼不匹配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.