क्या, यदि कोई हो, तो निम्नलिखित दो छोरों के बीच प्रदर्शन अंतर है?
for (Object o: objectArrayList) {
o.DoSomething();
}
तथा
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
क्या, यदि कोई हो, तो निम्नलिखित दो छोरों के बीच प्रदर्शन अंतर है?
for (Object o: objectArrayList) {
o.DoSomething();
}
तथा
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
जवाबों:
जोशुआ बलोच द्वारा प्रभावी जावा में आइटम 46 से :
1.5 रिलीज में पेश किए गए प्रत्येक लूप को पूरी तरह से इट्रेटर या इंडेक्स वेरिएबल को छिपाकर अव्यवस्था और त्रुटि के अवसर से छुटकारा मिलता है। परिणामी मुहावरे संग्रह और सरणियों पर समान रूप से लागू होते हैं:
// The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); }
जब आप कोलन देखते हैं (:), इसे "इन" के रूप में पढ़ें। इस प्रकार, उपरोक्त लूप "तत्वों में प्रत्येक तत्व ई के लिए" के रूप में पढ़ता है। ध्यान दें कि प्रत्येक लूप का उपयोग करने के लिए कोई प्रदर्शन जुर्माना नहीं है, यहां तक कि सरणियों के लिए भी। वास्तव में, यह कुछ परिस्थितियों में लूप के लिए एक साधारण से अधिक मामूली प्रदर्शन लाभ प्रदान कर सकता है, क्योंकि यह केवल एक बार सरणी सूचकांक की सीमा की गणना करता है। जब आप इसे हाथ से कर सकते हैं (आइटम 45), प्रोग्रामर हमेशा ऐसा नहीं करते हैं।
ये सभी लूप ठीक वैसा ही करते हैं, मैं सिर्फ अपने दो सेंट में फेंकने से पहले ये दिखाना चाहता हूं।
सबसे पहले, सूची के माध्यम से पाशन का क्लासिक तरीका:
for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }
दूसरा, पसंदीदा तरीका चूंकि यह कम त्रुटि वाला है (आपने कितनी बार "उफ़, इन चरणों में लूप्स में i और j को मिलाया है?"
for (String s : strings) { /* do something using s */ }
तीसरा, लूप के लिए माइक्रो-ऑप्टिमाइज़्ड:
int size = strings.size();
for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }
अब वास्तविक दो सेंट: कम से कम जब मैं इनका परीक्षण कर रहा था, तो तीसरा सबसे तेज़ था जब मिलीसेकंड की गिनती करते हुए, यह बताता था कि एक सरल ऑपरेशन के साथ प्रत्येक प्रकार के लूप में कितना समय लगा, कुछ मिलियन बार दोहराया - यह जावा 5 का उपयोग कर रहा था Windows में jre1.6u10 के साथ अगर कोई दिलचस्पी रखता है।
हालांकि यह कम से कम ऐसा प्रतीत होता है कि तीसरा सबसे तेज़ है, आपको वास्तव में अपने आप से पूछना चाहिए कि क्या आप अपने लूपिंग कोड में इस पीपल के अनुकूलन को हर जगह लागू करने का जोखिम उठाना चाहते हैं क्योंकि मैंने जो देखा है, उससे वास्तविक लूपिंग है ' टी आमतौर पर किसी भी वास्तविक कार्यक्रम का सबसे अधिक समय लेने वाला हिस्सा (या शायद मैं सिर्फ गलत क्षेत्र पर काम कर रहा हूं, जो जानता है)। और जैसा कि मैंने जावा के लिए प्रत्येक लूप के बहाने उल्लेख किया है (कुछ इसे Iterator लूप के रूप में संदर्भित करता है और अन्य को फॉर-इन लूप के रूप में ) आप इसका उपयोग करते समय उस विशेष बेवकूफ बग को हिट करने की कम संभावना रखते हैं। और बहस करने से पहले कि यह कैसे अन्य लोगों की तुलना में भी तेज हो सकता है, याद रखें कि javac बिल्टकोड को बिल्कुल भी अनुकूलित नहीं करता है (ठीक है, लगभग सभी पर), यह बस इसे संकलित करता है।
यदि आप हालांकि माइक्रो-ऑप्टिमाइज़ेशन में हैं और / या आपका सॉफ़्टवेयर पुनरावर्ती लूप का बहुत उपयोग करता है और ऐसे में आपको तीसरे लूप प्रकार में रुचि हो सकती है। बस अपने सॉफ़्टवेयर को अच्छी तरह से बेंचमार्क करने से पहले याद रखें कि लूप के लिए बदलने से पहले और बाद में आपको इस विषम, सूक्ष्म-अनुकूलित एक को बदलना होगा।
get(int)
, दूसरा उपयोग करता है Iterator
। इस बात पर विचार करें LinkedList
कि for(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
ऐसा करने के बाद से कहीं अधिक बुरा प्रदर्शन हो रहा है get(int)
।
प्रत्येक लूप के लिए आमतौर पर पसंद किया जाना चाहिए। "प्राप्त" दृष्टिकोण धीमा हो सकता है यदि आपके द्वारा उपयोग की जा रही सूची कार्यान्वयन यादृच्छिक पहुँच का समर्थन नहीं करता है। उदाहरण के लिए, यदि एक लिंक्डलिस्ट का उपयोग किया जाता है, तो आप एक ट्रैवर्सल लागत का अनुमान लगा सकते हैं, जबकि फॉर-एप्रोच दृष्टिकोण का उपयोग करता है जो सूची में अपनी स्थिति का ट्रैक रखता है। प्रत्येक लूप की बारीकियों के बारे में अधिक जानकारी ।
मुझे लगता है कि लेख अब यहां है: नया स्थान
यहां दिखाया गया लिंक मृत था।
खैर, प्रदर्शन प्रभाव ज्यादातर महत्वहीन है, लेकिन शून्य नहीं है। यदि आप RandomAccess
इंटरफ़ेस के JavaDoc को देखते हैं :
अंगूठे के एक नियम के रूप में, एक सूची कार्यान्वयन को इस इंटरफ़ेस को लागू करना चाहिए यदि, वर्ग के विशिष्ट उदाहरणों के लिए, यह लूप:
for (int i=0, n=list.size(); i < n; i++) list.get(i);
इस लूप से तेज चलता है:
for (Iterator i=list.iterator(); i.hasNext();) i.next();
और प्रत्येक लूप के लिए पुनरावृत्ति के साथ संस्करण का उपयोग किया जाता है, इसलिए ArrayList
उदाहरण के लिए, प्रत्येक लूप सबसे तेज़ नहीं है।
दुर्भाग्य से एक अंतर प्रतीत होता है।
यदि आप दोनों प्रकार के छोरों के लिए उत्पन्न बाइट्स कोड को देखते हैं, तो वे अलग हैं।
यहाँ Log4j स्रोत कोड से एक उदाहरण है।
/Log4j-api/src/main/java/org/apache/log/log4j/MarkerManager.java में हमारे पास एक स्थिर आंतरिक वर्ग है जिसे Log4jMarker कहा जाता है जो परिभाषित करता है:
/*
* Called from add while synchronized.
*/
private static boolean contains(final Marker parent, final Marker... localParents) {
//noinspection ForLoopReplaceableByForEach
for (final Marker marker : localParents) {
if (marker == parent) {
return true;
}
}
return false;
}
मानक लूप के साथ:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: arraylength
4: istore_3
5: iload_2
6: iload_3
7: if_icmpge 29
10: aload_1
11: iload_2
12: aaload
13: astore 4
15: aload 4
17: aload_0
18: if_acmpne 23
21: iconst_1
22: ireturn
23: iinc 2, 1
26: goto 5
29: iconst_0
30: ireturn
प्रत्येक के लिए:
private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
Code:
0: aload_1
1: astore_2
2: aload_2
3: arraylength
4: istore_3
5: iconst_0
6: istore 4
8: iload 4
10: iload_3
11: if_icmpge 34
14: aload_2
15: iload 4
17: aaload
18: astore 5
20: aload 5
22: aload_0
23: if_acmpne 28
26: iconst_1
27: ireturn
28: iinc 4, 1
31: goto 8
34: iconst_0
35: ireturn
THAT Oracle के साथ क्या हो रहा है?
मैंने इसे विंडोज 7 पर जावा 7 और 8 के साथ आज़माया है।
इंडेक्सिंग के बजाय इट्रेटर का उपयोग करना हमेशा बेहतर होता है। ऐसा इसलिए होता है क्योंकि अनुक्रमांक (कॉलिंग प्राप्त) नहीं हो सकता है, जबकि इट्रेटर सूची कार्यान्वयन के लिए सबसे अधिक अनुकूलित है। उदाहरण के लिए लिंक्डलिस्ट एक सूची है, लेकिन इसके तत्वों के माध्यम से अनुक्रमणिका इट्रेटर का उपयोग करने की तुलना में धीमी होगी।
foreach आपके कोड को स्पष्ट करता है और यह सामान्य रूप से बहुत मामूली गति सुधार पर पसंद किया जाता है - यदि कोई हो।
जब भी मैं एक अनुक्रमित लूप देखता हूं, तो मुझे यह सुनिश्चित करने के लिए थोड़ी देर पार्स करनी होगी कि मुझे क्या लगता है कि यह ऐसा करता है जैसे क्या यह शून्य से शुरू होता है, क्या इसमें अंतिम बिंदु आदि शामिल है या बाहर है?
मेरा ज्यादातर समय रीडिंग कोड (जो मैंने लिखा या किसी और ने लिखा है) बिताया गया लगता है और स्पष्टता हमेशा प्रदर्शन से अधिक महत्वपूर्ण होती है। इन दिनों प्रदर्शन को खारिज करना आसान है क्योंकि हॉटस्पॉट ऐसा अद्भुत काम करता है।
निम्नलिखित कोड:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
interface Function<T> {
long perform(T parameter, long x);
}
class MyArray<T> {
T[] array;
long x;
public MyArray(int size, Class<T> type, long x) {
array = (T[]) Array.newInstance(type, size);
this.x = x;
}
public void forEach(Function<T> function) {
for (T element : array) {
x = function.perform(element, x);
}
}
}
class Compute {
int factor;
final long constant;
public Compute(int factor, long constant) {
this.factor = factor;
this.constant = constant;
}
public long compute(long parameter, long x) {
return x * factor + parameter + constant;
}
}
public class Main {
public static void main(String[] args) {
List<Long> numbers = new ArrayList<Long>(50000000);
for (int i = 0; i < 50000000; i++) {
numbers.add(i * i + 5L);
}
long x = 234553523525L;
long time = System.currentTimeMillis();
for (int i = 0; i < numbers.size(); i++) {
x += x * 7 + numbers.get(i) + 3;
}
System.out.println(System.currentTimeMillis() - time);
System.out.println(x);
x = 0;
time = System.currentTimeMillis();
for (long i : numbers) {
x += x * 7 + i + 3;
}
System.out.println(System.currentTimeMillis() - time);
System.out.println(x);
x = 0;
numbers = null;
MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
for (int i = 0; i < 50000000; i++) {
myArray.array[i] = i * i + 3L;
}
time = System.currentTimeMillis();
myArray.forEach(new Function<Long>() {
public long perform(Long parameter, long x) {
return x * 8 + parameter + 5L;
}
});
System.out.println(System.currentTimeMillis() - time);
System.out.println(myArray.x);
myArray = null;
myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
for (int i = 0; i < 50000000; i++) {
myArray.array[i] = i * i + 3L;
}
time = System.currentTimeMillis();
myArray.forEach(new Function<Long>() {
public long perform(Long parameter, long x) {
return new Compute(8, 5).compute(parameter, x);
}
});
System.out.println(System.currentTimeMillis() - time);
System.out.println(myArray.x);
}
}
मेरे सिस्टम पर आउटपुट के बाद देता है:
224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895
मैं OracleJDK 1.7 अपडेट 6 के साथ Ubuntu 12.10 अल्फा चला रहा हूं।
सामान्य रूप से हॉटस्पॉट में बहुत सारे अप्रत्यक्ष और सरल निरर्थक संचालन का अनुकूलन होता है, इसलिए सामान्य तौर पर आपको उनके बारे में चिंता नहीं करनी चाहिए जब तक कि उनमें से बहुत से seqence में न हों या वे भारी नस्ट हों।
दूसरी ओर, लिंक्डलिस्ट पर अनुक्रमित मिलता है, लिंक्डलिस्ट के लिए अगले पर कॉल करने की तुलना में बहुत धीमा होता है ताकि आप पुनरावृत्ति को बनाए रखते हुए उस प्रदर्शन हिट से बच सकें जब आप पुनरावृत्तियों का उपयोग करते हैं (स्पष्ट रूप से या प्रत्येक लूप में अंतर्निहित)।
ArrayList या वेक्टर जैसी किसी चीज़ के साथ भी, जहाँ "get" एक साधारण सरणी लुक है, दूसरे लूप में अभी भी अतिरिक्त ओवरहेड है जो पहले नहीं है। मुझे उम्मीद है कि यह पहले की तुलना में थोड़ा धीमा होगा।
सुनिश्चित करने के लिए जानने का एकमात्र तरीका इसे बेंचमार्क करना है, और यहां तक कि यह उतना सरल नहीं है जितना कि यह ध्वनि हो सकता है । JIT कंपाइलर आपके कोड के लिए बहुत ही अप्रत्याशित चीजें कर सकता है।
एंड्रॉइड डेवलपमेंट टीम द्वारा किए गए अंतर का एक संक्षिप्त विश्लेषण इस प्रकार है:
https://www.youtube.com/watch?v=MZOf3pOAM6A
परिणाम है कि है है एक अंतर है, और बहुत बड़ी सूची के साथ बहुत संयमित वातावरण में यह एक उल्लेखनीय अंतर हो सकता है। उनके परीक्षण में, प्रत्येक लूप के लिए दो बार लंबे समय तक लिया गया। हालाँकि, उनका परीक्षण 400,000 पूर्णांकों की सूची से अधिक था। सरणी में प्रति तत्व वास्तविक अंतर 6 माइक्रोसेकंड था । मैंने परीक्षण नहीं किया है और उन्होंने कहा नहीं है, लेकिन मैं अपेक्षा करूंगा कि यह अंतर आदिमता की बजाय वस्तुओं के उपयोग से थोड़ा बड़ा होगा, लेकिन फिर भी जब तक आप लाइब्रेरी कोड नहीं बना रहे हैं, जहां आपको पता नहीं है कि आपके द्वारा पूछे जाने का पैमाना क्या होगा। अधिक से अधिक, मुझे लगता है कि अंतर के बारे में जोर देने लायक नहीं है।
चर नाम से objectArrayList
, मुझे लगता है कि इसका एक उदाहरण है java.util.ArrayList
। उस स्थिति में, प्रदर्शन अंतर स्पष्ट नहीं होगा।
दूसरी ओर, यदि इसका उदाहरण है java.util.LinkedList
, तो दूसरा दृष्टिकोण List#get(int)
एक ओ (एन) ऑपरेशन के रूप में बहुत धीमा होगा ।
इसलिए पहले दृष्टिकोण को हमेशा पसंद किया जाता है जब तक कि लूप में तर्क द्वारा सूचकांक की आवश्यकता न हो।
यह अजीब है कि किसी ने भी स्पष्ट रूप से उल्लेख नहीं किया है - फोरचेक मेमोरी (इट्रेटर के रूप में) आवंटित करता है, जबकि लूप के लिए एक सामान्य किसी भी मेमोरी को आवंटित नहीं करता है। एंड्रॉइड पर गेम के लिए, यह एक समस्या है, क्योंकि इसका मतलब है कि कचरा कलेक्टर समय-समय पर चलेगा। एक खेल में आप कचरा कलेक्टर को चलाने के लिए नहीं चाहते हैं ... कभी भी। तो अपने ड्रॉ (या रेंडर) विधि में फोरच लूप का उपयोग न करें।
स्वीकृत उत्तर प्रश्न का उत्तर देता है, इसके अलावा ArrayList के असाधारण मामले ...
चूंकि अधिकांश डेवलपर्स ArrayList पर भरोसा करते हैं (कम से कम मुझे ऐसा लगता है)
इसलिए मैं यहां सही उत्तर जोड़ने के लिए बाध्य हूं।
डेवलपर दस्तावेज़ से सीधे: -
लूप के लिए बढ़ाया (कभी-कभी "प्रत्येक के लिए" लूप के रूप में भी जाना जाता है) का उपयोग संग्रह के लिए किया जा सकता है जो Iterable इंटरफ़ेस को लागू करता है और सरणियों के लिए। संग्रह के साथ, हैनेटर () और अगले () के लिए इंटरफ़ेस कॉल करने के लिए एक इटेरेटर आवंटित किया जाता है। एक ArrayList के साथ, एक हाथ से लिखा गिनती लूप लगभग 3x तेज (जेआईटी के साथ या बिना) है, लेकिन अन्य संग्रह के लिए लूप सिंटैक्स के लिए बढ़ाया स्पष्ट इट्रेटर उपयोग के बराबर होगा।
एक सरणी के माध्यम से पुनरावृत्ति के लिए कई विकल्प हैं:
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
शून्य () सबसे धीमा है, क्योंकि JIT अभी तक लूप के माध्यम से प्रत्येक पुनरावृत्ति के लिए एक बार सरणी लंबाई प्राप्त करने की लागत को दूर नहीं कर सकता है।
एक () तेज है। यह लुकअप से बचते हुए सब कुछ स्थानीय चरों में खींचता है। केवल सरणी लंबाई एक प्रदर्शन लाभ प्रदान करती है।
JIT के बिना उपकरणों के लिए दो () सबसे तेज़ है, और JIT वाले उपकरणों के लिए एक () से अप्रभेद्य है। यह जावा प्रोग्रामिंग भाषा के संस्करण 1.5 में पेश किए गए लूप सिंटैक्स के लिए वर्धित उपयोग करता है।
तो, आपको डिफ़ॉल्ट रूप से लूप के लिए एन्हांस किए गए का उपयोग करना चाहिए, लेकिन प्रदर्शन-महत्वपूर्ण ArrayList पुनरावृत्ति के लिए एक हाथ से लिखे गिनती किए गए लूप पर विचार करें।
हां, for-each
वेरिएंट सामान्य से तेज है index-based-for-loop
।
for-each
संस्करण का उपयोग करता है iterator
। इसलिए ट्रैवर्सिंग सामान्य for
लूप की तुलना में तेज है जो इंडेक्स आधारित है।
यह इसलिए है क्योंकि iterator
ट्रैवर्सिंग के लिए अनुकूलित किया गया है, क्योंकि यह अगले तत्व से पहले और पिछले तत्व के ठीक बाद की ओर इशारा करता है । index-based-for-loop
धीमे होने का एक कारण यह है कि, उसे हर बार उस तत्व की स्थिति की गणना और स्थानांतरित करना होगा जो उसके साथ नहीं है iterator
।
public class FirstJavaProgram {
public static void main(String[] args)
{
int a[]={1,2,3,45,6,6};
// Method 1: this is simple way to print array
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
// Method 2: Enhanced For loop
for(int i:a)
{
System.out.print(i+" ");
}
}
}