Java 8'de, karakter kodlarını temsil eden bir s ( ) String.chars()
akışı döndüren yeni bir yöntem vardır . Sanırım birçok insan bunun yerine burada bir s akışı beklerdi . API'yı bu şekilde tasarlama motivasyonu neydi?int
IntStream
char
Java 8'de, karakter kodlarını temsil eden bir s ( ) String.chars()
akışı döndüren yeni bir yöntem vardır . Sanırım birçok insan bunun yerine burada bir s akışı beklerdi . API'yı bu şekilde tasarlama motivasyonu neydi?int
IntStream
char
Yanıtlar:
Diğerlerinin de belirttiği gibi, bunun arkasındaki tasarım kararı, yöntemlerin ve sınıfların patlamasını önlemekti.
Yine de, kişisel olarak bunun çok kötü bir karar olduğunu düşünüyorum ve yapmak istemedikleri göz önüne alındığında CharStream
, bunun yerine makul, farklı yöntemler chars()
olduğunu düşünmeliyim:
Stream<Character> chars()
, bu, bazı hafif performans cezalarına sahip olacak bir kutu karakterleri akışı sağlar.IntStream unboxedChars()
performans kodu için kullanılır.Ancak , şu anda neden bu şekilde yapıldığına odaklanmak yerine, bu cevabın Java 8 ile aldığımız API ile bunu yapmanın bir yolunu göstermeye odaklanması gerektiğini düşünüyorum.
Java 7'de böyle yapardım:
for (int i = 0; i < hello.length(); i++) {
System.out.println(hello.charAt(i));
}
Ve Java 8'de bunu yapmak için makul bir yöntem olduğunu düşünüyorum:
hello.chars()
.mapToObj(i -> (char)i)
.forEach(System.out::println);
Burada bir IntStream
lambda aracılığıyla bir nesneye alıyorum ve eşleştiriyorum i -> (char)i
, bu otomatik olarak bir kutuya yerleştirecek Stream<Character>
ve sonra istediğimizi yapabiliriz ve yine de yöntem referanslarını artı olarak kullanabiliriz.
Unutmayın bunu gerçi gerekir yapmak mapToObj
unutur ve kullanımı halinde map
, o zaman hiçbir şey şikayet edecek, ancak yine de bir ile sona erecek IntStream
ve bunu yerine karakterleri temsil eden dizeleri tamsayı değerlerini yazdırır neden merak bıraktığı olabilir.
Java 8 için diğer çirkin alternatifler:
Birinde kalarak IntStream
ve sonuçta yazdırmak isteyerek, yazdırma için artık yöntem referanslarını kullanamazsınız:
hello.chars()
.forEach(i -> System.out.println((char)i));
Ayrıca, kendi yönteminize yöntem başvuruları kullanmak artık işe yaramıyor! Aşağıdakileri göz önünde bulundur:
private void print(char c) {
System.out.println(c);
}
ve sonra
hello.chars()
.forEach(this::print);
Bu, muhtemelen kayıplı bir dönüşüm olduğu için derleme hatası verecektir.
Sonuç:
API çünkü eklemek istemeyen bu şekilde dizayn edilmiştir CharStream
, ben şahsen yöntem döndürmesi gerektiğini düşünüyorum Stream<Character>
ve çözüm şu an kullanmaktır mapToObj(i -> (char)i)
bir de IntStream
onlarla düzgün çalışması için muktedir.
codePoints()
yerine kullanmaktır chars()
ve zaten bir int
kod noktasını ek olarak kabul eden bir çok kütüphane işlevi bulacaksınız char
, örneğin tüm yöntemlerin java.lang.Character
yanı sıra StringBuilder.appendCodePoint
, vb. Bu destek o zamandan beri var jdk1.5
.
String
veya konumunda yedek çiftler olarak gösterilen ek karakterleri işleyecektir char[]
. Bahse girerim ki çoğu char
işlem kodu yanlış çiftleri idare eder.
void print(int ch) { System.out.println((char)ch); }
ve sonra yöntem referanslarını kullanabilirsiniz.
Stream<Character>
reddedildiğine ilişkin cevabımı görün .
Skiwi gelen cevap zaten önemli noktaların pek kaplı. Biraz daha arka plan dolduracağım.
Herhangi bir API'nin tasarımı bir dizi ödünleşmedir. Java'da zor konulardan biri, uzun zaman önce alınan tasarım kararlarıyla uğraşmaktır.
Temel öğeler 1.0'dan beri Java'da. İlkel nesneler nesne olmadığı için Java'yı "saf olmayan" nesne yönelimli bir dil yaparlar. İlkellerin eklenmesi, inanıyorum ki, nesne yönelimli saflık pahasına performansı artırmak için pragmatik bir karardı.
Bu, yaklaşık 20 yıl sonra bugün hala birlikte yaşadığımız bir ödünleşmedir. Java 5'te eklenen otomatik boks özelliği, kaynak kodunu boks ve kutudan çıkarma yöntemi çağrıları ile dağınık hale getirme ihtiyacını çoğunlukla ortadan kaldırdı, ancak ek yük hala orada. Birçok durumda farkedilmez. Ancak, bir iç döngü içinde boks veya kutudan çıkarma yapsaydınız, önemli miktarda CPU ve çöp toplama yükü uygulayabildiğini görürsünüz.
Streams API'sını tasarlarken, ilkelleri desteklememiz gerektiği açıktı. Boks / kutudan çıkarma yükü paralellikten herhangi bir performans faydasını öldürür. Bununla birlikte, tüm ilkeleri desteklemek istemedik , çünkü bu API'ya büyük miktarda karmaşa ekleyecekti. (Gerçekten bir a görebiliyor ShortStream
musunuz?) "Hepsi" veya "hiçbiri" bir tasarım için rahat yerler, ama ikisi de kabul edilebilir. Bu yüzden "bazı" makul bir değer bulmak zorunda kaldı. Biz ilkel uzmanlık ile sona erdi int
, long
ve double
. (Şahsen ben dışarıda int
kalırdım ama bu sadece benim.)
Çünkü CharSequence.chars()
geri dönmeyi düşündük Stream<Character>
(erken bir prototip bunu uygulamış olabilir) ama boks yükü nedeniyle reddedildi. Bir String'in char
ilkel olarak değerleri olduğu düşünüldüğünde , arayan muhtemelen değer üzerinde biraz işlem yapıp doğrudan bir dizgeye geri döndürdüğünde, koşulsuz olarak boks uygulamak bir hata gibi görünebilir.
Ayrıca CharStream
ilkel bir uzmanlık olarak düşündük , ancak kullanımı API'ya ekleyeceği toplu miktarla karşılaştırıldığında oldukça dar görünüyordu. Eklemek faydalı görünmüyordu.
Bunun arayanlara verdiği ceza, temsil edilen değerleri IntStream
içerdiğini ve dökümün uygun yerde yapılması gerektiğini bilmeleri gerektiğidir. Aşırı yüklenmiş API çağrıları olduğu ve davranışlarında belirgin şekilde farklı olduğu için bu iki kat kafa karıştırıcıdır . Ek bir karışıklık noktası da ortaya çıkabilir çünkü çağrı da geri döner, ancak içerdiği değerler oldukça farklıdır.char
ints
PrintStream.print(char)
PrintStream.print(int)
codePoints()
IntStream
Bu, birkaç alternatif arasından pragmatik olarak seçim yapmakla ilgilidir:
Basit, zarif, tutarlı bir API ile sonuçlanan, ancak yüksek performans ve GC ek yükü getiren ilkel uzmanlıklar sağlayamadık;
API'yi karmaşıklaştırma ve JDK geliştiricilerine bir bakım yükü getirme pahasına, eksiksiz bir ilkel uzmanlık seti sağlayabiliriz; veya
oldukça dar bir kullanım alanında (char işleme) arayanlar üzerinde nispeten küçük bir yük oluşturan orta büyüklükte, yüksek performanslı bir API vererek ilkel uzmanlıkların bir alt kümesini sağlayabiliriz.
Sonuncuyu seçtik.
chars()
biri Stream<Character>
(küçük performans cezası ile) ve diğeri döndüren olmak için iki farklı yöntem olamaz diye cevap vermez IntStream
? İnsanların Stream<Character>
performans cezası üzerinde rahatlığa değdiğini düşünürlerse, yine de insanların bunu eşlemeleri muhtemeldir .
chars()
Char değerlerini bir döndüren yöntem zaten varsa IntStream
, aynı değerleri ancak kutulu biçimde alan başka bir API çağrısına sahip olmak için fazla bir şey eklemez. Arayan, değerleri çok sorunsuz bir şekilde kutuya alabilir. Elbette, bu (muhtemelen nadiren) durumda bunu yapmak zorunda değilsiniz, ancak API'ye karmaşa ekleme pahasına daha uygun olacaktır.
chars()
geri dönüşün IntStream
büyük bir sorun olmadığını kabul ediyorum . Ancak geri dönüştürmek için yerleşik bir yol var iyi olurdu IntStream
için String
. İle yapılabilir .reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString()
, ama gerçekten uzun.
collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()
. Sanırım gerçekten daha kısa değil, ancak kod noktalarını kullanmak (char)
dökümleri önler ve yöntem referanslarının kullanılmasına izin verir. Ayrıca, vekilleri düzgün şekilde işler.
IntStream
bir collect()
yöntem yok Collector
. collect()
Önceki yorumlarda belirtildiği gibi sadece üç argümanlı bir yöntemleri vardır.
CharStream
yoksa, eklemek için sorun ne olurdu?