Java'da String Concatenation üzerinden String.format kullanmak daha iyi bir uygulama mıdır?


273

String.formatJava ile dize birleştirmeyi kullanma ve arasında fark edilebilir bir fark var mı ?

Kullanmaya meyilliyim String.formatama bazen kayar ve birleştirme kullanırız. Birinin diğerinden daha iyi olup olmadığını merak ediyordum.

Gördüğüm gibi, String.formatdizeyi "biçimlendirmek" için daha fazla güç verir; ve birleştirme, kazara fazladan% s koyma veya eksik olanı bulma konusunda endişelenmenize gerek olmadığı anlamına gelir.

String.format ayrıca daha kısadır.

Hangisinin daha okunabilir olduğu kafanızın nasıl çalıştığına bağlıdır.


Sanırım MessageFormat.format ile gidebiliriz. Daha fazla bilgi için lütfen stackoverflow.com/a/56377112/1491414 yanıtına bakın .
Ganesa Vijayakumar

Yanıtlar:


242

Kullanmanın daha iyi bir uygulama olduğunu öneririm String.format(). Bunun ana nedeni, String.format()kaynak dosyalardan yüklenen metinle daha kolay yerelleştirilebilirken, birleştirme her dil için farklı kod içeren yeni bir yürütülebilir dosya üretilmeden yerelleştirilemez.

Uygulamanızın yerelleştirilebilir olmasını planlıyorsanız, biçim belirteçleriniz için de argüman konumları belirleme alışkanlığı edinmelisiniz:

"Hello %1$s the time is %2$t"

Bu daha sonra yerelleştirilebilir ve farklı sıralamayı hesaba katmak için yürütülebilir dosyanın yeniden derlenmesine gerek kalmadan ad ve zaman belirteçlerinin değiştirilmesini sağlayabilir. Bağımsız değişken konumlarıyla, aynı bağımsız değişkeni işleve iki kez iletmeden yeniden kullanabilirsiniz:

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)

1
Beni Java'daki bağımsız değişken konumlarıyla / düzeniyle nasıl çalışacağından söz eden bazı belgelere işaret edebilir misiniz (yani, bağımsız değişkenlere konumlarına nasıl atıfta bulunulur)? Teşekkürler.
markvgti

13
Hiç olmadığı kadar iyi, rastgele Java sürümü: docs.oracle.com/javase/1.5.0/docs/api/java/util/…
Aksel

174

Performans hakkında:

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }
  long end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;

  start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = String.format("Hi %s; Hi to you %s",i, + i*2);
  }
  end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
}

Zamanlama sonuçları aşağıdaki gibidir:

  • Birleştirme = 265 milisaniye
  • Biçim = 4141 milisaniye

Bu nedenle, birleştirme String.format çok daha hızlıdır.


15
Hepsi kötü uygulama. StringBuilder kullanın.
Amir Raminfar

8
StringBuilder burada kapsam dışı (OP soru String.format dize Birleştirme üzerinden karşılaştırma hakkındaydı) ama String Builder hakkında veri performans var mı?
Icaro

108
@AmirRaminar: Derleyici otomatik olarak StringBuilder çağrılarına "+" dönüştürür.
Martin Schröder

40
@ MartinSchröder: Çalıştırırsanız javap -c StringTest.class, derleyicinin "+" işlevini yalnızca bir döngü içinde değilseniz otomatik olarak StringBuilder'e dönüştürdüğünü görürsünüz . Birleştirme işlemi tek bir satırda yapılırsa, '+' kullanmakla aynıdır, ancak birden çok satırda myString += "morechars";veya kullanırsanız myString += anotherString;, birden fazla StringBuilder oluşturulabileceğini fark edersiniz, bu nedenle "+" kullanmanın her zaman verimli olmadığı StringBuilder olarak.
ccpizza

7
@Joffrey: Demek istediğim, döngüler +için dönüştürülmediği, StringBuilder.append()bunun yerine new StringBuilder()her bir yinelemede gerçekleştiği idi.
17'de ccpizza

39

Performans hakkında tartışma olduğundan StringBuilder içeren bir karşılaştırma eklemek düşündüm. Aslında concat ve doğal olarak String.format seçeneğinden daha hızlıdır.

Bunu elma karşılaştırması için bir çeşit elma yapmak için, dışarıdan ziyade döngüde yeni bir StringBuilder oluşturuyorum (bu aslında, sonunda ilmek döngü için yeniden ayırma yükü nedeniyle büyük olasılıkla sadece bir örnekleme yapmaktan daha hızlıdır. bir oluşturucu).

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    log.info("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    log.info("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("; Hi to you ").append(i * 2);
    }

    end = System.currentTimeMillis();

    log.info("String Builder = " + ((end - start)) + " millisecond");
  • 2012-01-11 16: 30: 46,058 INFO [TestMain] - Biçim = 1416 milisaniye
  • 2012-01-11 16: 30: 46,190 INFO [TestMain] - Birleştirme = 134 milisaniye
  • 2012-01-11 16: 30: 46,313 INFO [TestMain] - Dize Oluşturucu = 117 milisaniye

21
StringBuilder testi toString () öğesini çağırmaz, bu nedenle adil bir karşılaştırma değildir. Bu hatayı düzeltirseniz, birleştirme performansının ölçüm hatası içinde bulacağınızdan şüpheleniyorum.
Jamey Sharp

15
Birleştirme ve biçim testlerinde bir String. StringBuilder testi, adil olmak için, StringBuilder'ın içeriğini bir String'e dönüştüren son bir adıma ihtiyaç duyar. Bunu arayarak yaparsın bldString.toString(). Umarım bunu açıklar?
Jamey Sharp

4
Jamey Sharp tam doğru. Dld birleşiminden daha yavaş değilse, bldString.toString () yönteminin çağrılması yaklaşık olarak aynıdır.
Akos Cz

3
String s = bldString.toString(); Zamanlamaları neredeyse birbirleriyle eşit birleştirme ve StringBuilder ile vardı: Format = 1520 millisecond, Concatenation = 167 millisecond, String Builder = 173 millisecond ben bir döngü içinde onları koştu ve iyi bir temsilcisi olsun her out ortalama: (Zamanım aldığımda, bir 10.000+ döngü çalışacağız önceden jvm'yi optimizasyonu)
TechTrip

3
Kodun yürütülüp yürütülmediğini nasıl anlarsınız? Değişkenler asla okunmaz veya kullanılmaz, JIT'in bu kodu ilk etapta kaldırmayacağından emin olamazsınız.
alobodzk

37

Sorunlardan biri .formatstatik tip güvenliği kaybetmenizdir. Biçiminiz için çok az bağımsız değişkeniniz olabilir ve biçim belirleyicileri için yanlış türlere sahip olabilirsiniz - her ikisi IllegalFormatException de çalışma zamanında olur , bu nedenle üretimi kesintiye uğratan günlük koduyla karşılaşabilirsiniz.

Aksine, argümanlar +derleyici tarafından test edilebilir.

Güvenlik geçmişi arasında( formatfonksiyonun modellentiği) uzun ve korkutucu.


16
sadece kayıt için, modern IDE'ler (örneğin IntelliJ) argüman sayımı ve tür eşleştirmesine yardımcı olur
Ron Klein

2
Derleme hakkında iyi bir nokta, bu kontrolleri FindBugs (IDE'de veya derleme sırasında Maven üzerinden çalıştırarak) aracılığıyla yapmanızı öneririm, bu da tüm kayıt işlemlerinizin biçimlendirmesini kontrol edeceğini unutmayın! Bu, kullanıcıların IDE
Christophe Roussy

20

Hangisinin daha okunabilir olduğu kafanızın nasıl çalıştığına bağlıdır.

Cevabını orada buldun.

Kişisel bir zevk meselesi.

Dize birleştirme marjinal olarak daha hızlı, sanırım, ama bu önemsiz olmalıdır.


3
Katılıyorum. Buradaki performans farklarını düşünmek sadece erken optimizasyondur - profil oluşturmanın burada bir sorun olduğunu göstermesi durumunda, endişelenin.
Jonik

3
Proje küçükse ve hiçbir zaman anlamlı bir anlamda uluslararasılaştırılmayı amaçlamıyorsa, bu sadece kişisel bir zevk meselesidir. Aksi takdirde String.format, her şekilde birleştirme üzerinden kazanır.
workmad3

4
Katılmıyorum. Proje ne kadar büyük olursa olsun, içinde inşa edilen her dizeyi yerelleştirmek zor olacaksınız. Başka bir deyişle, duruma bağlıdır (dizeler ne için kullanılır).
Jonik

Nasıl kimse 'String.format ("% s% s", a, b)' 'a + b' den daha okunabilir olduğunu düşünmek ve hız sıra büyüklüğü farkı göz önüne alındığında düşünemiyorum yanıt bana açık görünüyor (hata ayıklama veya çoğu günlük kaydı ifadesi gibi yerelleştirme gerektirmeyen durumlarda).
BobDoolittle

16

Burada milisaniye cinsinden birden fazla örnek boyutu olan bir test var.

public class Time {

public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;

public static void main(String[] args) {

  int i = 1;
  for(int run=1; run <= 12; run++){
      for(int test =1; test <= 2 ; test++){
        System.out.println(
                String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
        test(run, i);
      }
      System.out.println("\n____________________________");
      i = i*3;
  }
}

public static void test(int run, int iterations){

      long start = System.nanoTime();
      for( int i=0;i<iterations; i++){
          String s = "echo " + i + " > "+ sysFile;
      }
      long t = System.nanoTime() - start;   
      String r = String.format("  %-13s =%10d %s", "Concatenation",t,"nanosecond");
      System.out.println(r) ;


     start = System.nanoTime();       
     for( int i=0;i<iterations; i++){
         String s =  String.format(cmdString, i);
     }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "Format",t,"nanosecond");
     System.out.println(r);

      start = System.nanoTime();          
      for( int i=0;i<iterations; i++){
          StringBuilder b = new StringBuilder("echo ");
          b.append(i).append(" > ").append(sysFile);
          String s = b.toString();
      }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "StringBuilder",t,"nanosecond");
     System.out.println(r);
}

}

TEST: 1, RUN: 1, Iterations: 1
  Concatenation =     14911 nanosecond
  Format        =     45026 nanosecond
  StringBuilder =      3509 nanosecond

TEST: 1, RUN: 2, Iterations: 1
  Concatenation =      3509 nanosecond
  Format        =     38594 nanosecond
  StringBuilder =      3509 nanosecond

____________________________

TEST: 2, RUN: 1, Iterations: 3
  Concatenation =      8479 nanosecond
  Format        =     94438 nanosecond
  StringBuilder =      5263 nanosecond

TEST: 2, RUN: 2, Iterations: 3
  Concatenation =      4970 nanosecond
  Format        =     92976 nanosecond
  StringBuilder =      5848 nanosecond

____________________________

TEST: 3, RUN: 1, Iterations: 9
  Concatenation =     11403 nanosecond
  Format        =    287115 nanosecond
  StringBuilder =     14326 nanosecond

TEST: 3, RUN: 2, Iterations: 9
  Concatenation =     12280 nanosecond
  Format        =    209051 nanosecond
  StringBuilder =     11818 nanosecond

____________________________

TEST: 5, RUN: 1, Iterations: 81
  Concatenation =     54383 nanosecond
  Format        =   1503113 nanosecond
  StringBuilder =     40056 nanosecond

TEST: 5, RUN: 2, Iterations: 81
  Concatenation =     44149 nanosecond
  Format        =   1264241 nanosecond
  StringBuilder =     34208 nanosecond

____________________________

TEST: 6, RUN: 1, Iterations: 243
  Concatenation =     76018 nanosecond
  Format        =   3210891 nanosecond
  StringBuilder =     76603 nanosecond

TEST: 6, RUN: 2, Iterations: 243
  Concatenation =     91222 nanosecond
  Format        =   2716773 nanosecond
  StringBuilder =     73972 nanosecond

____________________________

TEST: 8, RUN: 1, Iterations: 2187
  Concatenation =    527450 nanosecond
  Format        =  10291108 nanosecond
  StringBuilder =    885027 nanosecond

TEST: 8, RUN: 2, Iterations: 2187
  Concatenation =    526865 nanosecond
  Format        =   6294307 nanosecond
  StringBuilder =    591773 nanosecond

____________________________

TEST: 10, RUN: 1, Iterations: 19683
  Concatenation =   4592961 nanosecond
  Format        =  60114307 nanosecond
  StringBuilder =   2129387 nanosecond

TEST: 10, RUN: 2, Iterations: 19683
  Concatenation =   1850166 nanosecond
  Format        =  35940524 nanosecond
  StringBuilder =   1885544 nanosecond

  ____________________________

TEST: 12, RUN: 1, Iterations: 177147
  Concatenation =  26847286 nanosecond
  Format        = 126332877 nanosecond
  StringBuilder =  17578914 nanosecond

TEST: 12, RUN: 2, Iterations: 177147
  Concatenation =  24405056 nanosecond
  Format        = 129707207 nanosecond
  StringBuilder =  12253840 nanosecond

1
StringBuilder, bir döngüye karakter eklerken, örneğin birer birer bin 1 ile bir dize oluşturmak istediğinizde, kesinlikle en hızlı yöntemdir. İşte daha fazla bilgi: pellegrino.link/2015/08/22/…
Carlos Hoyos

Ben her zaman nasıl çıktı için D String.format kullanın: D bir avantaj var. ve dürüst olmak gerekirse, milyonlarca yineleme hakkında konuşmuyorsak, kodunuz bariz avantajı gösterdiğinden okunabilirlik için string.format'ı tercih ederim!
mohamnag

9

StringBuilder'da toString () yönteminin çağrılmasıyla yukarıdaki gibi aynı test . Aşağıdaki sonuçlar, StringBuilder yaklaşımının + işlecini kullanan String birleşiminden biraz daha yavaş olduğunu göstermektedir .

dosya: StringTest.java

class StringTest {

  public static void main(String[] args) {

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("Hi to you ").append(i * 2).toString();
    }

    end = System.currentTimeMillis();

    System.out.println("String Builder = " + ((end - start)) + " millisecond");

  }
}

Kabuk Komutları: (StringTest'i 5 kez derleyin ve çalıştırın)

> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"

Sonuçlar :

Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond

Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

6

String.format()dizeleri birleştirmekten daha fazlasıdır. Örneğin, tuşlarını kullanarak belirli bir yerel ayardaki numaraları görüntüleyebilirsiniz String.format().

Ancak, yerelleştirmeyi önemsemiyorsanız, fonksiyonel bir fark yoktur. Belki biri diğerinden daha hızlıdır, ancak çoğu durumda ihmal edilebilir olacaktır.


4

Genellikle dize birleştirme tercih edilmelidir String.format. İkincisinin iki ana dezavantajı vardır:

  1. Yerel olarak oluşturulacak dizeyi kodlamaz.
  2. Oluşturma işlemi bir dize olarak kodlanır.

1. noktaya gelince, String.format()tek bir ardışık geçişte bir çağrının ne yaptığını anlamak mümkün değildir . Birisi, argümanların konumunu sayarken format dizesi ve argümanlar arasında ileri geri gitmek zorunda kalır. Kısa birleştirme için bu bir sorun değildir. Ancak bu durumlarda, dize birleştirmesi daha az ayrıntılıdır.

2. noktaya gelince, bina sürecinin önemli bir kısmının biçim dizesinde (DSL kullanılarak) kodlandığını kastediyorum . Kodu temsil etmek için dizeleri kullanmanın birçok dezavantajı vardır. Doğası gereği tür güvenli değildir ve sözdizimi vurgulama, kod analizi, optimizasyon vb.

Elbette, Java dilinin dışındaki araçları veya çerçeveleri kullanırken, yeni faktörler devreye girebilir.


2

Belirli bir ölçüt yapmadım, ancak birleştirme işleminin daha hızlı olabileceğini düşünürdüm. String.format () yeni bir Formatter oluşturur, bu da yeni bir StringBuilder oluşturur (yalnızca 16 karakter boyutunda). Bu, özellikle daha uzun bir dize biçimlendiriyorsanız ve StringBuilder yeniden boyutlandırmaya devam ederse adil bir miktar ek yüktür.

Ancak, birleştirme daha az yararlıdır ve okunması daha zordur. Her zaman olduğu gibi, hangisinin daha iyi olduğunu görmek için kodunuzda bir kıyaslama yapmaya değer. Kaynak paketleriniz, yerel ayarlarınız vb. Belleğe yüklendikten ve kod JITted edildikten sonra, sunucu uygulamasında farklılıklar göz ardı edilebilir.

Belki de en iyi uygulama olarak, düzgün bir StringBuilder (Appendable) ve Locale ile kendi Formatter'ınızı oluşturmak ve bunu yapmak için çok fazla biçimlendirmeniz varsa bunu kullanmak iyi bir fikir olacaktır.


2

Algılanabilir bir fark olabilir.

String.format oldukça karmaşıktır ve altında düzenli bir ifade kullanır, bu yüzden onu her yerde kullanmayı alışkanlık haline getirmeyin, sadece ihtiyacınız olan yerde yapın.

StringBuilder daha hızlı bir büyüklük sırası olurdu (burada birinin işaret ettiği gibi).


1

MessageFormat.formatHem okunabilirlik hem de performans açısından iyi olması gerektiği için devam edebileceğimizi düşünüyorum .

Icaro'nun yukarıdaki cevabında kullandığı programı kullandım MessageFormatve performans numaralarını açıklamak için kod ekleyerek geliştirdim .

  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = "Hi " + i + "; Hi to you " + i * 2;
    }
    long end = System.currentTimeMillis();
    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
  }

Birleştirme = 69 milisaniye

Biçim = 1435 milisaniye

MessageFormat = 200 milisaniye

GÜNCEL:

SonarLint Raporu'na göre, Printf tarzı biçim dizeleri doğru kullanılmalıdır (kalamar: S3457)

Çünkü printf-stylebiçim dizeleri derleyici tarafından çalışma zamanında yorumlanmış yerine doğrulanır, onlar yanlış dizeleri oluşturulan neden hata içerebilir. Bu kural statik korelasyon doğrular printf-stylebiçimi (...) yöntemlerini ararken onların argümanları biçim dizeleri java.util.Formatter, java.lang.String, java.io.PrintStream, MessageFormat, ve java.io.PrintWritersınıflar ve printf(...)yöntemleri java.io.PrintStreamveya java.io.PrintWritersınıflar.

Printf stilini kıvırcık parantezlerle değiştiriyorum ve aşağıdaki gibi ilginç sonuçlar aldım.

Birleştirme = 69 milisaniye
Biçim = 1107 milisaniye
Biçim: kıvırcık ayraçlar = 416 milisaniye
MessageFormat = 215 milisaniye
MessageFormat: kıvırcık köşeli ayraçlar = 2517 milisaniye

Benim Sonuç:
Yukarıda vurguladığım gibi, iyi parantez ve performans faydaları elde etmek için String.format kıvırcık parantez ile kullanmak iyi bir seçim olmalıdır.


0

String Concatenation ve String.Format'ı yukarıdaki programla karşılaştıramazsınız.

Bu aynı zamanda aşağıdaki gibi kod bloğunda String.Format ve Concatenation kullanma konumunu değiştirerek deneyebilirsiniz

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
  }

  long end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
  start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }

  end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}

Formatın burada daha hızlı çalıştığını görünce şaşıracaksınız. Bunun nedeni, oluşturulan başlangıç ​​nesnelerinin serbest bırakılmayabileceğidir ve bellek tahsisi ve dolayısıyla performansla ilgili bir sorun olabilir.


3
kodunu denedin mi Birleştirme her zaman on kat daha hızlı
Icaro

bu "System.currentTimeMillis ()" yürütmek için alınan milis hakkında ne dersiniz: P.
rehan

0

String.Format'a alışmak biraz zaman alır, ancak çoğu durumda buna değer. NRA dünyasında (asla hiçbir şeyi tekrarlamayın) tokenize iletilerinizi (günlük kaydı veya kullanıcı) sabit bir kitaplıkta (statik bir sınıfa neyin tercih ettiğini tercih ederim) tutmak ve bunları String.Format ile gerektiği gibi çağırmak, yerelleştiriyor veya etmiyor. Böyle bir kütüphaneyi bir birleştirme yöntemiyle kullanmaya çalışmak, birleştirme gerektiren herhangi bir yaklaşımla okunması, sorunlarının giderilmesi, düzeltilmesi ve yönetilmesi daha zordur. Değiştirme bir seçenektir, ancak performansından şüpheliyim. Yıllar süren kullanımdan sonra, String.Format ile ilgili en büyük sorunum, başka bir işleve (Msg gibi) geçirdiğimde çağrının uzunluğunun uygunsuz bir şekilde uzun olması, ancak takma ad olarak hizmet etmek için özel bir işlevle dolaşmak kolaydır. .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.