Iterator.remove()
is safe, you can use it like this:
List<String> list = new ArrayList<>();
// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String string = iterator.next();
if (string.isEmpty()) {
// Remove the current element from the iterator and the list.
iterator.remove();
}
}
Note that Iterator.remove()
is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.
Source: docs.oracle > The Collection Interface
And similarly, if you have a ListIterator
and want to add items, you can use ListIterator#add
, for the same reason you can use Iterator#remove
— it's designed to allow it.
In your case you tried to remove from a list, but the same restriction applies if trying to put
into a Map
while iterating its content.
You should use BindingList for your collection or implement IBindingList as this will notify back the DataGridView about any changes in the list.
Then implement INotifyPropertyChanged interface for Floor class, this will allow for a TwoWay binding mode between your individual Floor items and the DataGridView.
Eric, you could also do something like this
public class MyFloorCollection : BindingList<Floor>
{
public MyFloorCollection()
: base()
{
this.ListChanged += new ListChangedEventHandler(MyFloorCollection_ListChanged);
}
void MyFloorCollection_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType == ListChangedType.ItemAdded)
{
Floor newFloor = this[e.NewIndex] as Floor;
if (newFloor != null)
{
newFloor.HeightChanged += new Floor.HeightChangedEventHandler(newFloor_HeightChanged);
}
}
}
void newFloor_HeightChanged(int newValue, int oldValue)
{
//recaluclate
}
}
Of course you can create your own HeightChangedEvent and subscribe to that, that way you dont have to go by property names in an if statement.
So your Floor class will look like this
public class Floor : INotifyPropertyChanged
{
private int _height;
public int Height
{
get { return _height; }
set
{
if (HeightChanged != null)
HeightChanged(value, _height);
_height = value;
OnPropertyChanged("Height");
}
}
public int Elevation { get; set; }
private void OnPropertyChanged(string property)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public delegate void HeightChangedEventHandler(int newValue, int oldValue);
public event HeightChangedEventHandler HeightChanged;
}
this way you only have to subscribe to your HeightChanged variable, instead of to PropertyChanged. PropertyChanged will be consumed by the DataGridView to keep a TwoWay binding. I do believe this way is cleaner.
You can also change the delegate and pass the item as the sender.
public delegate void HeightChangedEventHandler(Floor sender, int newValue, int oldValue);
EDIT: To unsubscribe from HeightChanged event you need to override RemoveItem
protected override void RemoveItem(int index)
{
if (index > -1)
this[index].HeightChanged -= newFloor_HeightChanged;
base.RemoveItem(index);
}
Best Solution
Try the commons collections API:
Of course you don't have to use an anonymous class every time, you could create implementations of the
Predicate
interface for commonly used searchs.