iterating a HashSet ...

Sometimes i miss some people around me at work or home that can feed me with some java facts.

The last week i work on a from component that contains an AjaxFormLoop to edit a set of drivers (removing/adding) to an parent entity.

Thanks Tapestry it was a fine quick job to design the UI but than began the fight to save
the modified HashSet, many pitfalls i tapped but now the job is done.

my first solution was:


void onAfterSubmit()
{
for (FreightDriver driver : entity.getDrivers())
{
boolean mustRemoved = true;
for (FreightDriver freightDriver : DB.values())
{
if (driver.getId() == freightDriver.getId())
{
mustRemoved = false;
break;
}
}

if (mustRemoved)
entity.getDrivers().remove(driver);
}
}


but this throws a ConcurrentModificationException
after a while i found the right (IMHO) solution:


void onAfterSubmit()
{
List driversToDelete = new ArrayList();

//-------------------------------------------------
// -----> Start to delete drivers from HashSet
//-------------------------------------------------
for (FreightDriver driver : entity.getDrivers())
{
boolean mustRemoved = true;
for (FreightDriver freightDriver : DB.values())
{
if (driver.getId() == freightDriver.getId())
{
mustRemoved = false;
break;
}
}

if (mustRemoved)
driversToDelete.add(driver);
}

for (FreightDriver freightDriver : driversToDelete)
entity.getDrivers().remove(freightDriver);
//-------------------------------------------------
// -----> End delete drivers from HashSet
//-------------------------------------------------
}


and here the complete component

@IncludeStylesheet(value = {"DriverGrid.css"})
public class DriverGrid
{
private static final Map DB = CollectionFactory.newConcurrentMap();

private static final AtomicLong ID_ALLOCATOR = new AtomicLong(System.currentTimeMillis());

@Parameter(required = true, defaultPrefix = BindingConstants.PROP)
private FreightOrder entity;

@Inject
private Session session;

@Inject
private Logger logger;

@Inject
private Messages messages;

/**
* Request object for information on current request.
*/
@Inject
private Request request;

/**
* RenderSupport to get unique client side id.
*/
@Inject
private RenderSupport renderSupport;

/**
* For blocks, messages, crete actionlink, trigger event.
*/
@Inject
private ComponentResources resources;

@Inject
@Property
private Block addRowBlock;

@Property
private FreightDriver gridRow;

@Inject
private EmptyBeanModelSource beanModelSource;

@Inject
private StaffDAO staffDAO;

@Inject
private FreightDriverDAO freightDriverDAO;

@Retain
@Property
private BeanModel gridModel;

/**
* ----------------------------------------------------------
* Drivers Grid
* ----------------------------------------------------------
*/
@Component(parameters = {"source=gridSource", "value=gridRow", "encoder=encoder",
"addrow=block:addRowBlock"})
private AjaxFormLoop grid;

@Component
private AddRowLink addRowLink;

@Component
private RemoveRowLink removeRowLink;

@Component(parameters = {"value=gridRow.driver"})
private StaffAutoTextField inputDriver;

@Component(parameters = {"value=gridRow.freightPrice",
"validate=required,min=0"})
private TextField inputFreightPrice;

@Component(parameters = {"value=gridRow.diversePrice",
"validate=required,min=0"})
private TextField inputDiversePrice;

/**
* get the datasource for the driver grid.
*/
@Log
public Collection getGridSource()
{
return DB.values();
}

/**
* Tapestry render phase method.
* Initialize temporary instance variables here.
*/
@Log
public void setupRender()
{
DB.clear();
for (FreightDriver driver : entity.getDrivers())
{
driver.setMapId(ID_ALLOCATOR.getAndIncrement());
DB.put(driver.getMapId(), driver);
}
}

@Log
void onBeginSubmit()
{
}

@Log
void onAfterSubmit()
{
List driversToDelete = new ArrayList();

//-------------------------------------------------
// -----> Start to delete drivers from HashSet
//-------------------------------------------------
for (FreightDriver driver : entity.getDrivers())
{
boolean mustRemoved = true;
for (FreightDriver freightDriver : DB.values())
{
if (driver.getId() == freightDriver.getId())
{
mustRemoved = false;
break;
}
}

if (mustRemoved)
driversToDelete.add(driver);
}

for (FreightDriver freightDriver : driversToDelete)
entity.getDrivers().remove(freightDriver);
//-------------------------------------------------
// -----> End delete drivers from HashSet
//-------------------------------------------------

for (FreightDriver freightDriver : DB.values())
{
if (freightDriver.getId() == 0)
{
freightDriver.setOrder(entity);
entity.getDrivers().add(freightDriver);
}
}
}

/**
* add a new row.
*/
@Log
Object onAddRow()
{
FreightDriver driver = new FreightDriver();
driver.setMapId(ID_ALLOCATOR.getAndIncrement());
DB.put(driver.getMapId(), driver);
return driver;
}

/**
* remove a new row.
*/
@Log
void onRemoveRow(FreightDriver value)
{
DB.remove(value.getMapId());
}

/**
* tells the ajax loop how to encode the items.
*/
@Log
public PrimaryKeyEncoder getEncoder()
{
return new PrimaryKeyEncoder()
{
public Long toKey(FreightDriver value)
{
return value.getMapId();
}

public void prepareForKeys(List keys)
{
}

public FreightDriver toValue(Long key)
{
return DB.get(key);
}
};
}

/**
* create response for driver autocompleter.
*/
@OnEvent(value = "selectDriver")
StreamResponse selectDriver()
{
String driverName = request.getParameter("inputDriver");
List staffs = staffDAO.findByPartialName(driverName, false);
StringBuffer stringBuffer = new StringBuffer();

stringBuffer.append("<ul>");
for (Staff staff : staffs)
{
stringBuffer.append("<li id='").
append(staff.getRecId()).
append("'>");
stringBuffer.append(staff.getAddress().getName1()).
append(", ").
append(staff.getAddress().getName2()).
append(" (").
append(staff.getClient().getClientDesc()).
append(")");
stringBuffer.append("</li>");
}
stringBuffer.append("</ul>");

return new TextStreamResponse("text/html", stringBuffer.toString());
}
}

Kommentare