رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران

پست های پیشنهاد شده

10 تکنیک کاربردی که باعث بهینه شدن عملکرد برنامه های جاوا می شود.

1.     Use StringBuilder Class:

به عنوان یک قاعده کلی برای تولید رشته های پیچیده با عملگر (+) ، همیشه از کلاس StringBuilder استفاده نمایید.

String x = "a" + args.length + "b";

که کد فوق به شبه کد زیر کامپایل خواهدشد...

همانطور که مشاهده میکنید خود کامپایلر جاوا هم عملگر + برای جمع چند رشته، به کلاس StringBuilder تفسیر میکند.

 0  new java.lang.StringBuilder [16]
 3  dup
 4  ldc <String "a"> [18]
 6  invokespecial java.lang.StringBuilder(java.lang.String) [20]
 9  aload_0 [args]
10  arraylength
11  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [23]
14  ldc <String "b"> [27]
16  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [29]
19  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [32]
22  astore_1 [x]

اما چه اتفاقی می افتد، اگر بعدا، شما String خود را با رشته های جدید تغییر دهید؟

String x = "a" + args.length + "b";

if (args.length == 1)
    x = x + args[0];

اکنون شما StringBuilder دوم خواهید داشت، که صرفا حافظه را از پشته خود می گیرد با این کار باعث افزایش فشار بر روی GC خواهید شد.
 به جای آن بهتر است بنویسید:

StringBuilder x = new StringBuilder("a");
x.append(args.length);
x.append("b");

واگر String هایی که می خواهید به هم پیوند دهید پیچیده هستند، مرجع StringBuilder را نگه دارید و در متدهای دیگر هم از همان استفاده نمایید.

 

2. Avoid regular expressions :

عبارات منظم نسبتا ارزان و راحت هستند.

اما اگر منظور شما از بیان عبارت منظم چنین چیزی هست که به هیچ وجه قابل قبول نیست.!

String[] parts = ipAddress.split("\\.");

پس بهتر است به کاراکترهای معمولی با عملگر دستیابی[] مبتنی بر شاخص دست یابید.
به عنوان مثال این حلقه کاملا همان کار را انجام می دهد:

int length = ipAddress.length();
int offset = 0;
int part = 0;
for (int i = 0; i < length; i++) {
    if (i == length - 1 || ipAddress.charAt(i + 1) == '.') {
        parts[part] = ipAddress.substring(offset, i + 1);
        part++;
        offset = i + 2;
    }
}

عبارات منظم مفید هستند اما آنها پر هزینه هستند شما باید از عبارات منظم پر هزینه اجتناب کنید.
 مراقب استفاده از انواع روش های JDK String که از عبارات منظم مانند String.replaceAll () یا String.split () استفاده می کند، باشید.

برای مثال می توانید از کتابخانه های محبوبی مانند Apache Commons Lang به جای این روشها استفاده نمایید برای دستکاری رشته ها.

 

3. ()Do not use iterator:

نوشتن شیوه های foreach سبک جاوا 5 مناسب است. شما فقط می توانید به طور کامل در مورد داخل منطق حلقه ها فکر کنید و بنویسید ...

for (String value : strings) {
    // Do something useful here
}

با این حال، هر زمانی که شما به این حلقه وارد می شوید، اگر رشته ای قابل تغییر باشد، یک نمونه جدید Iterator ایجاد خواهید کرد. اگر شما از ArrayList استفاده می کنید، این موضوع در حال تخصیص یک شی با 3 بلاک در قسمت پشته برنامه شما هست طبق تفسیر کلاس زیر...

private class Itr implements Iterator<E> {
    int cursor;
    int lastRet = -1;
    int expectedModCount = modCount;
    // ...

در عوض، شما می توانید حلقه معادل زیر را بنویسید و تنها یک مقدار int را در پشته صرف کنید، شاید کد کثیف تری باشد از نظر بصری، اما بهینه تر است...

int size = strings.size();
for (int i = 0; i < size; i++) {
    String value : strings.get(i);
    // Do something useful here
}

Iterators، Iterable و حلقه foreach از دیدگاه نوشتن پذیری و قابلیت خواندن، و همچنین از دیدگاه طراحی API بسیار مفید هستند. با این حال، آنها یک نمونه جدید کوچک را در پشته برای هر تکرار تنها ایجاد می کنند.

اگر تكرارهای زیادی را اجرا می كنید، می خواهید مطمئن شوید كه این نمونه بی فایده را ایجاد نمی كنید، به جای آن از تكرارهای مبتنی بر index استفاده نمایید.

 

4. Don’t call that method :

برخی از متدها پر هزینه هستند. فرض کنید که JDBC Driver شما نیاز به انجام مشکل فوق العاده برای محاسبه مقدار ResultSet.wasNull() داشته باشد .

کد چارچوب SQL homegrown شما ممکن است مانند این باشد:

if (type == Integer.class) {
    result = (T) wasNull(rs,Integer.valueOf(rs.getInt(index)));
}

// And then...
static final <T> T wasNull(ResultSet rs, T value) 
throws SQLException {
    return rs.wasNull() ? null : value;
}

این منطق اکنون ResultSet.wasNull () را هر بار که int را از مجموعه نتیجه به دست می آورد، فراخوانی می کند. اما متد getInt()می نویسد:

مقدار بازگشت: مقدار ستون؛ اگر مقدار SQL NULL باشد، مقدار بازگشتی 0  است

بنابراین، یک متد ساده اما در عین حال احتمالا دشوار رو به بالا می تواند این باشد:

static final <T extends Number> T wasNull(
    ResultSet rs, T value
) 
throws SQLException {
    return (value == null || 
           (value.intValue() == 0 && rs.wasNull())) 
        ? null : value;
}

 

5. Use primitives and the stack :

از استفاده انواع بسته بندی ها در تعریف متغیرها از نوع اولیه اجتناب کنید..

//heap ذخیره در 
Integer i = 817598;

//stack ذخیره در 
int i = 817598;

وقتی که از آرایه ها استفاده می کنید، بدتر هم می شود:

//heap سه شی در 
Integer[] i = { 1337, 424242 };

//heap یک شی در 
int[] i = { 1337, 424242 };

استثنا برای این قاعده وجود دارد: boolean و بایت مقادیر کافی برای ذخیره شدن توسط JDK دارند. شما می توانید بنویسید:
 

Boolean a1 = true; 
// ... syntax sugar for:
Boolean a2 = Boolean.valueOf(true);

Byte b1 = (byte) 123;
// ... syntax sugar for:
Byte b2 = Byte.valueOf((byte) 123);

همین امر برای مقادیر اولیه از انواع دیگر، از جمله char، short، int، long، صادق است.

هرگز سازنده را بر روی انواع بسته بندی نکنید، مگر اینکه واقعا یک نمونه جدید را بخواهید.
 

6. Avoid recursion :

زبان برنامه نویسی مدرن مانند Scala، استفاده از بازگشت را تشویق می کند، زیرا آنها ابزار بهینه سازی الگوریتم های بازگشت به تکرار تبدیل می کنند.

اگر زبان شما از چنین خوش بینی ها پشتیبانی می کند، ممکن است خوب باشد.

اما حتی در این صورت، کوچکترین تغییر الگوریتم ممکن است یک شاخه ای ایجاد کند که مانع بازگشت شما از بازگشت بازگشتی می شود. که البته باید امیدوار باشید که کامپایلر این خطای منطقی را شناسایی کند! در غیر این صورت، شما ممکن است بسیاری از فریم های پشته را برای چیزی که ممکن است با استفاده از چند متغیر محلی اجرا شود، هزینه کرده اید.

بنابراین سعی کنید تا حد امکان از الگوریتم های بازگشتی استفاده نکنید.(تمامی الگوریتم های بازگشتی را می توان با استفاده از حلقه های for , while پیاده سازی کرد)

 

7. ()Use entrySet :

همیشه ازentrySet ()استفاده کنید وقتی که در طول تکرار Map به دو کلید و مقدار نیاز دارید.

for (K key : map.keySet()) {
    V value = map.get(key);
}

//به جای کد بالا 

for (Entry<K, V> entry : map.entrySet()) {
    K key = entry.getKey();
    V value = entry.getValue();
}


 

8. Use EnumSet or EnumMap :

بعضی موارد وجود دارد که تعداد کلید های ممکن در یک Map پیش از آن شناخته شده است - مثلا هنگام استفاده از یک Map پیکربندی.

اگر این تعداد نسبتا کوچک باشد، باید به جای HashSet یا HashMap به طور منظم از EnumSet یا EnumMap استفاده کنید.

 به سادگی با نگاه کردن بهEnumMap.put () توضیح داده شده است:

private transient Object[] vals;

public V put(K key, V value) {
    // ...
    int index = key.ordinal();
    vals[index] = maskNull(value);
    // ...
}

ماهیت این پیاده سازی این واقعیت است که ما یک آرایه از مقادیر نشان داده شده به جای یک جدول هش داریم.

هنگام وارد کردن یک مقدار جدید، همه کاری که ما باید انجام دهیم برای یافتن ورودی Map از enum برای ترتیب ثابت آن است که توسط کامپایلر جاوا در هر نوع enum ایجاد می شود.

اگر این یک Map پیکربندی Static است (یعنی تنها یک نمونه)، افزایش سرعت دسترسی به EnumMap کمک خواهد کرد که HashMap را به شدت بهبود بخشد، که ممکن است از حافظه heap کمی استفاده کند.

Enum و EnumMap بسیار نزدیک هستند. هر زمان که از ساختارهای enum مثل کلید استفاده می کنید، در واقع ساختن این ساختارها را در نظر بگیرید و از آنها به عنوان کلید در EnumMap استفاده کنید.

 

9. Optimise your hashCode() and equals() methods :

اگر شما نمیتوانید از EnumMapاستفاده کنید، دست کم متدهای hashCode()وequals() را بهینه کنید.(سعی کنید این دومتد را باهم پیاده سازی کنید.)

یک متد خوبhashCode() ضروری است، زیرا باعث می شود که تماسهای بیشتری به نسبت برابر با مقدار بسیار پر هزینه جلوگیری شود، زیرا GC بیشتری را در هر نمونه از آنها تولید می کند.

@Override
public int hashCode() {

    // [#1938] This is a much more efficient hashCode()
    // implementation compared to that of standard
    // QueryParts
    return name.hashCode();
}
@Override
public boolean equals(Object that) {
    if (this == that) {
        return true;
    }

    // [#2144] Non-equality can be decided early, 
    // without executing the rather expensive
    // implementation of AbstractQueryPart.equals()
    if (that instanceof AbstractTable) {
        if (StringUtils.equals(name, 
            (((AbstractTable<?>) that).name))) {
            return super.equals(that);
        }

        return false;
    }

    return false;
}

 

10. Think in sets, not in individual elements :

متاسفانه، بسیاری از برنامه نویسان از نظر الگوریتم های ساده، محلی فکر می کنند.

آنها گام به گام یک مشکل را حل می کنند، شاخه شاخه، حلقه با حلقه، متد و روش. این سبک اجرایی و / یا برنامه نویسی کاربردی است. در حالی که به طور فزاینده ای آسان است که "تصویر بزرگتر" را در حال حرکت از حالت ضروری به شیء گرا (همچنان ضروری) تا برنامه نویسی کارآمد، همه این سبک ها چیزی ندارند که فقط در SQL و R و زبان های مشابه وجود دارد:

Declarative programming

// Pre-Java 8
Set result = new HashSet();
for (Object candidate : someSet)
    if (someOtherSet.contains(candidate))
        result.add(candidate);

// Even Java 8 doesn't really help
someSet.stream()
       .filter(someOtherSet::contains)
       .collect(Collectors.toSet());

برخی ممکن است استدلال کنند که برنامه نویسی کاربردی و جاوا 8 به شما کمک می کند تا ساده تر، الگوریتم های مختصر تر بنویسید.

این لزوما درست نیست شما می توانید ضرورت Java 7 loop خود را به یک مجموعه کاربردی Java 8 Stream ترجمه کنید، اما هنوز همان الگوریتم را نوشته اید.

و برای بهینه تر کردن برنامه های خود می توانید از ابزار قدرتمند JProfiler's 

که در این آدرس Java Profiler - JProfiler  موجود می باشد استفاده نمایید.
 

  • پسندیدن 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

برای اینکه بتوانید دیدگاهی ارسال کنید نیاز دارید که کاربر سایت شوید

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری

×