Uplynulo půldruha roku od chvíle, co jsem se, tak trochu proti své vůli, začal učit Javu, a mám-li vyjádřit své rozpoložení lapidárně, rozpaky nepolevují, ba místy přecházejí do stavu tichého zoufalství. Stále mám pocit, že je to spíš náboženství než programovací jazyk, a zklámání se svou četností rozhodně vyrovnají příjemným překvapením.
Tak předně bych očekával, že má-li být Java budoucností programování, jak nám její autoři sugerují, bude mi na počítači na pozadí běžet JRE, které bleskurychle vykoná program, který mu pošlu, vytvářejíc inteligentní a na konkrétní architektuře nezávislou mezivrstvu mezi aplikacemi a procesorem. Leč není tomu tak: kdykoli spustím byť sebedrobnější utilitu v Javě, zahučí větrák, což je neklamnou známkou, že procesor cosi pracného vykonává, a provedení programu trvá nikoli jednotky milisekund, ale v nejlepším případě desetiny sekundy. Což je arci to samé jako Python, interpretovaný jazyk, u kterého není prioritou rychlost běhu, ale rychlost a jednoduchost vývoje.
Dále mi vadí neustále změny prakticky všech API, co jich Java má. Co bylo před rokem state of the art, je dnes anotováno jako @deprecated. Když vezmu algorithmus v K&R C z poloviny 80. let, bez nejmenších potíží ho i dnes zkompiluji, protože i když se leccos dá dělat lépe, požadavek stability je silnější, a novinky se prostě neimplementovaly.
U Javy ne: jako by její vývoj probíhal methodou tekuté písky
. Existuje třída StringBuffer a StringBuilder, které dělají v podstatě to samé, jen kdosi neuměl naprogramovat úkol na poprvé správně. Podobně dvojakost atomických a komplexních číselných typů: když chci 32-bitové celé číslo, musím se rozhodnout, zda zvolím int nebo Integer, aniž by existoval jediný rozumný důvod, proč by tento typ neměl být jen jeden a příslušné zacházení s ním volil podle kontextu překladač; když napíšu, že chci ArrayList<int>, je to chyba, přičemž jejím jediným faktickým důvodem je lenost programátorů.
Zato unsigned typy, které bych velmi potřeboval a na které jsem zvyklý, nemám vůbec, a tak každou chvíli někde zapomenu dopsat k bytu & 0xff a velmi se divím, že mi algorithmus nefunguje, protože byty jsou naprosto nepochopitelně signované.
Nebo I/O třídy: když ponechám stranou, že staré a dobré, fungující API java.io bylo nahrazeno výmyslem v podobě balíku NIO, nikdy jsem nepochopil, proč některé čtecí operace hlásí vyčerpání zdroje tím, že mi pošlou minus jedna, kdežto jiné generují výjimku. A když chci vědět, jak je určitý objekt velký, abych si bez nahlédnutí do Javadocu rovnou hodil korunou, jak to udělat: někdy to zjistím z vlastnosti length, jindy voláním methody length() a ještě jindy musím zavolat getLength() – případně size() nebo getSize(), to patrně podle nálady autora API.
Anebo ty pořád dokola opakované hádky
s překladačem, že proměnná může být neinicialisovaná, když z kontextu použití je naprosto zřejmé, že k takové situaci nemůže dojít, protože buď byla inicialisována uvnitř bloku try, anebo blok skončil výjimkou a k inkriminovanému místu se exekuce nikdy nedostala. A to nemluvím o složitějších situacích, kdy je překladač o tolik chytřejší než programátor, že je netriviálním úkolem ukonejšit ho natolik, aby překlad provedl.
Přitom Java obsahuje spoustu rozumných myšlenek a kdyby byla odpočátku budována s menší dávkou euforie a více rutinérsky, mohlo vzniknout něco pro vývoj počítačového programování podobně zásadního a trvalého jako kdysi C.
Mým posledním výtvorem v Javě je program Bitwriter, o němž si sice nemyslím, že by ho s ohledem na jeho složitost kdy používal kdokoli jiný než jeho autor, ale podobně jako vloni na emulátorech osmibitových retropočítačů jsem si s ním užil spoustu zábavy (a ještě víc zábavy si užiju s tím, k čemu budu program používat). Jen nevím, zda to není zároveň, pokud jde o Javu, moje dílo poslední, a nebudu programovat v něčem, co bude možná méně trendy, ale s mým způsobem myšlení to nebude fatálně inkompatibilní.
Komentáře
A to nemluvím o tom, že vše je „emulated by class“.
Java bohužel byla spíše jako náboženství, než jako praktický jazyk. Důležité je „dělat to správně“ (čti: v souladu s přikázáními javovské víry javovských kněží), než prakticky.
Dodnes jsem nepochopil, proč integer a character a boolean nejsou objekty. Rozdíl mezi primitivními typy a objekty mohl krásně skrýt kompilátor a navenek mohlo být vše objektově.
Mám ale dojem, že všechny moderní jazyky jsou spíše materializací nějaké sekty a jejich víry, než praktickými jazyky. A je jedno, jestli se to jmenuje Java, C#, Rust, Dart, Lua, a nebo jinak.
Já osobně ve svém posledním díle si mnohé knihovny obalil vlastní vrstvou pro zrychlení vývoje a pro ochranu svého duševního zdraví. Nicméně spíše se Javě vyhýbám.
Java má navíc problém, že není vlastně jasné, komu patří licence, a kdo smí/nesmí udělat interpretr Javy.
Miloslav Ponkrác
Mohl, mohl... jenomze tenkrat spis nemohl. Aspon ne tak aby to nevedlo k podstatnemu zpomaleni a ono to tenkrat bylo uz tak pomaly jak... ceske soudy?
Krome toho primitivni typy nemuzou byt `null`, coz je dost vyhoda. Nemoznost deklarovat typ jako non-null me obecne dost stve,
Skrývání rozdílu mezi primitivními a objektovými typy by nijak běh Java programu nezpomalilo, a to ani o femtosekundu.
Miloslav Ponkrác
int fib(int n) {
int[] a = new int[n+1];
a[0] = a[1] = 1;
for (int i=2; i<=n; ++i) a = a[i-1] + a[i-2];
return a[n];
}
Pripadne jak by slo udelat
Object[] a = new int[2];
Je zvláštní, že Microsoft asi nevěděl, že to nejde, zapomněl se poradit s vámi, a tak nejspíše omylem – protože se vás zapomněl zeptat na názor – částečně objektovou syntaxi zařadil do .NET a C#.
Tak od začátku: Celé objektové programování je především syntaktický cukr sám o sobě. Ve skutečnosti celé objekty vytváří jako iluzi kompilátor, to platí jak pro Javu, tak pro každý OOP jazyk.
Ve skutečnosti je objekt hrouda paměti (v C by se řeklo struktura, v Pascalu record), který kompilátor syntaktickým cukrem prezentuje jako „instanci objektu“. Objektové proměnné nejsou v Javě nic jiného, než pouhé pointery do paměti, které jsou syntaktickým cukrem Javy iluzorně programátorovi vydávány za „poroměnné typu objekt“. A null je obyčejný nulový pointer.
Stejně tak metody objektů jsou obyčejné podprogramy, kde k seznamu parametrů je předřazen jeden další parametr, a to pointer na hroudu paměti obsahující data objektu.
Překvapuje mě, že kdokoli programující v objektech si nedokáže uvědomit, že OOP je v jazycích silným nánosem syntaktického cukru, který řeší kompilátor. To, co vám Java prezentuje jako objekty je jen iluze, kterou vám dává kompilátor. Třeba v C můžete také programovat stejně dobře objektově, jako v Javě, ale C nenabízí ten syntaktický cukr, takže to musíte ovládat přímo.
Takže ta politováníhodná událost, kteou Microsoft zapomněl udělat, že se vám nezeptal, že určité věci nejdou, a zrealizovali je – a nikterak to rychlost neovlivňuje, to byste měl Nadellovi nejspíše napsat a seznámit ho s vaším stanoviskem.
Kompilátor samozřejmě ví a zná vnitřní reprezentaci datových typů. A samozřejmě ví, že int bude přímá proměnná a objekty pointery. Stejně tak typová kompatibilita (například všechny objekty s Object) je věc jazyka. Je možné cokoli, co kompilátor dovolí.
Stejně tak kompilátor má veškerou informaci o kontextu použití. Je takový problém, aby v případě, kdy je třeba objekt automaticky bez programátorova zásahu převedl new int ne objekt typu Integer a šoupl ho tam? Je někde řečeno, že kompilátor musí pod datovým typem int strkat vždy to samé? Podle kontextu jednou strčí int, podruhé objekt Integer, s tím, že bude dávat přednost první variantě, kdy to jen jde.
Třeba v C je běžné, že stejná proměnná je jednou uložena v registru, podruhé na zásobníku jako lokální proměnná, potřetí v datové sekci, atd. a není s tím problém.
Proč by kompilátor musel stejný datový typ ukládat vždy stejně. Od toho je to kompilátor, aby věci měnil, jediným požadavkem je, aby se dělalo to co je v kódu.
Ostatně Java kompilátor už podobné vyfikundace dělá. Třeba sčítání řetězců nahradí nějakým svým StringBuilderem na pozadí, protože je to rychlejší a programátor o tom ani neví, pokud se nezajímá.
Miloslav Ponkrác
Muze to vypadat ruzne, treba nejaky Lispy maji jen 28-bitovy int protoze nejaky bity se ukradly pro oznaceni pointru. To samozrejme na rychlosti neprida.
ComplexDouble { public double real; ....
Zrovna jste vyrobil mutable value type, coz vam nezavidim. Ted totiz
ComplexDouble tmp = a[0]; tmp.real = 2;
neni totez co
a[0].real = 2;
protoze menite kopii. Dalsi problemek je
Object[] oa = new Object[1]; oa[0] = cd;
protoze vasich 2*8 bajtu se tam nevleze.
C# neco takovyho ma, ale Java je 24 let stara. Ono to nejak jde, ale rozhodne to neni jednoduchy a tenkrat na to proste nemeli. Pridat to dodatecne je jeste tezsi a nema to prioritu.
msdn.microsoft.com/en-us/library/s1ax56ch.aspx
To ze ignorujete mou otazku neznamena ze vam verim ze na ni znate odpoved.
> A samozřejmě ví, že int bude přímá proměnná a objekty pointery.
To je samozrejme trivialni. Az do okamziku kdy to zacnete cpat do poli a podobne.
> Microsoft zapomněl udělat, že se vám nezeptal, že určité věci nejdou
Ja psal "Mohl, mohl... jenomze tenkrat spis nemohl.". Skoda ze se Gosling roku 1991 nezeptal Microsoftu jak to o osum let pozdeji udelali.
LISP je dynamicky typovaný jazyk, a runtime prostředí LISPu musí zajistit, že jakákoli proměnná přijme hodnotu libovolného typu. U staticky typovaných jazyků, jako je třeba Java má kompilátor dostatečné informace o datových typech a hodnotách k tomu, aby to nemuselo řešit runtime prostředí.
---
„C# neco takovyho ma, ale Java je 24 let stara. Ono to nejak jde, ale rozhodne to neni jednoduchy a tenkrat na to proste nemeli. Pridat to dodatecne je jeste tezsi a nema to prioritu.“
Je to jednoduché, akorát se na vybodli. Prostě Java jede v trendu hloupý jazyk, hloupý kompilátor, chudé prostředky jazyka (vše se emuluje by class). Ve výsledku pak javisté v IDE používají mimo jiné k tomu, aby si vygenerovali různé kusy kódu.
Přidat to dodatečně není nijak těžké, ale Java má raději sektářské ideologie, jak se to musí dělat, než skutečná praktická řešení pro programátora.
To, že se z toho snažíte udělat složitost je nesmysl. Když mi zaplatíte, já to do měsíce do kompilátoru doplním. Tedy napíši celý nový kompilátor Javy i s touto feature. To, že nerozumíte kompilátorům, jste dal najevo dost zjevně.
Miloslav Ponkrác
Kolik chcete? Staci kdyz mi to dodelate do nejnovejsiho eclipsu a javac.
Jistě. Je to jazyk vytvořený pojídači koláčů pro sobě podobné.
Filosofie "když se mi něco zdá moc složité, tak to tam neimplementuji" vedla k zákazu multiple inheritance, k signovanosti všech číselných typů, k použití nestandardního UTF-16 tam, kde by stačilo korektně podporovat standardní UTF-8, a k celé řadě dalších nesmyslů.
Vy mate 53 trid, kolika z nich by se hodila?
A nestaci vam docs.oracle.com/.../defaultmethods.html?
> signovanosti všech číselných typů
Jak casto potrebujete unsigned? Vetsina lidi asi vubec, nekteri to potrebuji a chybi jim to, nekteri to zvladaji v pohode bez.
> použití nestandardního UTF-16 tam, kde by stačilo korektně podporovat standardní UTF-8
UTF-8 was first officially presented at the USENIX conference in San Diego, from January 25 to 29, 1993. Tolik k tematu "standardni".
Az do roku 1996 byl Unicode omezeny na 16 bitu, takze UTF-16 bylo velmi rozumne rozhodnuti.
Java 9 bude interne umet i LATIN-1.
---
Me v Jave chybi hafo veci, ale vazne nic z vyse uvedeneho.
---
> Filosofie "když se mi něco zdá moc složité, tak to tam neimplementuji" vedla
tez k tomu ze jde o jeden z nejpouzivanejsich jazyku. Kazda ficurka neco stoji a to i nejen toho kdo ji implementuje.
Toto je miniaturní projekt, ale u emulátoru PMI-80 (s periferemi cca 250 tříd) jsem na takovou situaci narazil několikrát. Interfacy s defaultními methodami jsou krajně nesystémové řešení.
Jak casto potrebujete unsigned?
Velmi často, ve zmíněném emulátoru skoro neustále, stejně jako v Bitwriteru. Používám-li místo toho signované typy, jdu proti logice Javy. V té by bylo napsat si UnsignedByte jako třídu, jenže tam by zase vznikly známé problémy s ukládáním do polí, že…
Az do roku 1996 byl Unicode omezeny na 16 bitu, takze UTF-16 bylo velmi rozumne rozhodnuti.
Nebylo, protože je to nehorázné plýtvání místem i strojovým časem v důsledku nutnosti za běhu neustále stringy konvertovat tam a zpět.
tez k tomu ze jde o jeden z nejpouzivanejsich jazyku
Nikoli, ta byla důsledkem rozhodnutí udělat to nezávislé na platformě a s brutálně efektivním interpretem mezikodu. Chudoba výrazových prostředků popularitě nepřidala, stejně jako dodnes bohužel částečně přetrvávající neschopnost vytvořit použitelné GUI, které by nevypadalo jako 20 let staré retro.
Ono je to takto: Někdo vzal funkcionální jazyk a funkce, strašlivě je ořezal, a nazval to objektovým programováním. Zvalo se to Smalltalk a Simula. Ale bylo to dobře použitelné, protože autoři obou jazyků mysleli velmi prakticky. Pak přišla Java a ořezala objektové programování do nepoužitelna a pak se jala lepit zbytek.
Bohužel se Java nezamyslela nad dědičností, a dala dědičnosti hned 2 významy: 1) typová kompatibilita různých tříd mezi sebou (potomek může být použit na typu předka), 2) dědění a znovupoužití kódu (potomek dědí data, kód a metody předka). Takže nakonec situaci zalepili interfacy a zákazem multiple inheritance.
V poslední době se začíná zjišťovat, že schopnosti funkcí a původních OOP jazyků jsou větší, a tak se dolepuje: lamba funkce, různé objektové dolepováky.
---
Ohledně UTF-16 se ukazuje krásně, jak Java je neobjektová a na objektovou se jen tváří, a jak selhal její objektový model.
Javovský typ char pro znak je pevně daný jako unsigned 16 bitů. Kdyby to byl objekt, byť emulovaný kompilátorem, mohla to Java později rozšířit na 32 bitů a nemít nejhorší zpotvořeninu jaká kdy kolem unicode vznikla – UTF16.
Jenže Java znak střelila napevno jako primitivní typ a předem dané délce. Když se později unicode rozšířilo z 16 bitů na 32 bitů, byla Java v zadeli, protože „neobjektovost“ a pevná danost typu char to neumožňovala rozšířit. UTF-16 je pouze z nouze ctnost, protože Java má 16bitové znaky.
Jednoduše „nejčistější objektový jazyk“ Java se svou neobjektovostí a nedomyšleností střelil do vlastních řad. Ukázal, jak neohebný a nepraktický při změnách zadání je.
---
„Kolik chcete? Staci kdyz mi to dodelate do nejnovejsiho eclipsu a javac.“
Byl bych velmi drahý, protože to není nic čemu bych se chtěl věnovat. Dělal bych to jen pro finance. A udělal bych pouze javac. Slíbil jsem kompilátor, nikoli IDE.
---
Miloslav Ponkrác
No, ono by to bylo pomaly. Ja si kdysi napsal 3x overloadnutou metodu `unsigned` a problem vyresen, akorat ji nesmim zapomenout zavolat. Kdyz potrebuju simulovat unsigned long, tak mam smulu a musim pouzit neco z docs.guava-libraries.googlecode.com/.../.... Je tam i `UnsignedLong`, ale ten jsem pouzil snad jednou.
> > Az do roku 1996 byl Unicode omezeny na 16 bitu, takze UTF-16 bylo velmi rozumne rozhodnuti.
> Nebylo, protože je to nehorázné plýtvání místem i strojovým časem v důsledku nutnosti za běhu neustále stringy konvertovat tam a zpět.
Ne, prave naopak. Kdyz mate interne UTF-8 a chcete pracovat se znaky namisto s bajty, tak se spousta veci neskutecne zpomali. Kdyz chcete znak cislo 1000, tak musite projet prvnich 1000-5000 bajtu. Java ma podobny problem, pokud pracujete se znaky nad 2**16 (viz treba `String#offsetByCodePoints`). Samozrejme muzete rict, ze znak cislo 1000 jste nikdy nepotreboval.
Jedina konverze je treba pri IO, a tu byste si s UTF-8 usetril, teda pokud vas soubor je UTF-8.
> Nikoli, ta byla důsledkem rozhodnutí udělat to nezávislé na platformě a s brutálně efektivním interpretem mezikodu.
No ale aby tohle slo s danymi prostredky realizovat, muselo se setrit jinde. Navic nektere ficurky proste efektivne udelat nelze ci temer nelze.
> Chudoba výrazových prostředků popularitě nepřidala
Jiste, nektere veci mi vazne chybi. Ale jsou to jine nez u vas a vypada to ze se celkem trefili do toho co je potreba. Kazdy nadava ze neco chybi, ale kazdymu chybi neco jinyho.
Pokud jsem nikdy neslyšel o binárních stromech (u javového programátora nikoli vyloučený stav), pak ano.
Ono nejde o to ze to nejak jde, ale ma to znacny overhead a mizernou pametovou lokalitu. Navic to sezere tu pamet co jste pouzitim UTF-8 pracne usetril (mozna). Navic to musite vytvorit, coz taky neni zadarmo.
Lepsi by mozna bylo vest paralelne `int[]` co by si pro kazdy rekneme 16-ty bajt pamatovalo kolikaty znak to je. To by v porovnani se stromem bylo trivialni ale stejne furt slozity.
Kdybych mel delat `String` od nuly, tak bych asi pouzil `byte[]`, ktere bude interpretovany jako LATIN-1 nebo UTF-16 bez surrogate paru nebo UTF-32. Format by se vybral podle nejhorsiho znaku co v tom je, podobne jako to dela
Java 9.
V porovnani se soucasnym stavem muzete pamet usetrit nebo ji sezrat vic (coz se stane pokud mate hodne 3-bajtovych znaku). Optimalizace delana pro Javu 9 usetri 35-40% pameti pro stringy (zprumerovano pres 950 ruznych aplikaci) a v prumeru bude znamenat zrychleni (o kolik, to je tezko rict, zalezi na tom jake operace se pouzivaji). Pokud mate neco lepsiho, tak sem s tim.
Řešení v Java 9 je pitomé, řekl bych charakteristicky javovsky pitomé: u stringů, které mají převážnou část znaků s codepointem < 127 a jen zlomek jiných, se bude dál používat neúsporné UTF-16.
A víte, co mi přijde nejkomičtější? Že když javac předložíte soubor ve "standardním" UTF-16, nezvládne ho, musíte kodování nastavit ručně, kdežto UTF-8 ano. Komici.
No prave operace uvnitr pameti budou u vas pomalejsi. Dame si zavody?
> Řešení v Java 9 je pitomé, řekl bych charakteristicky javovsky pitomé: u stringů, které mají převážnou část znaků s codepointem < 127
Ne "< 127" ale "<= 255".
> a jen zlomek jiných, se bude dál používat neúsporné UTF-16.
Neusporne?
- Pro zapadni Evropu se pouzije LATIN-1, ktere oproti UTF-8 usetri semtam bajt.
- Pro CJK Unified Ideographs se pouzije UTF-16 ktere oproti UTF-8 usetri.
- Pro cestinu mate pravdu.
> A víte, co mi přijde nejkomičtější? Že když javac předložíte soubor ve "standardním" UTF-16, nezvládne ho, musíte kodování nastavit ručně, kdežto UTF-8 ano.
Java defaultne pouziva platform encoding, to je u me UTF-8 a u vas asi taky. Jeji interni kodovani do toho nema co mluvit.
Budou v průměru rychlejší, protože se bude kopírovat méně dat. Pomalejší, a to pouze zcela marginálně, budou u dlouhých stringů representovaných v Latin-1, protože se bude kopírovat nejen string a jeho délka, ale i strom. Ušetří se přitom procesorový čas tím, že nebude nutné (skoro) nikdy nic konvertovat.
Ne "< 127" ale "<= 255".
V UTF-8 se jedním bytem representují pouze znaky s codepointem <= 127; 127 jsem vynechal, protože to je DEL a ve Stringu se běžně nemůže vyskytnout.
Pro zapadni Evropu se pouzije LATIN-1, ktere oproti UTF-8 usetri semtam bajt.
Ovšem toliko v případě, že se nebudou používat žádné speciální znaky typu oblé uvozovky. Stačí jeden takový znak a neušetříte nic.
Java defaultne pouziva platform encoding, to je u me UTF-8 a u vas asi taky. Jeji interni kodovani do toho nema co mluvit.
Nicméně pokud přečte BOM, měla by poznat, co bude následovat. Přijde mi to zkrátka směšné.
Mnohe operace nic nekopiruji, treba `indexOf`, `compareTo` a `trim`. Zato ho prolizaji, coz vam dava vyhodu.
Pojdme si dat ty zavody. Vyberte metody a stringy, ja pridam svoje. Psani benchmarks neni trivialni ale existuje na to JMH. Nebo proste napisem metodu trvajici zhruba minutu co bude delat vsechno mozne a zmerime to.
> > > Řešení v Java 9 je pitomé, řekl bych charakteristicky javovsky pitomé: u stringů, které mají převážnou část znaků s codepointem < 127 a jen zlomek jiných, se bude dál používat neúsporné UTF-16.
> > Ne "< 127" ale "<= 255".
> V UTF-8 se jedním bytem representují pouze znaky s codepointem <= 127; 127 jsem vynechal, protože to je DEL a ve Stringu se běžně nemůže vyskytnout.
Ale my se bavime o reseni z Javy 9 pouzivajicim LATIN-1 a ne o UTF-8 (viz citace vyse).
> Ovšem toliko v případě, že se nebudou používat žádné speciální znaky typu oblé uvozovky. Stačí jeden takový znak a neušetříte nic.
Ano.
> Nicméně pokud přečte BOM, měla by poznat, co bude následovat.
Mozna.
UTF-8 BOM je 0xEF,0xBB,0xBF.
U+EFBB is one of the 6400 characters in the Private Use Unicode subset.
U+BBEF je HANGUL SYLLABLE MYIS, at je to co je to.
Takze BOM nevylucuje ze se jedna o UTF-16, ale vylucuje ze jde o platny Javovsky zdrojak v nem.
Vy davate BOM do UTF-8???
K tomu nám chybí soubor charakteristických stringů a charakteristických operací s nimi, takže výsledek by byl irelevantní.
Vy davate BOM do UTF-8???
Ne, ale v UTF-16 je na začátku standardně. Přesto javac podle něj soubor nepoznal a vyžadoval explicitní určení.
Ne uplne. V Java je nejcastejsi operaci `charAt`, takze byste to s UTF-8 jasne projel. Ale to by se vyresilo nejakym trikem jako treba prepisem
for (int i=0; i takže text, kde je aspoň jeden bœuf
Ono je sumak jestli to pro vas hovezi text projde, dulezite je ze se usetri 35-40% na realnych datech sebranych z 950 aplikaci.
Jak jste na to přišel? Nejčastější operací je zjišťování length a next(), a to je obojí v O(1), tedy v konstatntním čase. charAt() je v O(log n), nicméně velmi ušetřím na dalších operacích, jako např. kopírování. Kritická je pro můj přístup konkatenace a substr(), ale případné ztráty na nich si bohatě vykompensuji rychejším kopírováním a rychlejší konversí pro přenos do I/O. Ale pro skutečný benchmark by se muselo vědět, kolik čeho a nakých strinzích.
Ono je sumak jestli to pro vas hovezi text projde, dulezite je ze se usetri 35-40% na realnych datech sebranych z 950 aplikaci.
Ale oproti současnému stavu, kdy je vše UTF-16. To je podobné, jako zdražit zboží o 100 % a pak nabídnout 40% slevu. No nekup to – a nemyslící ovce tleskají a kupují.
OK, `length` jsem vynechal, protoze je trivialni a protoze se zainlajnuje, takze tam vlastne ani neni.
Zadny `String#next` neznam, to asi nebude Java??? Nebo proste abstrahujete, to beru.
---
> Ale pro skutečný benchmark by se muselo vědět, kolik čeho a nakých strinzích.
Samozrejme. Tak neco udelejme, ze to nebude 100% realny neni zas tak dulezity, to se da vzdycky vytunit. A me nezalezi na tom jak to dopadne, sam bych zkusil jednu implementaci na bazi UTF-8 (ale asi bez stromu, protoze ty podle vas Java programatori neumi :D:D:D).
---
> jako zdražit zboží o 100 %
Jenomze UTF-16 je 2x delsi nez UTF-8 pouze pro ASCII, pro vsechny jine znaky je stejne dlouhe nebo kratsi. Takze to zdrazeni je nekde mezi -33% a +100% v zavislosti na frekvenci znaku.
> pak nabídnout 40% slevu
Coz za vaseho nespravneho predpokladu ze predchozi zdrazeni bylo o celych 100% znamena zvbyseni pametove narocnosti (oproti UTF-8) o 20%. Pokud to vyrazne zvysi rychlost, pak to i tak za to stoji.
String není pro programátora iterabilní, ale na nízké úrovni je procházení jím nejčastější operací.
Tak neco udelejme, ze to nebude 100% realny neni zas tak dulezity, to se da vzdycky vytunit.
Nikoli, to se nedá "vytunit". Pokud nemáte tvrdá data, jaké stringy se používají a co s nimi jak často dělá, je jakýkoli benchmarking k ničemu.
Jenomze UTF-16 je 2x delsi nez UTF-8 pouze pro ASCII, pro vsechny jine znaky je stejne dlouhe nebo kratsi.
Nevíme, jak časté jsou stringy s takovými znaky, takže tím je veškeré uvažování opřené o kolo.
Pokud to vyrazne zvysi rychlost, pak to i tak za to stoji.
Tvrdíte-li, že je to lepší než současný (žalostný) stav, budu s vámi souhlasit, a to vřele: téměř všechno je lepší než takový paskvil.
Ale neni. Pokud to bude vypadat nadejne, pak se data jiste daji sehnat. Ono by to slo i ted ale nezda se mi ze byste je potreboval.
> Tvrdíte-li, že je to lepší než současný (žalostný) stav, budu s vámi souhlasit, a to vřele: téměř všechno je lepší než takový paskvil.
Vase ultrasuperchytre reseni uz se taky zkouselo a nebylo to ono. Nevim proc si myslite ze to umite lip ale rad se necham prekvapit.
Vubec ne. Zatim bych udelal prototypalni TPString a TPStringBuilder. Pokud budou co k cemu, tak bych to zabudoval do standardnich typu Javy 9, vyladil a nabidnul na vyzkouseni, pripadne si ty representativni data vyzadal (mozna se dokonce vali nekde v JDK repository). Akorat by je bylo potreba presvedcit ze `charAt` lze temer vzdy eliminovat (bud na urovni javac nebo JIT).
Ani jedno z toho jsem jeste nedelal, ale to jen proto, ze vim ze to neni jednoduche a protoze jsem nemel motivaci. Podle me bysme stejne skoncili u nedostatecne rychleho prototypu, ale to by zas bylo radove min prace.
Implementace je založena na immutabilitě Stringů, StringBuilder nemá smysl měnit.
nabidnul na vyzkouseni, pripadne si ty representativni data vyzadal
To budete jako Edison, který si žárovku nemá kde vyzkoušet, protože neumí vyrobit elektřinu (něco podobného je gravitační elektrárna pro satelity: skvělá myšlenka, jen prakticky nefunguje). Do toho nejdu.
Modifikovalo by se poměrně dost tříd, potenciálně i takových, které nepřistupují ke Stringu sekvenčně, ač mohou, ale randomly.
Ono by to asi stejne bylo na nic. Prumerna delka string je 45 znaku, tedy v UTF-16 mene nez 6 XMM registru, takze kopirovani zas tak moc casu nezabere (pokud zrovna nekopirujete nejake monstrum kde vas omezi memory bandwidth ale to neni zas tak casto).
To co byste usetril na kopirovani byste lehce ztratil kdykoliv by bylo treba udelat neco nesekvencniho, treba substring. A to i s s pouzitim stromu. Jedna dve branch mispredictions a je vymalovano.
> Modifikovalo by se poměrně dost tříd
Nejvic asi ten (Abstract)StringBuilder co jsem o nem psal.
Problém vznikl tím, že kdysi kterýsi trouba (zřejmě typický programátor v Javě: v očích zápal, v hlavě seno) usoudil, že dereference v poli podle indexu je rychlá operace a není proto nutné dělat stringy iterabilní, ale postačí array znaků. Ostatní problémy se na to nabalují.
Proc TreeMap a ne HashMap?
Meril jste nekdy pomer rychlosti tech dvou?
---
S tim byste dokonale projel na vsech frontach, vcetne pameti.
Pro nejaky mirne exoticky jazyk jako treba rustina tam nebude nic jinyho nez interpunkce a escape. Potrebujete ulozit polohu a hodnotu, rekneme 4+2 bajty na znak plus ten vas escape plus nejaky overhead. Takze jsme na 8+ bajtech pro znak. UTF-32 bledne zavisti.
---
> není proto nutné dělat stringy iterabilní, ale postačí array znaků
Ti troubove o kterych pisete jsou schopni to iterovani nasimulovat bez toho aby se pouzival explicitni iterator. Transformace z
for (int i=0; i<s.length(); ++i) {char c = s.charAt(i); ...}
na neco jako
for (int i=0, j=0; i<s.length(); ++i, j+=tpLengthAt(j)) {char c = s.tpCharAt(j); ...}
by nebyla slozitejsi nez jine transformace co JIT dela.
Pro tento případ by se musel volit basový znak šestnáctibitový. Prototože řetězec je immutabilní, lze ho projít před uložením a zvolit optimální strategii. Proč ne třeba Huffmanovo kodování a LZ kompresi, možností je mnoho.
A ted navrhujete to co "očích zápal, v hlavě seno" programatori, nämlich UTF-16, jen namisto surrogate paru mate escape do nekam pryc.
To by mohlo byt rozumny, ale ty surrogate pary se pouzivaji tak malo ze se na ne proste kasle.
Pripadne mate navic jeste LATIN-1 jako Jave 9. Mozna taky s pouzitim escapes, ale pak je potreba nejake netrivialni kriterium ktera reprezentace se pouzije.
> immutabilní... Huffmanovo kodování a LZ kompresi
Jenomze immutabilni neznamena vecny a komprese je neco tak neskutecne pomalyho ze by se se to pro vetsinu stringu tezko vyplatilo. A dekomprese taky neni rychlostne zadny zazrak.
> Pro tento případ
To je dalsi vec. Vybrat reprezentaci podle typu stringu zni rozumne a dokazu si predstavit ze treba 5 ruznych reprezentaci by melo smysl. Jenomze kazda sranda neco stoji a naprosta vetsina objektu zije prilis kratce na takove vyfikundace.
Celé mi to přijde jako myšlení "640KB ought to be enough for anyone": 16bitový znak musí přece stačit každému! Přitom pro někoho je příliš úzký, pro jiného příliš široký.
Ale prd, psal jsem vyse ze tehdy se vse vlezlo do 16 bitu. A tak dali prednost jednoduchosti a rychlosti pred pameti.
programmers.stackexchange.com/q/174947/14167
Je zvlastni ze C# prevzal UTF-16. Ze by taky "v očích zápal, v hlavě seno"?
> 16bitový znak musí přece stačit každému!
To si bezte stezovat na Unicode consortium. Ti nejdriv vymysleli ze bude potreba jen 16, pak 31 a nakonec 20.087462841250343 bitu. A co se koduje neni znak ani glyph ale codepoint. Takze "Á" je U+00C1 anebo U+0041 U+0301, pokud ovsem nechcete jednu ze tri jinych (ne nutne spravnych) variant. Plus tohle:
www.unicode.org/.../0067.html
---
> Pro vyšší efektivitu random přístupu by postačilo uložit binární strom adres ve stringu, např. do úrovně bloků o 256 položkách.
Asi jednodussi a rychlejsi by bylo asi ukladat kazdy 256-ty charovy index v poli, rekneme neco jako
byte[] utf8 = new byte[byteLength]
int[] starts = new int[byteLength>>8];
char charAt(int index) {
int start = starts[index>>8];
for (int i=0; i<(index&255); ++i) {
start++;
if ((utf8[start] & 0x80) != 0) {
do {
start++;
} while ((utf8[start] & 0xC0) = 0xC0);
}
return charAtByteIndex(start);
}
int charIndexToByteIndex(int charIndex) {
int result = starts[charIndex>>8];
for (int i=charIndex&255; i-- > 0; ) {
result += (TABLE >> (4 * (utf8[result] >> 4))) & 15;
}
return result;
}
coz je krasne krypticke nacpanim 16 4-bitovych hodnot do longu. Bude v tom tak asi 10 chyb.
Z toho stromu jsem ovsem jelen.
- kuk na starts[15000000 >> 8] = starts[58593] coz je napriklad 98765
- a od bajtu cislo 98765 jen 15000000 & 255 = 192 iteraci
No, a co cekate, kdyz Java je tech prvnich par milisekund taky 100% interpretovana? Delat v Jave neco co trva mene nez sekundu a cekat od toho rychlost je spatne. Viz tez Ja su rad ze tam nejsou. Dalsi pozadavek by asi byl `List`, dale `List` a to cely vede do pekel. Na chybejici `& 0xFF` jsem se samozrejme taky parkrat nachytal, ale to se da prezit.
> byty jsou naprosto nepochopitelně signované
Divny by spis bylo kdyby to byl jediny unsigned typ.
> výmyslem v podobě balíku NIO
Ja to taky nemusim. Je to v necem lepsi (rychlost) a jinak taky zmrseny.
> z vlastnosti length... podle nálady autora API
Souhlas. Zabit malo.
> proměnná může být neinicialisovaná, když z kontextu použití je naprosto zřejmé
Muzete uvest nejaky priklad. Me samozrejme neco napadlo, ale to nesedi s vasim popisem.
> kdy je překladač o tolik chytřejší než programátor
Naopak, co pro prekladac je zrejme je presne definovano par primitivnimi pravidly a on nesmi byt chytrejsi. Treba `int f(int x) {if (x==0) return 0; else if (x!=0) return 1;}` nesmi projit.
Nicmene ja se s prekladacam hadam zcela vyjimecne a s casem ktery bych ztratil kdyby neinicializovane promenne nedetekoval se to neda srovnavat.
> mým způsobem myšlení to nebude fatálně inkompatibilní
Mozna si zvyknete a mozna se vam to i zalibi. Misty ten kod vypada hodne ne-javovsky, napr. chybejici exception chaining a silene dlouhy metody (ty maji mimochodem casto negativni vliv na rychlost, protoze je tezky je optimalizovat a JIT umi inlining ale ne outlining).
Pokud vam jde o rychlost, pak vase pouziti `BigInteger` asi neni optimalni. Dale veci jako `value = Math.round((Double)value);` nasledovano `new BigInteger(value.toString()))` nejsou ani optimalni ani hezke. Mozna by `BigInteger.valueOf(((Double)value).longValue())` bylo spravne.
Ale ať se to tedy nejmenuje byte. Ten je z definice unsigned. Toto není dobře vyřešené ani v C, kde může být stejně nelogicky signovaný char, nicméně nevidím důvod, proč tuto zjevnou chybu propagovat do dalšího jazyka.
Muzete uvest nejaky priklad. Me samozrejme neco napadlo, ale to nesedi s vasim popisem.
MyObject x; try { x = new MyObject(parameters); } catch (MyException exception) { throw new MyOtherException("Message"); případně return null; } log.fine("MyObject created: " + x);
Po bloku try/catch bude x vždy inicialisované, což by kompilátor měl poznat. Váš příklad je jiného druhu.
Pokud vam jde o rychlost, pak vase pouziti `BigInteger` asi neni optimalni.
U Bitwriteru na rychlosti nezáleží, to bych programoval zcela jinak.
Mozna by `BigInteger.valueOf(((Double)value).longValue())` bylo spravne.
Nebylo, naopak by neprošlo testy (můžete naklonovat a vyzkoušet ). Nashhorn převádí všechna čísla na jeden universální typ, což je Double, a zaokrouhlení je proto nezbytné.
ScriptProcessor.java:131: Z bindings přečtu hodnotu proměnné name (měla by tam být, protože jsem ji na ř. 119 inicialisoval). Nevím, jaký má typ, může to být leccos (experimentálně ověřeno). Jestliže je to Double, musím hodnotu zaokrouhlit (ř. 139). Poté se pokusím z hodnoty, o které stále nevím, co je to za typ, vytvořit BigInteger, což dělám jediným možným universálním způsobem, s mezipřevodem na String (ř. 143).
Domnívám se, že je to tak správně.
Osobne bych tomu rikal treba ubyte, ale na to uz je peknych par let pozde. Beru to tak ze slovo "byte" nerika nic o signedness a v Java je vsecko signed.
> Toto není dobře vyřešené ani v C, kde může být stejně nelogicky signovaný char
V C je to tragedie, protoze se pouziva `char` kde by mel byt `byte` a protoze nic neni definovany.
> Po bloku try/catch bude x vždy inicialisované, což by kompilátor měl poznat.
Ale on to pozna, tohle projde:
void q() {Object x; try {x = "a";} catch (final Exception e) {throw new RuntimeException(e);} System.out.println(x); }
> Nashhorn převádí všechna čísla na jeden universální typ, což je Double, a zaokrouhlení je proto nezbytné.
No, ale `Double#longValue` vraci `long`, nezaokrouhluje ale orezava. Pokud jste nekdo nepoztracel presnost, tak to pro cisla do asi 2**56 bude totez. Pokud vam 2**56 nestaci, pak mate stejne smulu, protoze `long` se do `double` proste nevleze.
I kdyby jste to zaokrouhlovani opravdu potreboval, tak nema smysl delat
value = Math.round((Double)value);
return new BigInteger(value.toString()));
kdyz muzete udelat
long l = Math.round((Double)value);
return BigInteger.valueOf(l);
Ta oklika pres `Long` a `String` nepridava ani na rychlosti ani na citelnosti.
> můžete naklonovat a vyzkoušet
Naklonovano a vyzkouseno, ale neprojde to ani bez te zmeny. Mam ale jen Javu 7 a vas kod je 8, ale to by nemelo nic ovlivnit. Divne.
Error in process, string (1) expected:<0> but was:<3>
No i kdyz nezalezi, tak bych pouzil vlastni tridu namisto `BigInteger`. Umela by veci ktere delate opakovane, treba `valueOf(Object)`, kde parametrem je vase `value`. Mozna by byla implementovana pomoci `BigInteger` a mozna ne.
> Poté se pokusím z hodnoty, o které stále nevím, co je to za typ, vytvořit BigInteger, což dělám jediným možným universálním způsobem, s mezipřevodem na String (ř. 143).
Aha. No kdyz stale nevite co to je, tak pak snad jo. Jinak to jde samozrejme lepe. A mnozstvi typu ktere se tam muzou vyskytnout je asi omezene, ne?
K tem chybam co mi to hazi:
1. TestProcessor#testInputTreeProcessor:
Error in process, string (1) expected:<0> but was:<3>
Ta je zpusobena tim ze to nenajde "input/Crc-Crc32C-1.xml", vy vracite `null` a pak s tim nepocitate. Obvykle by bylo lepsi nechat tu `NullPointerException` proste probublat.
2. TestBitWriter#testProcess:
Je tam "Processing error: Cannot initialize the bindings". Proc se to deje, nevim, ale vas test by to mel vypsat. Tim chytanim vyjimek bez exception chaining se ztraci kopec informace.
Mate tam asi 50x vic try-catch nez je normalni. Vy s vyjimkama zachazite jako by to bylo errno a to je spatne. Nechte je byt, zkratite se kod na polovinu a bude to lepsi.
"catch (NullPointerException e)" je temer vzdy spatne. Proste ji nepripustit je spravne.
"catch (...) {return null;} je temer vzdy spatne, protoze se ztrati vsechna informace a s tim `null` to zdechne o kus dal.
V nejmene 90% pripadu je spravne nedelat nic. V 90% zbyvajicich pripadu je exception chaining spravne. Proste jen pridat informaci nebo zmenit typ vyjimky.
Máte pravdu, jsem překvapen. Takto jednoduché případy tedy pozná.
No, ale `Double#longValue` vraci `long`, nezaokrouhluje ale orezava. Pokud jste nekdo nepoztracel presnost, tak to pro cisla do asi 2**56 bude totez. Pokud vam 2**56 nestaci, pak mate stejne smulu, protoze `long` se do `double` proste nevleze.
Spíš bych řekl 2**53 než 56, ale stejně nemáte pravdu. V každé arithmetice, která používá pro float representaci binary a ne decimal, se může stát, že v důsledku zaokrouhlovací chyby vyjde výsledek, který má být celočíselný, jako celé číslo + delta, přičemž znaménko delty je nepředvídatelné. Takže kdybych mechanicky vzal Double a udělal longValue(), dostanu něco jiného, než bych očekával.
Ta oklika pres `Long` a `String` nepridava ani na rychlosti ani na citelnosti.
Naopak, je elegantní. Povšimněte si, že se na String převádí jakékoli hodnoty, nejen Double. Tedy např. i BigInteger, který tak neztratí hodnotu. Elegance je v tom, že mohu k BigIntegerům, nejsou-li "nadměrečné", přistupovat jako k normálním numerickým hodnotám.
Rychlé to není, ale Bitwriter přece ani rychlý být nemá. Je to program na generování malých binárních souborů.
Naklonovano a vyzkouseno, ale neprojde to ani bez te zmeny. Mam ale jen Javu 7 a vas kod je 8, ale to by nemelo nic ovlivnit. Divne.
Přirozeně. Sedmička používala pro JavaScript Rhino, v osmičce je s ním nekompatibilní Nashhorn. Výrazy jsou stejné, ale javové objekty se tam odkazují jinak.
Žádný dostatečně přísný test jsem tam arci neměl; přidám
To jsem zvažoval, ba i zkoušel, ale od záměru jsem upustil, protože subclassovat BigInteger nestačí a zkopírovat (resp. obalit wrapperem) všechny methody je nesmyslně pracné.
Ta je zpusobena tim ze to nenajde "input/Crc-Crc32C-1.xml", vy vracite `null` a pak s tim nepocitate. Obvykle by bylo lepsi nechat tu `NullPointerException` proste probublat.
Vy jste si svévolně změnil build.xml na nepodporovanou versi, to se pak nemůžete divit, že vám program dělá psí kusy.
"Probublávání" jako uživatel cizího sw bytostně nesnáším, protože je mi úplně k ..., že se dozvím, že na řádku 1345 soubour HyperSuperDuperProcessor.java (neby py, v Pythonu je to to samé) vznikla excepce HyperSuperDuperException. Povinnosít programátora je sdělit uživateli něco relevantního, v daném případě např. to, ve kterém výrazu poslaném do skript processoru má chybu.
catch (...) {return null;} je temer vzdy spatne, protoze se ztrati vsechna informace a s tim `null` to zdechne o kus dal.
To může být legitimní, pokud je null součástí kontraktu: hlásí volajícímu kodu neúspěch. Jinak samozřejmě ne, a v Bitwriteru takovou konstrukci ani jednou nemám.
První verze C neměla unsigned typy, takže vše bylo signed, včetně char. Později ANSI C zavedla klíčová slova signed a unsigned, které lze směle používat ke každému celočíselnému typu.
Typ char tedy není signed, ale může být jak signed, tak unsigned podle typu a nastavení kompilátoru. Ale „unsigned char“ je vždy unsigned.
Miloslav Ponkrác
> Spíš bych řekl 2**53 než 56
Ano.
> stejně nemáte pravdu
Mam... vzdycky. :D:D Jen se nekdy blbe vyjadruju. Pokud pocitate 0.3 + 0.7 tak muze dojit k chybe. Pokud pocitate 123456789.0 + 987654321.0, tak ne. Kdyz oba operandy i vysledek lze vyjadrit presne, tak pro elementarni operace zadna zaokrouhlovaci chyba nesmi nastat. Predpokladam ze to je vas pripad. Se zaokrouhlovanim se muzete citit bezpecneji, a ono to bude fungovat o chvilku dyl, ale zadna zaruka to neni: Pokud cisla budou opravdu velky, tak bude oboji spatne.
> Naopak, je elegantní. Povšimněte si, že se na String převádí jakékoli hodnoty, nejen Double.
Tak jo, de gustibus...
> Sedmička používala pro JavaScript Rhino, v osmičce je s ním nekompatibilní Nashhorn.
OK, tim je to jasny.
> subclassovat BigInteger nestačí a zkopírovat (resp. obalit wrapperem) všechny methody je nesmyslně pracné.
Pokud ta vec se kterou delate je opravdu velky cislo, pak proti `BigInteger`u nic nemam. Ja mel pocit, ze neni ale asi je.
---
> Vy jste si svévolně změnil build.xml na nepodporovanou versi, to se pak nemůžete divit, že vám program dělá psí kusy.
To se pekne pletete, ja doted ani nevedel ze tam nejaky build.xml je.
Chyba byla na me strane, protoze jsem nedal test/res do eclipsiho build classpath, ale co jsem kritizoval je ze jste ten problem totalne zatajil.
---
> "Probublávání" jako uživatel cizího sw bytostně nesnáším
No ale jako programator byste to mel milovat. Pokud jde o informace pro uzivatele, tak mu ukazte co chcete, ale to neni duvod pro chytani exceptions vsude a furt.
> Povinnosít programátora je sdělit uživateli něco relevantního, v daném případě např. to, ve kterém výrazu poslaném do skript processoru má chybu.
Jiste. Ale to se probublavanim nevylucuje. Proste vsude kde to ma smysl pridejte dalsi informace. Zadny nezahazujte. Nikdy. Uzivateli ukazte jen to cemu bude rozumnet, programatorovi ukazte vsechno.
U me to nejede kvuli
sun.org.mozilla.javascript.internal.EcmaError: ReferenceError: "Java" is not defined.
...
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:240)
...
at cz.pecina.bin.bitwriter.ScriptProcessor.(ScriptProcessor.java:345)
a vy jste uplne zatajil ze se tam neco takovyho dela. Nashorn v Jave 7 nefunguje, ale existuje backport, takze nekdo by mohl chtit backportovat vas program (z Javy 8 jinak nic krome jedny default metody nepouzivate) a pak mu takova informace prijde vhod.
---
> > catch (...) {return null;}
> v Bitwriteru takovou konstrukci ani jednou nemám.
Mate ji v `TestProcessor#resourceToString`. Jsem si nevsimnul ze je to jen test, tam to neresim.
To záleží, co chcete programovat. Pokud má být program spolehlivý, pak by program neměl mít miliardu míst, odkud probublá výjimka, která ukončí program.
Nehledě na to, že uživateli je takový text výjimky většinou naprd.
Ideální je, když všechny výjimky vyřeší programátor, v případných handlerech jejich typ vymění podle toho, co čeká vyšší vrstva, a na konec to probublá jen v krajním případě.
Java stejně neumožňuje takový spolehlivý program udělat, protože některé výjimky se ošetřit rozumně nedají a často i Java runtime ani nevolá handlery (při překročení stacku, při nedostatku paměti, atd.).
A to nemluvím o tom, že výjimka je součástí threadu, a pokud mám hodnotit a udržet v chodu silně multithreadový program, tak nemohou z threadů jen tak mýrnix týrnix probublávat náhodně výjimky.
Miloslav Ponkrác
To jistě, ale se zaokrouhlením jsem v bezpečí a nemusím řešit, jestli je skutečně 0.25 * 4 == 1.
Pokud ta vec se kterou delate je opravdu velky cislo, pak proti `BigInteger`u nic nemam. Ja mel pocit, ze neni ale asi je.
Velmi velké. U crypto aplikací běžně 8192 bitů, ale hodnoty se využívají např. i pro bufferování vstupu. Příkladmo budu dávat do examples/ gzip soubor, a tam se musí spočítat CRC nekomprimovaného souboru. Nebo je něco podobného už v příkladu s hlavičkou IP paketu, kde je checksum uprostřed dat. Tedy si zbytek nejdřív uložím do BigInteger proměnné, pak si sestavím stack s input streamem o šiřce bufferu a buffer poštu do InStreamu. S Bitwriterem běžné techniky.
Mou zásadou je, že uživatel vůbec nesmí spatřit slovo "Exception". Programátor ho musí informovat o chybě pokud možno na jediném řádku a vyčerpávajícím způsobem.
Kdybych chtěl podporovat Rhino, zřejmě by stačilo v ScriptProcessor.java:344 otestovat versi a poslat buď starý, nebo nový string. Domnívám se, že vše ostatní je stejné.
Eclipse umí přečíst a intrerpretovat build.xml, je-li správně napsán (můj přirozeně je): mně chodila na první spuštění.
Na to staci odchyceni na urcitych mistech. Kolik je puvodnich zdroju problemu je pri tom jedno.
> Nehledě na to, že uživateli je takový text výjimky většinou naprd.
Tak mu dejte cist neco jinyho a stack trace napiste do logu. Support vam podekuje.
> na konec to probublá jen v krajním případě.
No jiste. Ale pan Pecina vyjimky chyta snad bez vyjimky hned jak nastanou.
> nemohou z threadů jen tak mýrnix týrnix probublávat náhodně výjimky
To ani nejde (vyjimka muze maximalne kilnout svuj thread). Ja nikdy nerikam ze je mate ignorovat uplne, jen ze probublani/prebaleni je skoro vzdy spravne, zatimco chytani jen tam kde se s tim da opravdu neco delat.
Priklad: Pokud nejde precist soubor ktery si uzivatel vybral, pak ho necham vybrat znova. To muze znamenat probublani IOException pres 20 metod a pak jedno odchyceni nekde okolo `SwingWorker#get()`. Namisto jednoho dvou probublani muze byt prebaleni.
S tim ja spise i souhlasim, ale uzivatel ma jen velmi omezeny pristup, a sice pres main. To znamena, ze tam musite odchytit vsechno, jinde jen pokud chcete nejakou informaci pridat nebo zmenit typ vyjimky.
Namisto `ProcessorException(String message)` muzete vsude pouzivat `ProcessorException(String message, Exception cause)` bez toho ze by to cemukoliv vadilo. Naopak, mate vic informaci, uzivatel to cist nemusi. Jeste jsem nevidel ze by to nekdo rozumny nepouzival.
> Eclipse umí přečíst a intrerpretovat build.xml
To ja nezkoumal, proste jsem udelal projekt abych se podival na kod. Vubec jsem neplanoval ho spoustet.
Váš příklad je úplně netypický, protože se to jednak stalo v rámci testovacího kodu, který není vypiplaný z povahy věci, jednak proto, že jste aplikaci pusti v nekompatibilní versi JRE.
Chtěl jsem jen naznačit, že v Javě nejsou výjimky zkrátka dořešené. A jakmile začnete programovat multithreadově, musáte si vybrat jiný mechanismus přenosu chybových informací.
V seriózních jazycích, jako je třeba Ada, která je určena pro programování pro opravdové praktické případy, je přenos výjimky mezi thready možný pomocí Exception_Occurence.
Ostatně si docela dobře dokáži představit i přehození výjimky do jiného threadu v Javě, a není to ani moc pracné.
---
„Priklad: Pokud nejde precist soubor ktery si uzivatel vybral, pak ho necham vybrat znova. To muze znamenat probublani IOException pres 20 metod a pak jedno odchyceni nekde okolo `SwingWorker#get()`. Namisto jednoho dvou probublani muze byt prebaleni.“
Ano, ale vy musíte znát vnitřek těch 20 vrstev. Pokud těch 20 vrstev pod vámi hází z 50 možných míst IOException, a jen jedno místo znamená, že nejde přečíst soubor, se velmi snadno stane, že nabízíte uživateli něco jako reakci na jinou chybu, která by chtěla jiné řešení.
Takže nakonec stejně dojdete k tomu, pokud chcete mít program dobře, že chytáte výjimky co nejdříve. Na 20 vrstev zapomenete, tak daleko to bublat nenecháte. Chytíte výjimku co nejdříve a ošetříte – buď nějakou akcí a nebo přebalením výjimky na jiný typ, který máte pod kontrolou.
Zkrátka pokud nevíte, co máte pod těmi vrstvami, přes které bublají výjimky, nemůžete si být jist u výjimky IOException ničím jiným, než že pravděpodobně došlo k I/O chybě (pokud programátor vrstev dodržoval alespoň tuto štábní kulturu), a ničím jiným. Čím větší šíře místa možných výjimek, tím nesmyslnější je předpoklad, že výjimka znamená to, co si myslíte.
Miloslav Ponkrác
Na to je grep kratky. Pracuje radkove, takze dostanu
src/cz/pecina/bin/bitwriter/CrcElement.java:105: throw new ProcessorException(
a pokracovani je na dalsim radku. Ale verim vam. Tez vidim ze vas konstruktor to pozaduje.
---
> Tedy exception chaining používám
Podle bezne definice "The original exception is saved as a property (such as cause) of the new exception" ne.
---
> Váš příklad je úplně netypický
Ano, je. Ja nerikam ze vas program je chybny ani nic takovyho. Tvrdim ze vyhybanim se standardnimu postupu si sam ztezujete praci. A to zbytecne, protoze exception chaining stoji jen pridani ", e" do re-throw.
Me Ada pripada spis dost neseriozni. Takova ukecana a nic z toho, viz Ariane 5.
> Ostatně si docela dobře dokáži představit i přehození výjimky do jiného threadu v Javě, a není to ani moc pracné.
No jiste, treba jako `ExecutionException` ve `Future#get`. Ja jen ze se to nestane uplne automaticky, takze se toho netreba bat. Automatika funguje v tom smyslu, ze kdyz chcete znat vysledek `ExecutorService#submit`, pak dostanete bud vysledek nebo vyjimku.
-A, -B.
Mně zase přijde téměř jakýkoli programovací jazyk serióznější, než Java. Java je ukecaná a nic z toho. Ada je ukecaná, ale mýá z toho informace a optimalizace a kontroly.
Ada například neřeší signed a unsigned. Proč? Chci byte? type Byte is mod 256;
Chci typ pro školní známky: Promenna: integer range 1..5;
Chci mít unsigned číslo co to jde: Pormenna: integer range 0..Integer'last;
Přijde mi to i z hlediska typů jako dokonalé řešení. A zároveň kompilátor i runtime konstroluje vše, co jste o typech napsali včetně rozsahu. Žádné signed/unsigned není potřeba.
Java nemá ani tu pitomou konstrukci na definici typu. I to hloupé C má typedef. Prostě Java je hloupý jazyk, kterému chybějí zcela základní elementární koinstrukce, které programátor potřebuje.
---
Ohledně výjimek jsem chtěl jen říci, že je to nástroj pro předávání informací o výjimečných situacích a má své limity. Souhlasím s panem Pecinou v tom, že uživatel by o žádné výjimce vědět neměl. Program by měl správně vyřešit a obsloužit všechny chyby, ze kterých se může zotavit, a případná chybová hlášení by neměla pocházet z výjimek.
Miloslav Ponkrác
Diky!
> Bylo by to složité, ale cílem je šťastný programátor a fungující program, ne méně práce pro autora kompilátoru.
Ano i ne. Zrovnopravneni primitivnich typu stoji c1 casu a udela prumerneho programatora o x1 stastnejsim. Jina ficurka stoji c2 s effektem x2. Kdyz plati x1/c1 < x2/c2, tak maji primitivni typy (zatim) smulu.
Navic hodnota x1 je neznama. Musite se rozhodnout jak to presne udelat. Ty problemy se nejak musi vyresit. Programator ktery jednou zazije ze `ComplexDouble tmp = a[0]; tmp.real = 2;` neni totez co `a[0].real = 2;` radsi zmeni jazyk. Takze value type musi byt immutable. To je jednoduche rozhodnuti.
Co ale s `Object[] oa = new Object[1]; oa[0] = cd;`? Prece nemuzete namisto 4 ci 8 bajtu pro odkaz alokovat tolik aby se tam vlezlo cokoliv. Pokud alokujete vic, tak vyrazne zpomalite vsechny existujici programy a zvetsite jejich spotrebu pameti. Pokud alokujete stejne jak doted, pak musite tajne pouzivat reference, takze jsme zhruba tam kde ted (autoboxing). Nejslozitejsi na tom je interakce s existujicimi a budoucimi ficurkami. Na
cr.openjdk.java.net/~jrose/values/values-0.html
hledejte "Subtyping".
Základem byla chyba, že Java vůbec nějaké primitivní typy měla. To neměla být starost programátora, že typy mají různé vlastnosti uvnitř runtime, a programátor od toho má být odstíněn.
V javě nefunguje žádné x1/c1 < x2/c2, v Javě funguje náboženství „jak to dělat správně“. Tedy Java funguje podle rozhodnutí imámů a kněží Javy, žádná racionalita v tom není a nikdy nebyla.
---
„Pokud alokujete vic, tak vyrazne zpomalite vsechny existujici programy a zvetsite jejich spotrebu pameti. Pokud alokujete stejne jak doted, pak musite tajne pouzivat reference, takze jsme zhruba tam kde ted (autoboxing). Nejslozitejsi na tom je interakce s existujicimi a budoucimi ficurkami.“
Java se opravdu velmi trápí s množstvím zabrané paměti. Nad tím vždycky návrháři Javy přemýšleli a udělali Javu velmi paměťově úspornou (konec sarkasmu).
Tak nedomyšlený jazyk jako je Java aby jeden pohledal. Není vůbec jasné, kdo ho vlastní, standardizuje a má na něho práva (Oracle?). Multiplatformní Java není, má až příliš mnoho neplatformního. Knihovny se neustále mění, co bylo před chvílí doporučováno, je najednou deprecated. Kromě toho je Jav několik, ačkoli se budí dojem, že je to jedna platforma.
Paměťová náročnost Javy se odvíjí především od toho, že si špatně vymyslela garbage collector, a nemá-li výkon Javy jít do kopru, musí jej spouštět co nejméně. Takže Java klidně zabírá enormně velkou paměť, kterou nepotřebuje a nepoužívá, ale šetří na spouštění garbage collectoru. Garbage collector totiž vyžaduje zastavení všech threadů a překopání všech referencí.
Tak, jak Václav Havel udělal pravdu a lásku a pokřivil všechno rozumné do pokrytectví a nepřirozenosti, mám dojem, že totéž udělala Java v sw. V Javě člověk musí myslet jako vykastrovaný s odřezanou většinou mozku, znásilnit všechny dobré zásady v programování i IT. Java je pravdoláska programovacích jazyků.
A stejně jako Havel a pravdoláskaři se oslavují a nesnesou kritiku, tak je to i s komuitou kolem Javy (horší už jsou jen rubisti). Dělají aureolu kolem Javy, která se dá shrnout do jedné věty: „Cokoli dělá Java je správné a etalonem správnosti“ – stejně jako pravdoláskaři.
Ten pocit, že když člověk dělá v Javě, nutí ho to myslet nepřirozeně – ten mám stejný jako pan Pecina. Stejně tak jsem zjistil, že kdo si za svůj první programovací jazyk zvolí Javu, o několik let si prodlouží naučení programování. Neznám žádného dobrého programátora, který by začal s Javou. To je jako učit se být moudrým ve světě podle spisů a řečí pravdoláskařů. Pokroutí vám to svět a dlouho hledáte cestu ven.
Kompilátor a gramatika Javy je jednoduchá, protože jazyk je hloupý a neschopný. Kdyby Sub/Oracle chtěli, dávno už všechny typy jsou navenek pouze objektové a nebyl by problém.
Howgh
Miloslav Ponkrác
"Dodnes jsem nepochopil, proč integer a character a boolean nejsou objekty."
Zkuste si spustit tento kod
long t = System.currentTimeMillis();
int k = 0;
for (int i = 0; i < 10000000; i++) {
k = k + i;
}
t = System.currentTimeMillis() - t;
System.out.println(t);
t = System.currentTimeMillis();
Integer ko = 0;
for (Integer io = 0; io < 10000000; io++) {
ko = ko + io;
}
t = System.currentTimeMillis() - t;
System.out.println(t);
a pochopite to; ja dostavam cca 10x lepsi cas s intem nez s Integerem.
Napr. u Matlabu, jehoz jadro bylo alespon sveho casu postaveno na Jave a jehoz primarnim
ucelem je prace s maticemi je tento rozdil dost zasadni. A ve velikosti pameti bude take rozdil.
Dale k StringBuffer a StringBuilder; pokud je mi znamo, rozdil je pouze v tom, ze jedna trida je threadsafe
(tj. vse synchronized), druha nikoliv, v cem je problem?
"aniž by existoval jediný rozumný důvod, proč by tento typ neměl být jen jeden
a příslušné zacházení s ním volil podle kontextu překladač; když napíšu,
že chci ArrayList, je to chyba,
přičemž jejím jediným faktickým důvodem je lenost programátorů"
proc je vyhodne mit k dispozici int a Integer je myslim jasne (rychlost/pamet);
kolekce vyzaduji objektove typy mimo jine i proto,
ze volaji metody instanci (napr. equals pri volani metody contains a == a equals neni to same).
"Tak od začátku: Celé objektové programování je především syntaktický cukr sám o sobě. Ve skutečnosti celé objekty vytváří jako iluzi kompilátor, to platí jak pro Javu, tak pro každý OOP jazyk."
- plati napr. pro C, u Javy bych toto netvrdil. Pokud je objekt jen iluze kompilatoru, jak je mozne, ze muzete java program dekompilovat a dostanete s vyjimkami ("konstanty" apod.) puvodni zdrojovy kod? Dale muzete za behu zkoumat strukturu objektu neznameho v dobe kompilace atd.
"Bohužel se Java nezamyslela nad dědičností, a dala dědičnosti hned 2 významy: 1) typová kompatibilita různých tříd mezi sebou (potomek může být použit na typu předka), 2) dědění a znovupoužití kódu (potomek dědí data, kód a metody předka). Takže nakonec situaci zalepili interfacy a zákazem multiple inheritance."
Mě připadá, že se naopak zamysleli a zavedení rozhraní jako oddělení API a implementace bylo velmi dobrý nápad.
Je na tom postaveno dost návrhových vzorů,(dále např.v Java Server Faces máte podobný přístup u composite components).
protože subclassovat BigInteger nestačí a zkopírovat (resp. obalit wrapperem) všechny methody je nesmyslně pracné.
-vytvoreni wrapperu cehokoliv je napr. v Eclipse otazka par sekund (Generate Delegate Methods), pripadne muzete pouzit proxy objekt.
No jistě, ale to by měl být problém kompilátoru a runtimu, ne programátora.
(tj. vse synchronized), druha nikoliv, v cem je problem?
Tak pro začátek v tom, že je to nelogicky pojmenováno. Kdo si má ty jejich šílené názvy pamatovat? StringBuilder a SynchronizedStringBuilder by úplně stačilo.
kolekce vyzaduji objektove typy mimo jine i proto,
ze volaji metody instanci (napr. equals pri volani metody contains a == a equals neni to same).
Opět, to by neměl být problém programátora.
vytvoreni wrapperu cehokoliv je napr. v Eclipse otazka par sekund (Generate Delegate Methods),
A v další versi Javy budu wrappery generovat znovu, pokud nějakou methodu přidají. To není cesta.
pripadne muzete pouzit proxy objekt.
Tím bych vzal uživateli pohodlí psát ve skriptovém kodu přímo operace s mými typy. Na té aplikaci je vlastně jen jedna chytrá věc, a to právě tato možnost, tzn. operovat s čísly pod stejným identifikátorem buď nativně, jsou-li dostatečně malá, nebo jako s BigIntegery. Můžete do skriptu napsat "a = b +c" nebo "a = b.add(c)" a obojí bude fungovat (pro velká čísla dá přirozeně jen druhá varianta správný výsledek).
RSS kanál komentářů k tomuto článku