Devoxx 2017

I was lucky to get a chance to visit Devoxx in Antwerp the sixth time in a row. As always there were interesting talks to listen to. Some issues that were visible across different talks:

Java 9 is now out and Java 8 will soon go into the first steps of deprecation. The step of moving to Java 9 is probably the hardest in the history of Java. There were features in the past that brought very significant changes, but they were usually kind of optional, so adoption could be avoided or delayed. Java 9 brings the module system and a new level of abstraction in that classes of modules can be made public to other modules selectively or globally. Otherwise they can be by themselves declared as public, but only be visible within the module. This actually applies to classes of the standard library, that were always declared as being private, but that could not be efficiently hidden away from external usage. Now they suddenly do not work any more and a lot of software has some difficulty and needs to be adjusted to avoid these internal classes. Beyond that a lot of talks were about Java 9, for example also covering somewhat convenient methods for writing constant collections in code. Future releases will follow a path that is somewhat similar to that of Perl 5. Releases will be created roughly every half year and will include whatever is ready for inclusion at that time. Some releases will be supported for a longer time than others.

In the arena of non-Java JVM-languages the big winner seems to be Kotlin, while Groovy, Clojure, JRuby and Ceylon where not visible at the conference. Scala has retained its position as an important JVM language besides Java at this conference. The rise of Kotlin may be explained by the fact that Idea (IntelliJ) has become much more important as IDE than Eclipse and Netbeans, which already brings Kotlin onto every JVM-language-developer’s desktop. And Google has moved from Eclipse to Idea as recommended and supported IDE for Android-development and is now officially supporting Kotlin besides Java as language for Android-development. There were heroic efforts to do development in Scala, Clojure, Groovy for Android without support from Google, which is quite possible, but having to deploy the libraries with each app instead of having them already on the phone is a big disadvantage. The second largest mobile OS has added support for Swift as an alternative to Objective C and Swift and Kotlin are different languages, but they are sufficiently similar in terms of concepts and possibilities to ease development of Apps targeting the two most important mobile system platforms in mixed teams at least a bit. And Kotlin gives developers many of the cool and interesting features of Scala, while remaining a bit easier to learn and to understand, because some of the most difficult parts of Scala are left out. Anyway, Scala is not yet heavily challenged by Kotlin and remains important and I think that Clojure and JRuby and Groovy retain their importance and live in somewhat differenct niches than Scala and Kotlin. I would think that they are just a bit too small to be present on each Devoxx. Or it was just random effects about how much news there was about the languages and what kind of speeches had been proposed for them. On the other hand, I would assume that Ceylon has become a dead end, because it came out at the same time as Kotlin and tries to cover the same niche. It is hard to stay important in the same niche with a strong winner.

Then there was of course security security security… Even more important than in the past.

And a lot more…

I listened to the following talks:
Wednesday, 2017-11-08

Thursday, 2017-11-09

Friday, 2017-11-10

Links:

Previous years:

Btw. I always came to Devoxx by bicycle, at least part of the way…

Share Button

UTF-16 Strings in Java

Deutsch

Strings in Java and many other JVM-languages consist of Unicode content and are encoded as utf-16. It was fantastic to already consider Unicode when introducing Java in the 90es and to make it the only reasonable way to use strings, so there is no temptation to start with a „US-ASCII“-version of a software that never really gets enhanced to deal properly with non-English content and data. And it also avoids having to deal with many kinds of String encodings within the software. For outside storage in databases and files and for communication with other processes and over the network, these issues of course remain. But Java strings are always encoded utf-16. We can be sure. Most common languages can make use of these Strings easily and handling common letter based languages from Europe and western Asia is quite strait forward. Rendering may still be a challenge, but that is another issue. A major drawback of this approach is that more memory is used. This usually does not hurt too much. Memory is not so expensive and a couple of Strings even with utf-16 will not be too big. With 64-bit Java, which should be used these days, the memory limitations of the JVM are not relevant any more, they can basically use as much memory as provided.

But some applications to hit the memory limits. And since usually most of the data we are dealing with is ultimately strings and combinations of relatively long strings with some reference pointers, some integer numbers and more strings, we can say that in typical memory intensive applications strings actually consume a large fraction of the memory. This leads to the temptation of using or even writing a string library that uses utf-8 or some other more condensed internal format, while still being able to express any Unicode content. This is possible and I have done it. Unfortunately it is very painful, because Strings are quite deeply integrated into the language and explicit conversions need to be added in many places to deal with this. But it is possible and can save a lot of memory. In my case we were able to abandon this approach, because other optimizations, that were less painful, proved to be sufficient.

An interesting idea is to compress strings. If they are long enough, algorithms like gzip work on a single string. As with utf-8, selectively accessing parts of the string becomes expensive, because it can only be achieved by parsing the string from the beginning or by adding indexing structures. We just do not know which byte to go to for accessing the n-th character, even with utf-8. In reality we often do not have long strings, but rather many relatively short strings. They do not compress well by themselves. If we know our data and have a rough idea about the content of our complete set of strings, custom compression algorithm can be generated. This allows good results even for relatively short strings, as long as they are within the „language“ that we are prepared for. This is more or less similar to the step from utf-16 to utf-8, because we replace common byte sequences by shorter byte sequences and less common sequences may even get replaced by something longer. There is no gain in utf-8, if we have mostly strings that are in non-Latin alphabets. Even Cyrillic or Greek, that are alphabets similar to the Latin alphabet, will end up needing two bytes for each letter, which is not at all better than utf-16. For other alphabets it will even become worse, because three or four bytes are needed for one symbol that could easily be expressed with two bytes in utf-16. But if we know our data well enough, the approach with the specific compression will work fine. The „dictionary“ for the compression needs to be stored only once, maybe hard-coded in the software, and not in each string. It might be of interest to consider building the dictionary dynamically at run-time, like it is done with gzip, but keeping it in a common place for all strings and thus sharing it. The custom strings that I used where actually using a hard coded compression algorithm generated using a large amount of typical data. It worked fine, but was just too clumsy to use because Java is not prepared to replace String with something else without really messing around in the standard run-time libraries, which I would neither recommend nor want.

It is important to consider the following issues:

  1. Is the memory consumption of the strings really a problem?
  2. Are there easier optimizations that solve the problem?
  3. Can it just be solved by adding more hardware? Yes, this is a legitimate question.
  4. Are there solutions for the problem in the internet or even within the current organization?
  5. A new String class is so fundamental that excellent testing is absolutely mandatory. The unit tests should be very extensive and complete.
Share Button

How to create ISO Date String

It is a more and more common task that we need to have a date or maybe date with time as String.

There are two reasonable ways to do this:
* We may want the date formatted in the users Locale, whatever that is.
* We want to use a generic date format, that is for a broader audience or for usage in data exchange formats, log files etc.

The first issue is interesting, because it is not always trivial to teach the software to get the right locale and to use it properly… The mechanisms are there and they are often used correctly, but more often this is just working fine for the locale that the software developers where asked to support.

So now the question is, how do we get the ISO-date of today in different environments.

Linux/Unix-Shell (bash, tcsh, …)

date "+%F"

TeX/LaTeX


\def\dayiso{\ifcase\day \or
01\or 02\or 03\or 04\or 05\or 06\or 07\or 08\or 09\or 10\or% 1..10
11\or 12\or 13\or 14\or 15\or 16\or 17\or 18\or 19\or 20\or% 11..20
21\or 22\or 23\or 24\or 25\or 26\or 27\or 28\or 29\or 30\or% 21..30
31\fi}
\def\monthiso{\ifcase\month \or
01\or 02\or 03\or 04\or 05\or 06\or 07\or 08\or 09\or 10\or 11\or 12\fi}
\def\dateiso{\def\today{\number\year-\monthiso-\dayiso}}
\def\todayiso{\number\year-\monthiso-\dayiso}

This can go into a file isodate.sty which can then be included by \include or \input Then using \todayiso in your TeX document will use the current date. To be more precise, it is the date when TeX or LaTeX is called to process the file. This is what I use for my paper letters.

LaTeX

(From Fritz Zaucker, see his comment below):

\usepackage{isodate} % load package
\isodate % switch to ISO format
\today % print date according to current format

Oracle


SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') FROM DUAL;

On Oracle Docs this function is documented.
It can be chosen as a default using ALTER SESSION for the whole session. Or in SQL-developer it can be configured. Then it is ok to just call

SELECT SYSDATE FROM DUAL;

Btw. Oracle allows to add numbers to dates. These are days. Use fractions of a day to add hours or minutes.

PostreSQL

(From Fritz Zaucker, see his comment):

select current_date;
—> 2016-01-08


select now();
—> 2016-01-08 14:37:55.701079+01

Emacs

In Emacs I like to have the current Date immediately:

(defun insert-current-date ()
"inserts the current date"
(interactive)
(insert
(let ((x (current-time-string)))
(concat (substring x 20 24)
"-"
(cdr (assoc (substring x 4 7)
cmode-month-alist))
"-"
(let ((y (substring x 8 9)))
(if (string= y " ") "0" y))
(substring x 9 10)))))
(global-set-key [S-f5] 'insert-current-date)

Pressing Shift-F5 will put the current date into the cursor position, mostly as if it had been typed.

Emacs (better Variant)

(From Thomas, see his comment below):

(defun insert-current-date ()
"Insert current date."
(interactive)
(insert (format-time-string "%Y-%m-%d")))

Perl

In the Perl programming language we can use a command line call

perl -e 'use POSIX qw/strftime/;print strftime("%F", localtime()), "\n"'

or to use it in larger programms

use POSIX qw/strftime/;
my $isodate_of_today = strftime("%F", localtime());

I am not sure, if this works on MS-Windows as well, but Linux-, Unix- and MacOS-X-users should see this working.

If someone has tried it on Windows, I will be interested to hear about it…
Maybe I will try it out myself…

Perl 5 (second suggestion)

(From Fritz Zaucker, see his comment below):

perl -e 'use DateTime; use 5.10.0; say DateTime->now->strftime(„%F“);‘

Perl 6

(From Fritz Zaucker, see his comment below):

say Date.today;

or

Date.today.say;

Ruby

This is even more elegant than Perl:

ruby -e 'puts Time.new.strftime("%F")'

will do it on the command line.
Or if you like to use it in your Ruby program, just use

d = Time.new
s = d.strftime("%F")

Btw. like in Oracle SQL it is possible add numbers to this. In case of Ruby, you are adding seconds.

It is slightly confusing that Ruby has two different types, Date and Time. Not quite as confusing as Java, but still…
Time is ok for this purpose.

C on Linux / Posix / Unix


#include
#include
#include

main(int argc, char **argv) {

char s[12];
time_t seconds_since_1970 = time(NULL);
struct tm local;
struct tm gmt;
localtime_r(&seconds_since_1970, &local);
gmtime_r(&seconds_since_1970, &gmt);
size_t l1 = strftime(s, 11, "%Y-%m-%d", &local);
printf("local:\t%s\n", s);
size_t l2 = strftime(s, 11, "%Y-%m-%d", &gmt);
printf("gmt:\t%s\n", s);
exit(0);
}

This speeks for itself..
But if you like to know: time() gets the seconds since 1970 as some kind of integer.
localtime_r or gmtime_r convert it into a structur, that has seconds, minutes etc as separate fields.
stftime formats it. Depending on your C it is also possible to use %F.

Scala


import java.util.Date
import java.text.SimpleDateFormat
...
val s : String = new SimpleDateFormat("YYYY-MM-dd").format(new Date())

This uses the ugly Java-7-libraries. We want to go to Java 8 or use Joda time and a wrapper for Scala.

Java 7


import java.util.Date
import java.text.SimpleDateFormat

...
String s = new SimpleDateFormat("YYYY-MM-dd").format(new Date());

Please observe that SimpleDateFormat is not thread safe. So do one of the following:
* initialize it each time with new
* make sure you run only single threaded, forever
* use EJB and have the format as instance variable in a stateless session bean
* protect it with synchronized
* protect it with locks
* make it a thread local variable

In Java 8 or Java 7 with Joda time this is better. And the toString()-method should have ISO8601 as default, but off course including the time part.

Summary

This is quite easy to achieve in many environments.
I could provide more, but maybe I leave this to you in the comments section.
What could be interesting:
* better ways for the ones that I have provided
* other databases
* other editors (vim, sublime, eclipse, idea,…)
* Office packages (Libreoffice and MS-Office)
* C#
* F#
* Clojure
* C on MS-Windows
* Perl and Ruby on MS-Windows
* Java 8
* Scala using better libraries than the Java-7-library for this
* Java using better libraries than the Java-7-library for this
* C++
* PHP
* Python
* Cobol
* JavaScript
* …
If you provide a reasonable solution I will make it part of the article with a reference…
See also Date Formats

Share Button

Conversion of ASCII-graphics to PNG or JPG

Images are usually some obscure binary files. Their most common formats, PNG, SVG, JPEG and GIF are well documented and supported by many software tools. Libraries and APIs exist for accessing these formats, but also a phantastic free interactive software like Gimp. The compression rate that can reasonably be achieved when using these format is awesome, especially when picking the right format and the right settings. Tons of good examples can be found how to manipulate these image formats in C, Java, Scala, F#, Ruby, Perl or any other popular language, often by using language bindings for Image Magick.

There is another approach worth exploring. You can use a tool called convert to just convert an image from PNG, JPG or GIF to XPM. The other direction is also possible. Now XPM is a text format, which basically represents the image in ASCII graphics. It is by the way also valid C-code, so it can be included directly in C programms and used from there, when an image needs to be hard coded into a program. It is not generally recommended to use this format, because it is terribly inefficient because it uses no compression at all, but as intermediate format for exploring additional ways for manipulating images it is of interest.
An interesting option is to create the XPM-file using ERB in Ruby and then converting it to PNG or JPG.

Share Button

Logging

Software enthält häufig eine Log-Funktionalität. Üblicherweise werden dort ein- oder mehrzeilige Einträge in eine Datei, nach syslog oder in die Standardausgabe geschrieben (und letzlich in eine Datei umgeleitet), die etwas darüber sagen, was die Software so macht. Normalerweise kann man das alles ignorieren, aber sobald dort etwas mit „ERROR“ auftritt oder schlimmer noch sogenannte Stacktraces, ist eigentlich angesagt, dass man dem Problem auf den Grund geht. Da nun leider Software häufig von minderer Qualität ist, was durchaus von den Unzulänglichkeiten der verwendeten Libraries und Frameworks kommen kann, sieht man das leider recht oft, teilweise so oft, dass man denen nicht mehr wirklich nachgeht. Ärgerlich ist vor allem, dass der eigentliche Fehler oft vorher an einer ganz anderen Stelle aufgetreten ist, sich aber in der Log-Datei nicht nachvollziehen lässt.

Nun ist es schön, dass die Log-Dateien einen gewissen Aufbau der Einträge einhalten. Meist beginnen sie mit einer Zeitangabe im ISO-Format, oft auf die Millisekunde genau. Da in der Regel mehrere Prozesse oder mehrere Threads gleichzeitig laufen, werden deren Einträge natürlich mehr oder weniger wild gemischt. Das ist gut erträglich, wenn auch mehrzeilige Log-Einträge (z.B. Stack-Traces) zusammen bleiben und wenn sich Anfang und Ende von mehrzeiligen Log-Einträgen gut erkennen lassen. Man kann dann mit Splunk oder mit Perl- oder Ruby-Scripten die Log-Dateien analysieren und jeweils für einzelne Threads die Abläufe nachvollziehen. Das Zusammenhalten mehrzeiliger Einträge lässt sich realisieren, indem man ein atomares write verwendet oder indem man einen „Logger-Thread“-hat und alle Log-Einträge diesem Thread in einer Queue zur Verfügung stellt, die dieser dann abarbeitet und herausschreibt.

Insgesamt ist das ganze Thema aber unübersichtlich und unhandhabbar geworden. In der Java-Welt hatte man früher log4j und eine relativ einfache Konfigurations-Datei im properties-Format. Das wurde dann irgendwann durch XML ersetzt und es kamen andere Logging-Frameworks dazu und jeweils immer wieder etwas, was alle diese vereinheitlichte. Letztlich wurde es dadurch komplizierter und unübersichtlicher.

Es stellt sich die Frage, wie viel von der Logik für die Handhabung der Log-Dateien in die Software selbst gehört. Muss die Software wissen, in welche Datei geloggt wird? Muss sie Log-Rotation kennen? Muss es sein, dass eine Software überhaupt nicht startet, nur weil man es nicht schafft, die komplizierte Log-Konfiguration richtig einzustellen? Letztlich müssen die Log-Dateien am Ende den Systemadministrator zufriedenstellen, der die Software auf dem Produktivsystem betreibt. Soll man diesem zumuten, für jede Software eine spezielle Konfiguration des Logging zu pflegen? Oder für diesen Zweck jeweils von den Entwickerln eine neue Software-Version anzufordern, weil die Konfiguration als Teil des Deployments „hardcodiert“ ist? Interessant wird es auch dann, wenn man auf Technologien wie „Platform as a Service“ (PAAS) setzt, wo man Applikationsserver, Framework, Datenbank u.s.w. zur Verfügung hat, aber wo die Software ohne weiteres den Server wechseln kann und damit Dateien verloren gehen.

Ist es vielleicht einfach die bessere Lösung, unter Einhaltung eines vernünftigen Formats nach stdout zu loggen die Software so laufen zu lassen, dass deren Standardausgabe (stdout oder stderr) in einen logmanager geleitet wird? Dieser kann dann für alle Software, die auf dem System läuft, verwendet werden und vom Systemadministrator einheitlich konfiguriert werden. Einheitlich heißt nicht nur, für alle Java-Programme gleich, sondern für alle Software, die sich dazu bringen lässt, ihre Log-Ausgabe nach stdout zu schreiben und gewisse Mindestanforderungen an das Format einzuhalten. Im Prinzip ließe sich mit named-Pipes auch eine beliebig hard-codierte Log-Datei unterstützen, aber das macht die Dinge nur komplizierter. Wenn dann das Logging-Framework der Software selbst noch Log-Rotation unterstützt, kann das natürlich durcheinandergeraten, wenn etwa nach n geschriebenen Bytes oder m Sekunden die named-pipe geschlossen, umbenannt und eine neue Datei angelegt wird, was je nach Schreibrechten in dem Verzeichnis zu verschiedenen Fehler führt.

Was ist jetzt mit Software, die selbst als Filter fungiert, wo also stdout Teil der Funktionalität ist? Solche Software findet man üblicherweise in Form kleinerer Programme oder Skripte, die nicht unbedingt Log-Dateien schreiben müssen oder in Form von gut ausgetesteter Software, die Teil der Installation ist. Oft kann man hier mit stderr für Log-Ausgaben, in diesem Fall meist für Fehlermeldungen, auskommen. Oder man könnte eine Log-Datei auf der Kommandozeile angeben, was dann auch wieder für den Systemadminstrator leicht zugänglich wäre und auf eine named-pipe umleiten könnte.

Share Button

Rounding of Money Amounts

Deutsch

Many numerical calculations deal with amounts of money. It is always nice if these calculations are somewhat correct or if we can at least rest assured that we do not loose money due to such inaccuracies. Off course there are calculations that may legitimately deal with approximations, for example when calculating profits as percentages of the investment (return on investment). For this kind of calculations floating point numbers (double, Float or something like that) come in handy, but off course dealing with the numeric subtleties can be quite a challenge and rounding errors can grow huge if not dealt with properly.

It is often necessary to do calculations with exact amounts. It is quite easy to write something like 3.23 as floating point number, but unfortunately these are internally expressed in binary format (base 2), so the fraction with a power of 10 in the denominator needs to be expressed as a fraction with a power of two in the denominator. We can just give it a try and divide x=323_{10}=101000011_{2} by y=100_{10}=1100100_2, doing the math in base 2 arithmetic. We get something like z=x/y=11.0011101011100001010001111010111000010100011110101\ldots_{2}, more precisely z=11.00\overline{11101011100001010001} or as fraction z=11_2+\frac{11101011100001010001_2}{100_2*(100000000000000000000_2-1_2)} or in base 10 z=3_{10}+\frac{964689_{10}}{4_{10}\cdot1048575_{10}}.

It can be seen that such a harmless number with only two digits after the decimal point ends up having an infinite number of digits after the decimal point. Our usual Double and Float types are usually limited to 64 Bits, so some digits have to be discarded, causing a rounding error. Fortunately this usually works well and the stored number is still shown as 3.23. But working a little bit with these floating point numbers sooner or later results like 3.299999999999 or 3.2300000001 will appear instead of 3.23, even when doing only addition and subtraction. When doing larger sums it might even end up as 3.22 or 3.24. For many applications that is unacceptable.

A simple and often useful solution is to use integral numbers and storing the amounts in cents instead of dollars. Then the rounding problem of pure additions and subtractions is under control and for other operations it might at least become easier to deal with the issue. A common mistake that absolutely needs to be avoided is mixing amountInDollar and amountInCent. In Scala it would be a good idea to use different types to make this distinction, so that such errors can be avoided at compile time. In any case it is very important to avoid such numeric types as int of Java that have a weird and hardly controllable overflow behavior where adding positive numbers can end up negative. How absurd, modular arithmetic is for sure not what we want for our money, it is a little bit too socialistic. 😉 Estimations about the upper bound of money amounts of persons and companies and other organizations are problematic, because there can be inflation and there can be some accumulation of money in somebody’s accounts… Even though databases tend to force us to such assumption, but we can make them really huge. So some languages might end up using something like BigInteger or BigNum or BigInt. Unfortunately Java shows one of its insufficiencies here, which makes quite ugly to use for financial applications, because calculations like a = b + c * d for BigInteger appear like this: a = b.{\rm add}(c.{\rm multiply}(d)). The disadvantage is that the formula cannot be seen at one glance, which leads to errors. In principal this problem can be solved using a preprocessor for Java. Maybe a library doing some kind of RPN-notation would possible, writing code like this:

Calculation calc = new Calculation();
calc.push(b)
calc.push(c)
calc.push(d)
calc.add()
calc.multiply()
a = calc.top()

Those who still know old HP calculators (like HP 25 and HP 67 😉 in the good old days) or Forth might like this, but for most of us this is not really cutting it.

Common and useful is actually the usage of some decimal fixed point type. In Java this is BigDecimal, in Ruby it is LongDecimal.
And example in Ruby:

> sudo gem install long-decimal
Successfully installed long-decimal-1.00.01
1 gem installed
....
> irb
irb(main):001:0> require "long-decimal"
=> true
irb(main):002:0> x = LongDecimal("3.23")
=> LongDecimal(323, 2)
irb(main):003:0> y = LongDecimal("7.68")
=> LongDecimal(768, 2)
irb(main):004:0> z = LongDecimal("3.9291")
=> LongDecimal(39291, 4)
irb(main):005:0> x+y
=> LongDecimal(1091, 2)
irb(main):006:0> (x+y).to_s
=> "10.91"
irb(main):007:0> x+y*z
=> LongDecimal(33405488, 6)
irb(main):008:0> (x+y*z).to_s
=> "33.405488"
irb(main):009:0> 

It is interesting to see that the number of digits remains the same under addition and subtraction if both sides have the same number of digits. But the longer number of digits wins otherwise. During multiplication, division and off course during more complex operations many decimal places can become necessary. It becomes important to do rounding and to do it right and in a controlled way. LongDecimal supports the following rounding modes:

ROUND_UP
Round away from 0.
ROUND_DOWN
Round towards 0.
ROUND_CEILING
Round to more positive, less negative numbers.
ROUND_FLOOR
Round to more negative, less positive numbers.
ROUND_HALF_UP
Round the middle and from above up (away from 0), everything below down (towards 0).
ROUND_HALF_DOWN
Round the middle and from above down (towards 0), everything below up (away from 0).
ROUND_HALF_CEILING
Round from the middle onward towards infinity, otherwise towards negative infinity.
ROUND_HALF_FLOOR
Round up to and including the middle towards negative infinity, otherwise towards infinity.
ROUND_HALF_EVEN
Round the middle in such a way that the last digit becomes even.
ROUND_HALF_ODD
Round the middle in such a way that the last digit becomes odd (will be added to long-decimal in next release).
ROUND_UNNECESSARY
Do not round, just discard trailing 0. If that does not work, raise an exception.

Which of these to use should be decided with domain knowledge in mind. The above example could be continued as follows:

irb(main):035:0> t=(x+y*z)
=> LongDecimal(33405488, 6)
irb(main):036:0> 
irb(main):037:0* t.round_to_scale(2, LongDecimal::ROUND_UP).to_s
=> "33.41"
irb(main):038:0> t.round_to_scale(2, LongDecimal::ROUND_DOWN).to_s
=> "33.40"
irb(main):039:0> t.round_to_scale(2, LongDecimal::ROUND_CEILING).to_s
=> "33.41"
irb(main):040:0> t.round_to_scale(2, LongDecimal::ROUND_FLOOR).to_s
=> "33.40"
irb(main):041:0> t.round_to_scale(2, LongDecimal::ROUND_HALF_UP).to_s
=> "33.41"
irb(main):042:0> t.round_to_scale(2, LongDecimal::ROUND_HALF_DOWN).to_s
=> "33.41"
irb(main):043:0> t.round_to_scale(2, LongDecimal::ROUND_HALF_CEILING).to_s
=> "33.41"
irb(main):044:0> t.round_to_scale(2, LongDecimal::ROUND_HALF_FLOOR).to_s
=> "33.41"
irb(main):045:0> t.round_to_scale(2, LongDecimal::ROUND_HALF_EVEN).to_s
=> "33.41"
irb(main):046:0> t.round_to_scale(2, LongDecimal::ROUND_UNNECESSARY).to_s
ArgumentError: mode ROUND_UNNECESSARY not applicable, remainder 5488 is not zero
        from /usr/local/lib/ruby/gems/1.9.1/gems/long-decimal-1.00.01/lib/long-decimal.rb:507:in `round_to_scale_helper'
        from /usr/local/lib/ruby/gems/1.9.1/gems/long-decimal-1.00.01/lib/long-decimal.rb:858:in `round_to_scale'
        from (irb):46
        from /usr/local/bin/irb:12:in `
' irb(main):047:0>

A specialty is that some countries do not use the coins with one of the smallest unit, like 1 cent in the US. In Switzerland the smallest common coin is 5 Rp (5 cent=0.05 CHF). It might be possible to make the bank do a transfer for amounts not ending in 0 or 5, but usually invoices apply this kind of rounding and avoid such exact amounts that could not possibly be paid in cash. This can be dealt with by multiplying the amount by 20, rounding it to 0 digits after the decimal point and divide it by 20 and round the result to 2 digits after the point. In Ruby there is a better way using an advanced feature of LongDecimal called remainder rounding (using the method round_to_allowed_remainders(…) ). Assuming we want to round a number x with n decimal places in such a way that, 10^n\cdot x belongs to one of the residue classes \overline{0} \mod 10 or \overline{5} \mod 10. In this case we are just talking about the last digit, but the mechanism has been implemented in a more general way allowing any set of allowed residues and even a base other than 10. If 0 is not in the set of allowed residues, it may be unclear how 0 should be rounded and this needs to be actually defined with a parameter. For common practical uses the last digit of 0 is allowed, so things work out of the box:


irb(main):003:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_UP).to_s
=> "33.45"
irb(main):005:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_DOWN).to_s
=> "33.40"
irb(main):006:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_CEILING).to_s
=> "33.45"
irb(main):007:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_FLOOR).to_s
=> "33.40"
irb(main):008:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_HALF_UP).to_s
=> "33.40"
irb(main):009:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_HALF_DOWN).to_s
=> "33.40"
irb(main):010:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_HALF_CEILING).to_s
=> "33.40"
irb(main):011:0> t.round_to_allowed_remainders(2, [0, 5], 10, LongDecimal::ROUND_HALF_FLOOR).to_s
=> "33.40"

In an finance application the rounding methods should probably be defined for each currency, possible using one formula and some currency specific parameters.

LongDecimal can do even more than that. It is possible to calculate logarithms, exponential functions, square root, cube root all to a desired number of decimal places. The algorithms have been tuned for speed without sacrificing precision.

Share Button

RISC und CISC

Vor 20 Jahren gab es einen starken Trend, Mikroprossoren mit RISC-Technologie (reduced instruction set computer), zu bauen. Jeder größere Hersteller wollte so etwas bauen, Sun mit Sparc, Silicon Graphics mit MIPS, HP mit PA-Risc, IBM schon sehr früh mit RS6000, was später, als man auf die Idee kam, das zu vermarkten, als PowerPC rebranded wurde, DEC mit Alpha u.s.w. Zu einer Zeit, als man zumindest in Gedanken noch für möglich hielt Assembler zu programmieren (auch wenn man es kaum noch tat), tat das noch richtig weh. Und Software war doch sehr CPU-abhängig, weil plattformunabhägige Sprache, die es damals selbstverständlich schon lange vor Java gab, einfach wegen des Overheads der Interpretation für viele Zwecke zu langsam waren. So behalf man sich mit C-Programmen mit wahren Orgien an ifdfefs und konnte die mit etwas Glück für die jeweilige Plattform aus CPU und einem UNIX-Derivat kompilieren. Der Wechsel der CPU-Architektur eines Herstellers war damals eine große Sache, z.B. bei Sun von Motorola 680×0 zu Sparc. Und die Assemblerprogrammierung der RISC-CPUs war ein Albtraum, an den sich auch erfahrene Assemblerprogrammierer kaum herangewagt haben. Zum Glück gab es damals schon sehr gut optimierende Compiler für C und Fortran und so konnte man das Thema einem ganz kleinen Personenkreis für die Entwicklung von kleinen Teilen des Betriebssytemkerns und kleinen hochperformanten Teilen großer Libraries überlassen.

Eigentlich sollte RISC ermöglichen, mit derselben Menge an Silizium mehr Rechenleistung zu erzielen, insgesamt also Geld und vielleicht sogar Strom zu sparen. Vor allem wollte man für die richtig coole Server-Applikation, die leider immer etwas zu ressourchenhungrig für reale Hardware war, endlich die richtige Maschine kaufen können. RISC war der richtige Weg, denn so hat man einen Optmierungsschritt beim Compiler, der alles optimal auf die Maschinensprache abbildet, die optimiert dafür ist, schnell zu laufen. Ich wüsste nicht, was daran falsch ist, wenn auch diese Theorie vorübergehend nicht zum Zuge kam. Intel konnte das Problem mit so viel Geld bewerfen, dass sie trotz der ungünstigeren CISC-Architektur immer noch schneller waren. Natürlich wurde dann intern RISC benutzt und das irgendwie transparent zur Laufzeit übersetzt, statt zur Compilezeit, wie es eigentlich besser wäre. Tatsache ist aber, dass die CISC-CPUs von Intel, AMD und Co. letztlich den RISC-CPUs überlegen waren und so haben sie sich weitgehend durchgesetzt.

Dabei hat sich die CPU-Abhängigkeit inzwischen stark abgemildert. Man braucht heute kaum noch Assembler. Die Plattformen haben sich zumindest auf OS-Ebene zwischen den Unix-Varianten angeglichen, so dass C-Programme leichter überall kompilierbar sind als vor 20 Jahren, mit cygwin sogar oft unter MS-Windows, wenn man darauf Wert legt. Applikationsentwicklung findet heute tatsächlich weitgehend mit Programmiersprachen wie Java, C#, Scala, F#, Ruby, Perl, Python u.ä. statt, die plattformunabhängig sind, mittels Mono sogar F# und C#. Und ein Wechsel der CPU-Architektur für eine Hardware-Hersteller ist heute keine große Sache mehr, wie man beim Wechsel eines Herstellers von PowerPC zur Intel-Architektur sehen konnte. Man kann sogar mit Linux dasselbe Betriebssystem auf einer unglaublichen Vielfalt von Hardware haben. Die große Mehrheit der schnellsten Supercomputer, die allermeisten neu vekrauften Smartphones und alle möglichen CPU-Architekturen laufen mit demselben Betriebssystemkern, kompiliert für die jeweilige Hardware. Sogar Microsoft scheint langsam fähig zu sein, verschiedene CPU-Architekturen gleichzeitig zu unterstützen, was lange Zeit außerhalb der Fähigkeiten dieser Firma zu liegen schien, wie man an NT für Alpha sehen konnte, was ohne große finanzielle Zuwendungen seitens DEC nicht aufrechterhalten werden konnte.

Aber nun, in einer Zeit, in der die CPU-Architektur eigentlich keine Rolle mehr spielen sollte, scheint alles auf Intel zu setzen, zum Glück nicht nur auf Intel, sondern auch auf einige konkurrierende Anbieter derselben Architektur wie z.B. AMD. Ein genauerer Blick zeigt aber, das RISC nicht tot ist, sonder klammheimlich als ARM-CPU seinen Siegeszug feiert. Mobiltelefone habe häufig ARM-CPUs, wobei das aus den oben genannten Gründen heute fast niemanden interessiert, weil die Apps ja darauf laufen. Tablet-Computer und Netbooks und Laptops sieht man auch vermehrt mit ARM-CPUs. Der Vorteil der RISC-Architektur manifestiert sich dort nicht in höherer Rechenleistung, sondern in niedrigerem Stromverbrauch.

Ist die Zeit reif und CISC wird in den nächsten zehn Jahren wirklich durch RISC verdrängt?

Oder bleibt RISC in der Nische der portablen stromsparendenen Geräte stark, während CISC auf Server und leistungsfähigen Arbeitsplatzrechnern dominierend bleibt? Wir werden es sehen. Ich denke, dass früher oder später der Vorteil der RISC-Architektur an Relevanz auf leistungsfähigen Servern und Arbeitsplatzrechnern gewinnen wird, weil die Möglichkeiten der Leistungssteigerung von CPUs durch mehr elektronischen Elementen pro Quadratmeter Chipfläche und pro Chip an physikalische Grenzen stoßen werden. Dann die bestmögliche Hardwarearchitektur mit guten Compilern zu kombinieren scheint ein vielversprechender Ansatz.

Die weniger technisch interessierten Nutzer wird diese Entwicklung aber kaum tangieren, denn wie Mobiltelefone mit Android werden Arbeitsplatzrechner mit welcher CPU-Architektur auch immer funktionieren und die Applikationen ausführen, die man dort installiert. Ob das nun plattformunabhängig ist, ob man bei kompilierten Programmen ein Binärformat hat, das mehrere CPUs unterstützt, indem einfach mehrere Varianten enthalten sind oder ob der Installer das richtige installiert, interessiert die meisten Benutzer nicht, solange es funktioniert.

Share Button

Einmal entwickeln – überall installieren

Das ist eine der großen Versprechungen von Java gewesen. Nun gab es drei oder bei genauerem Hinschauen etwas mehr als drei Varianten von Java, also „Micro-Edition“ (JavaME) für Toaster, Radios und Mobiltelefone, „Enterprise-Edition“ (JavaEE, JEE oder J2EE) für die ganz großen Server, die Sun auch gerne selber verkauft hat und „Standard-Edition“ (JavaSE) für „normale“ Aufgaben. Diese Aufteilung hat sich ein bisschen relativiert. Von JavaME hört man heute weniger, weil die Mobiltelefone heute leistungsfähig genug für JavaSE wären, wenn man es sich antun wollte, sogar für JavaEE. Die Programmierung von Toastern, Radios und Waschmaschinen ist ein Spezialgebiet, „embedded-Entwicklung“, mit dem man auch in der Informatik nur selten zu tun hat und diese Dinge nimmt man auch im Alltag kaum als Software wahr. JavaEE war in den ersten 10 Jahren einfach nur ein Rezept, um Programme langsamer, größer, komplizierter, störanfälliger und schlechter wartbar zu machen. Und man hatte plötzlich Abhängigkeiten vom Applikationsserver, die zwar idealerweise nicht den Programmtext selbst, wohl aber die Tonnen von XML-Konfiguration und die Deployment-Prozesse betraf. Da haben sich dann andere Frameworks entwickelt, zum Beispiel Spring, die etwas weniger schwerfällig daherkamen. Und für die Persistenz in JavaEE etablierte sich Hibernate und Eclipselink, was auch in JavaSE verwendbar ist. Nennt man nun eine Applikation, die mit Spring und Hibernate arbeitet, JavaEE oder JavaSE? Ich habe beides gehört. Jetzt sollte man bitte nicht versuchen, eine Applikation so zu entwickeln, dass sie wahlweise mit Spring und dem „Original-JavaEE“ läuft.

In den späten 90er-Jahren hatten Browser JavaSE eingebaut, damit man Applets schreiben konnte. So zeichnete es sich ab, dass jeder Rechner, den man kauft, eine gewisse Ausstattung bekommt, zum Beispiel einen Browser und eine Java-Installation. Dann hätten Java-Programme auf allen Desktop-Rechnern laufen können. Vielleicht müsste die Version hoch genug sein, weil Java sich natürlich weiterentwickelt hat und gelegentlich auch auf JavaVM-Ebene Erweiterungen nötig waren. Mit einem gute Software-Update-Mechanismus, wie wir ihn heute bei Linux-Distributionen oder bei den meisten Mobiltelefonen finden, hätte sich das lösen lassen. Dumm war nur, dass es eine Zeit gab, wo noch viele Anwender ein Modem mit 56k hatten und wo eine Java-Installation 80 MB groß war. Dann ist aber irgendwann, etwa nach dem Jahr 2000, die Java-Installation auf gängigen PCs selten geworden und man musste Endkunden bei der Installation von Java (wohlgemerkt 80 MB über das Modem) supporten, um ihre Java-Installation zum Laufen zu bringen. Das konnte sich nur in wenigen Fällen lohnen und man hat lieber auf Web-Applikationen gesetzt, was sich weitgehend durchgesetzt hat. Und die Browser können heute JavaScript, aber kein Java.

Bei den Mobiltelefonen gab es einige vielversprechende Anläufe, um Software-Entwicklung für eine Vielzahl von Geräten auf einmal zu ermöglichen. JavaME war eigentlich keine schlechte Idee, weil das auch auf recht kleinen Telefonen lief und auf den dicksten Smartphone könnte man es auch haben. Ein anderer Ansatz, der vielleicht ein bisschen weniger bekannt ist, ist die Verwendung des Qt-Frameworks für C++. Das ermöglicht auch auf einer Vielzahl von Geräten vom Arbeitsplatzrechner mit Linux oder MS-Windows bis zum Mobiltelefon „dieselbe“ Software laufen zu lassen, natürlich mit kleinen Anpassungen, um dem Benutzerinterface Rechnung zu tragen, und mit einer Neucompilierung für die Zielplattform. Diesen Weg hat Nokia verfolgt, als sie noch ernsthaft Mobiltelefone entwickelt haben und Qt-Applikationen liefen auf Symbian und Linux-Geräten (N900, N9), womit die Umstellung von Symbian auf Meego-Linux innerhalb kürzester Zeit eine reiche Auswahl an Apps für die neue Plattform ermöglicht hätte. Da Nokia sich aus der Mobiltelefonieentwicklung weitgehend und aus Meego und Symbian ganz zurückgezogen hat, bietet sich hier die Chance für neue Anbieter, wie zum Beispiel Jolla, mit dem Knowhow der ehemaligen Nokia-Mitarbeiter und deren vielversprechenden Ideen in den Markt einzusteigen und einen Teil der Rolle, die Nokia einmal hatte, zu übernehmen. Für native Apps auf dem Mobiltelefon ist aber aus heutiger Sicht die Dalvik-Engine von Android das mit Abstand zukunftsträchtigste Modell. Das ist letztlich eine weitere Java-„Edition“ und ermöglicht immerhin eine Lauffähigkeit auf dem überwiegenden Teil der neu verkauften Smartphones. Neue Linux-basierte Mobiltelefon-Betriebssysteme haben zum Teil die Möglichkeit integriert, für Dalvik geschriebene Android-Apps auszuführen. Aber einige Anbieter fahren bewusst die Schiene der Inkompatibilität ihrer Geräte und verlangen so eine Eigeneentwicklung nur für diese. Das schöne daran ist, dass dieser Ansatz scheitern wird, sobald die Marktanteile nicht mehr so groß sind. Hier werden sich wahrscheinlich Web-Applikationen durchsetzen.

Was Java betrifft, ist dessen Domäne heute neben Android-Telefonen im Serverbereich. Tatsächlich laufen Java-Applikationen ja mit hinreichend neuen JavaVM-Installationen oft auf allen Servern, wenn bei der Entwicklung gewisse Einschränkungen beachtet wurden. Ein Java-Programm, das seine Konfiguration in „C:/Programme/myCoolApp/config.properties“ sucht, wird auf Linux-Servern eher nicht das gewünschte finden, obwohl die „forward-slashes“ in Java-Programmen auch unter MS-Windows empfehlenswert sind und funktionieren. Aber es gibt immer noch genug potentielle Probleme, zum Beispiel mit Bibliotheken, die von der Java-Applikation benötigt werden, aber die inkomptabil zu der gewünschten Tomcat-Version sind, weil dort eine ältere Version derselben Bibliothek integriert und verwendet wird. Bis zu einem gewissen Grade lassen sich solche Probleme lösen und man findet immer wieder coole Ideen, die das ganz vollständig lösen wollen, wie z.B. OSGi. Mit entsprechendem Aufwand geht es auch tatsächlich in vielen Fällen, aber gewisse Bibliotheks-Inkomptabilitäten lassen sich nicht so leicht aus der Welt schaffen. so bleibt das „write once – run everywhere“ ein Versprechen, an dem viel Wahres dran ist, das aber auch seine Grenzen hat.

Nebenbei bemerkt ist das gar nicht so sehr die Eigenheit von Java. Qt mit C++ wurde schon erwähnt. Aber auch Perl, Ruby, Python, Oracle, C# (mit Mono), Lua, TeX und viele andere können ein ähnliches Versprechen wie Java abgeben und dieses ähnlich gut und mit ähnlichen Einschränkungen halten. Vielleicht ist es in Java schwieriger, nicht-triviale Dinge zu tun, die spezifisch für eine Plattform sind. Und vielleicht reicht Java in gewissen Bereichen mit diesem Versprechen etwas weiter als andere, aber für typische Server-Applikationen stimmt das „write-once-run-everywhere“ für Ruby-on-Rails mindestens genauso wie für Java-Web-Applikationen.

Share Button

Closures II (Java)

In Java gibt es schon seit recht frühen Versionen die sogenannten inneren Klassen.

Davon gibt es einige Ausprägungen:

  • statische innere Klassen
  • nicht-statische innere Klassen
  • anonyme innere Klassen

Beispiel 1: statische innere Klasse:

public class MyOuterClass {
  private static class MyInnerClass {
    ...
  }
}

Beispiel 2: nicht-statische innere Klasse:

public class MyOuterClass {
  private class MyInnerClass {
    ...
  }
}

Beispiel 3: anonyme innere Klasse:

public class MyOuterClass {
  public Runnable myMethod(..) {
    return new Runnable() {
      public void run() {
        ...
      }
   }
}

Sind die statischen inneren Klassen nichts anderes als weitere Klassen, die halt nur in ihrer Sichtbarkeit speziell für die umgebende Klasse zugänglich sind, so sind die anderen beiden Ausprägungen viel weitgehender in die umgebende Klasse integriert. Alle Attribute und Methoden der umgebenden Klasse können auch von der inneren Klasse aus angesprochen werden. Es fehlt leider ein zum „super“ analoges Schlüsselwort „outer“, so dass man sich mit Konstrukten wie „MyOuterClass.this.method()“ behelfen muss, wenn man etwas ansprechen will, was sowohl in der inneren als auch in der umgebenden Klasse vorkommt.

Genauer betrachtet gilt das für die statischen inneren Klassen und die statischen Attribute und Methoden der umgebenden Klasse natürlich auch, wobei man hier einfach „MyOuterClass.myStaticMethod()“ aufrufen kann.

Um in den nicht-statischen und anonymen inneren Klasse auf die umgebenden Attribute und Methoden zugreifen zu können, muss also implizit eine Instanz der äußeren Klasse referenziert werden. Das sollte man beachten, weil diese Referenz natürlich gehalten wird und so eine innere Klasse verhindert, dass die umgebende Instanz der Garbagecollection zugeführt wird. Beim Serialisieren kann ein scheinbar kleines Objekt riesig werden, wenn es einer solchen inneren Klasse entstammt.

Praktisch sind diese inneren Klassen, wenn man mit einem Objekt zwei verschiedene Interfaces befriedigen will. Es kann auch so etwas banales sein wie ein Collection, die mit getImmutable() eine Variante von sich selbst herausgibt, wo alle Methoden, die zu Änderungen führen würden, UnsupportedOperationException werfen.

Aber wie kommt man damit zu einem Ersatz für die Closures?

Vielleicht so:

public interface Function {
  public Y f(X x);
}

und dann kann man so etwas machen wie

public class C {
  public Function createAdder(final int x) {
    return new Function() {
      public Integer f(Integer xx) {
        return x+xx;
      }
    };
  }
  
  
  public static void main(String args[]) {
    C c = new C();
    Function ff = c.createAdder(3);
    for (int i = 0; i < 3; i++) {       System.out.println("i=" + i + " s=" + ff.f(i));     }     System.out.println();     ff = c.createAdder(90);     for (int i = 0; i < 3; i++) {       System.out.println("i=" + i + " s=" + ff.f(i));     }   } }

Das erzeugt etwa diese Ausgabe:

i=0 s=3
i=1 s=4
i=2 s=5

i=0 s=90
i=1 s=91
i=2 s=92

Diese Quelltexte kann man auf Github finden.

Es kommt also dem nahe, was man an dieser Stelle mit Perl machen kann.

Mit den Lambda-Funktionen oder Closures aus Java 8 sieht es dann etwa so aus:

  public Function createAdder(final int x) {
    return (xx) -> x+xx;
  }

Offensichtlich hat man dadurch also eine kürzere Schreibweise gewonnen, was Konstrukte dieser Art viel attraktiver macht.

Share Button

Integration numerischer Typen in Programmiersprachen

Rechnen ist ja das, was wir mit den Computern so machen, deshalb heißen sie ja auch Rechner.
Und zum Rechnen brauchen wir die numerischen Typen andauernd, also kann das wohl kein Problem sein, oder?

Es hängt ein bisschen davon ab, was man sich unter numerischen Typen vorstellt. Fließkommazahlen oder irgendeine Art von Ganzzahlen können fast alle, manche sogar beides. Gute Ganzzahlentypen sind aber leider nur selten verfügbar, da die Frage des Überlaufs oft schlecht gelöst ist. Weitere interessante numerische Datentypen sind rationale Zahlen, komplexe Zahlen und Festkomma-Dezimalzahlen.

Doch wie sieht es mit der Integration in die Sprache aus? Man möchte gerne so etwas wie
a = b*c + d*e
schreiben und meint damit, dass eine Zuweisung an a erfolgen soll, die den Wert (b*c) + (d*e) beinhaltet. Wegen „Punktrechnung vor Strichrechnung“ sollte man die Klammern aber weglassen können. Im Fall von Sprachen wie Lisp oder Forth, die eine völlig andere Syntax verwenden, passt diese Infix-Schreibweise natürlich nicht ins Bild und man kann diese Anforderung nicht sinnvoll stellen. In Lisp mit der Präfix-Schreibweise wäre es so etwas wie:
(setq a (+ (* b c) (* d e)))
und in Forth mit seiner Postfix-Schreibweise wäre es etwa so etwas:
b @ c @ * d @ e @ * + a ! .
C, Perl, Ruby, C++, Java, C#, JavaScript, Lua und vielen anderen funktioniert das mit den eingebauten Datentypen recht gut, aber sobald man eigene numerische Datentypen einführt, braucht man so etwas wie „Operator überladen“, was z.B. Lua, Perl, Ruby, C++ und C# können, aber Java und JavaScript nicht. Deshalb fängt man in Java an, für BigDecimal, BigInteger oder eigene Datentypen so etwas wie
a = b.multiply(c).add(d.multiply(e))
zu schreiben, was praktisch unlesbar und damit fehleranfällig ist. Vielleicht kann man sich mit einem Präprozessor behelfen, aber es bleibt ein Gebastel.

Ein anderer Aspekt ist am Beispiel von Java ganz gut zu sehen. Dort soll ja „alles“ ein Objekt sein. Man kann schön Interfaces, Klassen und Methoden schreiben und Verwenden, die sich auf Objekte verlassen, wie z.B. Map. Nun sind diese „primitiven“ Typen leider keine Objekte und man muss diese Wrapper-Klassen wie Integer, Double, Long, Boolean u.s.w. verwenden, was leider umständlich ist, zumal man mit den Wrappern die numerischen Fähigkeiten nicht mehr zur Verfügung hat. Scheinbar wurde das durch Autoboxing und Autounboxing gelöst, aber ich glaube, dass diese Erweiterung mehr Probleme geschaffen als gelöst hat. Nur als Beispiel, was bedeutet
x==y
wenn x und y long oder Long sind? Mal wird die Objekt-Identität und mal der Wert verglichen und ich vergesse immer, ob unboxing oder boxing zum Zuge kommt, wenn man dabei long und Long zusammenkommen lässt. Man kann aber einige spezielle Collection-Klassen finden, die auf Primitive zugeschnitten sind und dadurch ohne Boxing und Autoboxing auskommen, schneller laufen und weniger Speicher verbrauchen. In erster Linie bleibt es aber umständlich, weil man immer wieder diese Sonderfälle für Primitive und zum Teil auch Arrays berücksichtigen muss.

Share Button