关于for和foreach,兼顾效率与安全
生活随笔
收集整理的這篇文章主要介紹了
关于for和foreach,兼顾效率与安全
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
對于數組的訪問,是應該使用for的方式的,因為這樣性能更高。以下代碼是恰當的。 Object[]?objArray?=?...;
int?objArrayLength?=?objArray.Length;
for?(int?i?=?0;?i?<?objArrayLength;?++i)
{
????//?do?something ...
}
String?str?=?...;
int?strLength?=?str.Length;
for?(int?i?=?0;?i?<?strLength;?++i)?
{
???//?do?something ...
}
對ArrayList這樣的可使用下標進行隨機訪問的數據結構,使用下標訪問,要比foreach的方式進行順序訪問,速度要快一些。foreach這樣寫法,使用的過程產生一個額外的對象Enumerator,而且每次訪問需要更多的操作,降低性能。下面的兩種寫法編譯出的代碼是一樣的:
第一種寫法:
IList?list?=?new?ArrayList();
IEnumerator?iter?=?list.GetEnumerator();
try
{
????while?(iter.MoveNext())
????{
????????Object?obj?=?iter.Current;
????????//do?something?...
????}
}
finally
{
????IDisposable?disposableObj?=?iter?as?IDisposable;
????if?(disposableObj?!=?null)
????{
????????disposableObj.Dispose();
????}
}
第二種寫法: IList?list?=?new?ArrayList();
foreach?(Object?obj?in?list)
{
????//do?something?...
}
對比這兩種寫法,第一種寫法非常羅嗦,所以C#引入了foreach的語法。通過觀察第一種寫法,foreach是通過GetEnumerator獲得一個IEnumerator對象,通過IEnumerator對象執行MoveNext()方法和獲取Current屬性進行遍歷的。
我們再通過Reflector工具,查看mscorlib.dll中System.Collection.ArrayList的實現: //為了簡單起見,我只列出ArrayList的Add、Clear、GetEnumerator的代碼
public?class?ArrayList
{
????//這是一個版本標識,ArrayList對象,每做一個修改操作,_version都會加1
????private?int?_version;
????public?virtual?int?Add(object?value)
????{
????????int?num1;
????????if?(this._size?==?this._items.Length)
????????{
????????????this.EnsureCapacity((this._size?+?1));
????????}
????????this._items[this._size]?=?value;
????????++this._version;?//注意此處
????????this._size?=?((num1?=?this._size)?+?1);
????????return?num1;
????}
????public?virtual?void?Clear()
????{
????????Array.Clear(this._items,?0,?this._size);
????????this._size?=?0;
????????++this._version;?//注意此處
????}
????//每次調用GetEnumerator方法,都會構造一個FastArrayListEnumerator
????//或者ArrayListEnumeratorSimple對象。
????public?virtual?IEnumerator?GetEnumerator()
????{
????????if?(base.GetType()?==?typeof(ArrayList))
????????{
????????????return?new?ArrayList.FastArrayListEnumerator(this);
????????}
????????return?new?ArrayList.ArrayListEnumeratorSimple(this);
????}
}
通過上述代碼可以看到,ArrayList是通過_version成員變量作版本標識的,每次執行Add、Clear等修改ArrayList內容的操作,都會將版本號加1,而每次調用GetEnumerator方法,都會構造一個FastArrayListEnumerator或者ArrayListEnumeratorSimple對象。我們再看FastArrayListEnumerator的實現: class?FastArrayListEnumerator
{
????private?int?version;
????internal?FastArrayListEnumerator(ArrayList?list)
????{
????????this.list?=?list;
????????this.index?=?-1;
????????//獲取構建FastArrayListEnumerator對象時ArrayList的版本號
????????this.version?=?list._version;?
????????this.lastIndex?=?(list._size?-?1);
????}
????public?bool?MoveNext()
????{
????????int?num1;
????????//比較ArrayList當前的版本號,
????????//是否和構建FastArrayListEnumerator對象時的版本號一致
????????//如果不一致,則拋出異常。
????????if?(this.version?!=?this.list._version)
????????{
????????????throw?new?InvalidOperationException(
????????????????Environment.GetResourceString("InvalidOperation_EnumFailedVersion")
????????????????);
????????}
????????//... ...?
????}
}
FastArrayListEnumerator對象構建時,當時時ArrayList的版本號。當執行MoveNext()操作時,檢查ArrayList當前的版本號是否和FastArrayListEnumerator對象構建時的版本號一致,如果不一致就會拋出異常。
由于Enumerator中,做了版本檢查處理的工作,所以使用foreach是線程安全,而使用for則不時。為什么呢?如果在使用foreach遍歷對象的過程中,其他線程修改了List的內容,例如添加或者刪除,就會出現不可知的錯誤,而使用foreach則能夠正確拋出錯誤信息。
綜上所述,結論如下:
使用for,更高效率。
使用foreach,更安全。
那么如何選擇呢?我的建議是,在一些全局的,多線程可以訪問的數據結構對象,使用foreach。而對本地變量,則使用for,效率和安全兼顧!例如: public?void?F1(IList?globalList)
{
????IList?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用foreach,保證線程
????foreach?(Object?item?in?globalList)
????{
????????if?(condition)
????????{
????????????waitForDeleteList.Add(item);
????????}
????}
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.Count;
????for?(int?i?=?0;?i?<?waitForDeleteListCount;?++i)
????{
????????globalList.Remove(waitForDeleteList[i]);
????}
}
以上建議,對于在Java環境下也使用,我閱讀過JDK 1.4的java.util.ArrayList的實現,.NET Framework的實現和JDK的實現,幾乎是一樣的,是否抄襲,見仁見智。上述的C#代碼在Java環境中應為: public?void?f1(List?globalList)?{
????List?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用Iterator遍歷,保證線程
????Iterator?iter?=?globalList.iterator();
????while?(iter.hasNext())?{
????????Object?item?=?iter.next();
????????if?(condition)?{
????????????waitForDeleteList.add(item);
????????}
????}
????
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.size();
????for?(int?i?=?0;?i?<?waitForDeleteListCount;?++i)?{
????????globalList.remove(waitForDeleteList.get(i));
????}
}
注意,以上代碼并不是做該項工作的最優算法,如果需要更高的效率,修改如下:
C#版本
public?void?F1(IList?globalList)
{
????bool?condition?=?true;
????IList?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用foreach,保證線程
????int?itemIndex?=?0;
????foreach?(Object?item?in?globalList)
????{
????????if?(condition)
????????{
????????????waitForDeleteList.Add(index);
????????}
????????++itemIndex;
????}
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.Count;
????for?(int?i?=?waitForDeleteListCount?-?1;?i?>=?0;?--i)
????{
????????index?=?(int)?waitForDeleteList[i];
????????globalList.RemoveAt(itemIndex);
????}
}
Java版本:
public?void?f1(List?globalList)?{
????List?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用Iterator遍歷,保證線程
????Iterator?iter?=?globalList.iterator();
????int?index?=?0;
????while?(iter.hasNext())?{
????????Object?item?=?iter.next();
????????if?(condition)?{
????????????waitForDeleteList.add(new?Integer(index));
????????}
????????++index;
????}
????
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.size();
????for?(int?i?=?waitForDeleteListCount?-?1;?i?>=?0;?--i)?{
???????index?=?((Integer)?waitForDeleteList.get(i)).intValue();
????????globalList.remove(index);
????}
}
int?objArrayLength?=?objArray.Length;
for?(int?i?=?0;?i?<?objArrayLength;?++i)
{
????//?do?something ...
}
String?str?=?...;
int?strLength?=?str.Length;
for?(int?i?=?0;?i?<?strLength;?++i)?
{
???//?do?something ...
}
對ArrayList這樣的可使用下標進行隨機訪問的數據結構,使用下標訪問,要比foreach的方式進行順序訪問,速度要快一些。foreach這樣寫法,使用的過程產生一個額外的對象Enumerator,而且每次訪問需要更多的操作,降低性能。下面的兩種寫法編譯出的代碼是一樣的:
第一種寫法:
IList?list?=?new?ArrayList();
IEnumerator?iter?=?list.GetEnumerator();
try
{
????while?(iter.MoveNext())
????{
????????Object?obj?=?iter.Current;
????????//do?something?...
????}
}
finally
{
????IDisposable?disposableObj?=?iter?as?IDisposable;
????if?(disposableObj?!=?null)
????{
????????disposableObj.Dispose();
????}
}
第二種寫法: IList?list?=?new?ArrayList();
foreach?(Object?obj?in?list)
{
????//do?something?...
}
對比這兩種寫法,第一種寫法非常羅嗦,所以C#引入了foreach的語法。通過觀察第一種寫法,foreach是通過GetEnumerator獲得一個IEnumerator對象,通過IEnumerator對象執行MoveNext()方法和獲取Current屬性進行遍歷的。
我們再通過Reflector工具,查看mscorlib.dll中System.Collection.ArrayList的實現: //為了簡單起見,我只列出ArrayList的Add、Clear、GetEnumerator的代碼
public?class?ArrayList
{
????//這是一個版本標識,ArrayList對象,每做一個修改操作,_version都會加1
????private?int?_version;
????public?virtual?int?Add(object?value)
????{
????????int?num1;
????????if?(this._size?==?this._items.Length)
????????{
????????????this.EnsureCapacity((this._size?+?1));
????????}
????????this._items[this._size]?=?value;
????????++this._version;?//注意此處
????????this._size?=?((num1?=?this._size)?+?1);
????????return?num1;
????}
????public?virtual?void?Clear()
????{
????????Array.Clear(this._items,?0,?this._size);
????????this._size?=?0;
????????++this._version;?//注意此處
????}
????//每次調用GetEnumerator方法,都會構造一個FastArrayListEnumerator
????//或者ArrayListEnumeratorSimple對象。
????public?virtual?IEnumerator?GetEnumerator()
????{
????????if?(base.GetType()?==?typeof(ArrayList))
????????{
????????????return?new?ArrayList.FastArrayListEnumerator(this);
????????}
????????return?new?ArrayList.ArrayListEnumeratorSimple(this);
????}
}
通過上述代碼可以看到,ArrayList是通過_version成員變量作版本標識的,每次執行Add、Clear等修改ArrayList內容的操作,都會將版本號加1,而每次調用GetEnumerator方法,都會構造一個FastArrayListEnumerator或者ArrayListEnumeratorSimple對象。我們再看FastArrayListEnumerator的實現: class?FastArrayListEnumerator
{
????private?int?version;
????internal?FastArrayListEnumerator(ArrayList?list)
????{
????????this.list?=?list;
????????this.index?=?-1;
????????//獲取構建FastArrayListEnumerator對象時ArrayList的版本號
????????this.version?=?list._version;?
????????this.lastIndex?=?(list._size?-?1);
????}
????public?bool?MoveNext()
????{
????????int?num1;
????????//比較ArrayList當前的版本號,
????????//是否和構建FastArrayListEnumerator對象時的版本號一致
????????//如果不一致,則拋出異常。
????????if?(this.version?!=?this.list._version)
????????{
????????????throw?new?InvalidOperationException(
????????????????Environment.GetResourceString("InvalidOperation_EnumFailedVersion")
????????????????);
????????}
????????//... ...?
????}
}
FastArrayListEnumerator對象構建時,當時時ArrayList的版本號。當執行MoveNext()操作時,檢查ArrayList當前的版本號是否和FastArrayListEnumerator對象構建時的版本號一致,如果不一致就會拋出異常。
由于Enumerator中,做了版本檢查處理的工作,所以使用foreach是線程安全,而使用for則不時。為什么呢?如果在使用foreach遍歷對象的過程中,其他線程修改了List的內容,例如添加或者刪除,就會出現不可知的錯誤,而使用foreach則能夠正確拋出錯誤信息。
綜上所述,結論如下:
使用for,更高效率。
使用foreach,更安全。
那么如何選擇呢?我的建議是,在一些全局的,多線程可以訪問的數據結構對象,使用foreach。而對本地變量,則使用for,效率和安全兼顧!例如: public?void?F1(IList?globalList)
{
????IList?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用foreach,保證線程
????foreach?(Object?item?in?globalList)
????{
????????if?(condition)
????????{
????????????waitForDeleteList.Add(item);
????????}
????}
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.Count;
????for?(int?i?=?0;?i?<?waitForDeleteListCount;?++i)
????{
????????globalList.Remove(waitForDeleteList[i]);
????}
}
以上建議,對于在Java環境下也使用,我閱讀過JDK 1.4的java.util.ArrayList的實現,.NET Framework的實現和JDK的實現,幾乎是一樣的,是否抄襲,見仁見智。上述的C#代碼在Java環境中應為: public?void?f1(List?globalList)?{
????List?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用Iterator遍歷,保證線程
????Iterator?iter?=?globalList.iterator();
????while?(iter.hasNext())?{
????????Object?item?=?iter.next();
????????if?(condition)?{
????????????waitForDeleteList.add(item);
????????}
????}
????
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.size();
????for?(int?i?=?0;?i?<?waitForDeleteListCount;?++i)?{
????????globalList.remove(waitForDeleteList.get(i));
????}
}
注意,以上代碼并不是做該項工作的最優算法,如果需要更高的效率,修改如下:
C#版本
public?void?F1(IList?globalList)
{
????bool?condition?=?true;
????IList?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用foreach,保證線程
????int?itemIndex?=?0;
????foreach?(Object?item?in?globalList)
????{
????????if?(condition)
????????{
????????????waitForDeleteList.Add(index);
????????}
????????++itemIndex;
????}
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.Count;
????for?(int?i?=?waitForDeleteListCount?-?1;?i?>=?0;?--i)
????{
????????index?=?(int)?waitForDeleteList[i];
????????globalList.RemoveAt(itemIndex);
????}
}
Java版本:
public?void?f1(List?globalList)?{
????List?waitForDeleteList?=?new?ArrayList();
????//全局變量,使用Iterator遍歷,保證線程
????Iterator?iter?=?globalList.iterator();
????int?index?=?0;
????while?(iter.hasNext())?{
????????Object?item?=?iter.next();
????????if?(condition)?{
????????????waitForDeleteList.add(new?Integer(index));
????????}
????????++index;
????}
????
????//本地變量使用for,保證效率
????int?waitForDeleteListCount?=?waitForDeleteList.size();
????for?(int?i?=?waitForDeleteListCount?-?1;?i?>=?0;?--i)?{
???????index?=?((Integer)?waitForDeleteList.get(i)).intValue();
????????globalList.remove(index);
????}
}
轉載于:https://www.cnblogs.com/jobs/archive/2004/07/17/25218.html
總結
以上是生活随笔為你收集整理的关于for和foreach,兼顾效率与安全的全部內容,希望文章能夠幫你解決所遇到的問題。