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?intIntStreamchar
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?intIntStreamchar
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 IntStreamlambda 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 mapToObjunutur ve kullanımı halinde map, o zaman hiçbir şey şikayet edecek, ancak yine de bir ile sona erecek IntStreamve 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 IntStreamve 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 IntStreamonlarla düzgün çalışması için muktedir.
codePoints()yerine kullanmaktır chars()ve zaten bir intkod 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.Characteryanı sıra StringBuilder.appendCodePoint, vb. Bu destek o zamandan beri var jdk1.5.
Stringveya konumunda yedek çiftler olarak gösterilen ek karakterleri işleyecektir char[]. Bahse girerim ki çoğu chariş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 ShortStreammusunuz?) "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, longve double. (Şahsen ben dışarıda intkalı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 charilkel 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 CharStreamilkel 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 IntStreamiç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.charintsPrintStream.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 IntStreambüyük bir sorun olmadığını kabul ediyorum . Ancak geri dönüştürmek için yerleşik bir yol var iyi olurdu IntStreamiç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.
IntStreambir collect()yöntem yok Collector. collect()Önceki yorumlarda belirtildiği gibi sadece üç argümanlı bir yöntemleri vardır.
CharStreamyoksa, eklemek için sorun ne olurdu?