Java Data Containers
Variablen
Type | Größe | Von bis |
boolean | 1 | |
char | 2 | |
byte | 1 | -128...127 |
short | 2 | -32768...32767 |
int | 4 | -2147483648 ... 2147483647 |
long | 8 | -9223372036854775808...9223372036854775807 |
float | 4 | 1,40239846 * 10^-45 ... 3,40282347 * 10^38 |
double | 8 | 4,94065645841246544 * 10^-324 ... 1,79769131486231570 * 10^308 |
BigDecimal
Ein BigDecimal erzeugen
Ein BigDecimal runden
(x bleibt dabei unverändert)
Mit BigDecimal teilen
BigDecimal b=new BigDecimal("1.2345");
BigDecimal r=a.divide(b, RoundingMode.HALF_UP);
Achtung, die Genauigkeit beim Teilen zweier BigDecimal wird durch das Objekt festgelegt, dessen divide Methode aufgerufen wird. Das kann zu überraschenden Ergebnissen führen! Daher überlegen, die Genauigkeit beim Teilen anzugeben
Gibt man beim teilen zweier BigDecimal Zahlen keine Roundsart an, und passt das Ergebnis nicht in ein BigDecimal (z.B. 1/3), gibt es eine Exception
Deshalb beim Teilen immer eine Rundungsart angeben!
Datencontainer
Entweder für beliebige Objekte
c.add("Hello");
c.add("World");
for ( Object x : c ) System.out.println( x );
Oder für spezielle
for ( String x : c ) System.out.println( x );
Listen
ArrayList, LinkedList
Mengen
HashSet, TreeSet, LinkedHashSet
Assoziativspeicher
HashMap, TreeMap, LinkedHashMap, WeakHashMap
- man hat ein Object O
- und einen Key K
- Von dem Key K gibt es ein H(K)
Die HashMap fügt im Hash dann unter H(K) alle Objekte O ein. Es kann mehrere Objekte O geben, die den gleichen Wert für H(K) haben, aber unterschiedliche Werte für K. Ist auch K gleich, wird der vorherige Eintrag überschrieben. Für die Klasse Integer gilt z.B. H(K)=K, das muss aber nicht so sein.
WeakHashMap
Der Garbage Collector in Java entsorgt automatisch alle Java Objekte, auf die niemand mehr eine Referenz hält.
Wenn man untersuchen möchte, ob ein Objekt schon entsorgt worden ist, kann man daher natürlich keine Referenz mehr auf das Objekt halten. Hier kommt die WeakHashMap ins Spiel. Objekte die als Value in ihr enthalten sind, zählen nicht als Referenz für die Garbage Collector. Sobald ein Objekt entsorgt wird, wird es auch automatisch aus der WeakHashMap entfernt. Kleines Beispiel:
public void doTest()
{
notYetFreed=new WeakHashMap<Integer, BigThing>();
for(int i=0; i<100; i++)
{
BigThing bt=new BigThing();
notYetFreed.put(i, bt);
System.out.println(notYetFreed.size());
}
while(notYetFreed.size()>0)
{
System.out.println(notYetFreed.size());
}
}
Da die BigThing Objekte nach jedem Durchlauf der Schleife von niemandem außer der WeakHashMap referenziert werden, werden die Einträge aus der WeakHashMap schnell wieder entfernt.
Achtung: Das funktioniert nicht, wenn man die Objekte auch als Key und nicht nur als Value in der WeakHashMap verwendet. So würde es also nicht funktionieren:
...
notYetFreed.put(bt, bt);
Memory Efficient Containers
If at least your key and maybe even the values are of basic types like int you can save memory by using those memory efficient and fast containers. Here is an overview of the memory efficient and fast containers.
final Int2FloatMap int2float=new Int2FloatOpenHashMap();
final Object2FloatMap<Foo> int2object = new Object2FloatOpenHashMap<>();
Cache
This cache implementation returns the cached value if it is already there or call your method myHelperMethodToGetTheValueOfNotThereYet to get it, store it and return it to you. Only problem here is that your method only gets the key as a parameter.
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
...
LoadingCache<String, List<String>> memoryCache = CacheBuilder.newBuilder().concurrencyLevel(1).maximumSize(memoryCacheMaxEntries)
.expireAfterWrite(memoryCacheMaxAgeMinutes, TimeUnit.MINUTES)
.build(new CacheLoader<String, List<String>>() {
@Override
public List<String> load(final String key) throws Exception {
return myHelperMethodToGetTheValueOfNotThereYet(key);
}
});
...
final List<String> res = memoryCache.get("foo");
In case you method for loading new values needs more parameters this works like this
import com.google.common.cache.CacheBuilder;
...
memoryCache = CacheBuilder.newBuilder().concurrencyLevel(1).maximumSize(memoryCacheMaxEntries)
.expireAfterWrite(memoryCacheMaxAgeMinutes, TimeUnit.MINUTES).build();
...
final List<String> res = memoryCache.get("foo", () -> myHelperMethodToGetTheValueOfNotThereYet("foo", a, b, c, ...));
You can also add values by yourself (less cool)
.build();
myCache.put("Key", "Value");
Queue
LinkedList, ArrayBlockingQueue, PriorityQueue
Sortieren von Datencontainern
So kann man eine Collection sortieren lassen:
{
...
@Override
public int compare(Foo o1, Foo o2)
{
...
}
...
}
List<Foo> myList;
Collections.sort(myList);
Comparator myComparator=new MyFooComparator();
Collections.sort(myList, myComparator);
Im ersten Fall muss Foo Comparable implementieren, im zweiten Fall kann man seine(n) eigenen Comparator(en) schreiben. Die Sortierung arbeitet direkt auf dem Container selbst, gegenfalls vorher klonen.
Bei Sortieren kann man leicht das Problem bekommen, dass man zwei Elemente a, b hat, die nicht gleich sind (!a.equals(b)). Allerdings könnten beide Objekte bezüglich einer Sortierung weder kleiner noch größer sein. Beispielsweise bei der Sortierung von Personen nach Schuhgröße. a und b können ganz verschiedene Menschen sein und dennoch genau die selbe Schuhegröße haben. In diesem Fall muss man besonders mit einem TreeSet Container aufpassen. Dieser speichert jedes Objekt höchstens ein mal und aus a.compareTo(b)==0 schließt er (fälschlicherweise?) nur eines der beiden Objekte muss im Container gespeichert werden. Lösung: compareTo() auf mehr Attribute ansetzten oder statt TreeSet eine andere Datenstruktur benutzen (TreeBag?)
Deal with value is there yet or not
Adding a value is complicated as you never know if the value is already there
if (!myMap.containsKey("john")) {
lst = new ArrayList<>();
myMap.put("john", lst);
lst.add(42);
} else {
lst = myMap.get("john");
lst.add(42);
}
Better way, put value if not yet there. But watch out, putIfAbsent returns the value for the key before you called the method, so you can not operate directly on the return value. Also the value is always computed even if the key is there. Use computeIfAbsent instead!
myMap.get("john").add(42);
With this you can provide a lamda to do even more in the case there is no value yet
if (key.startsWith("j")) {
return new ArrayList<>();
} else {
return new LinkedList<>();
}
});
lst.add(42);
If you only want to do something if value is already there
value.add(42);
return value;
});
Provide code to handle both cases (value there, value not there)
if (value == null) {
value = new ArrayList<>();
}
value.add(42);
return value;
});
If you already have the to be placed data in the right format you can also you merge as it puts your value directly to the key if there is no value yet and exectues your code otherwise
myMap.merge("john", extra, (valueCurrent, valueNew) -> {
valueCurrent.addAll(valueNew);
return valueCurrent;
});
Strings
Strings Vorsicht beim Addieren von chars, das Ergebnis ist eine Zahl und kein String
char c2='b';
Object c1_2=c1+c2;
System.out.println(c1_2.getClass()+" "+c1_2); //class java.lang.Integer 195
Abhilfe, entweder sicherstellen, dass der erste Summand ein String ist
Oder einen StringBuffer verwenden
sb.append(c1).append(c2);
Aber auch eine Summe aus Zahlen kann leicht (ungewollt) zu einem String werden
int i2=2;
Object c1_2=""+i1+i2; // 42
String Vergleiche
Man sollte String immer über deren equals() Methode auf Gleichheit vergleichen. Aus Performance Gründen versucht Java allerdings konstante und gleiche Strings in identische Objekte abzubilden
String s2="Foo"+2;
boolean x=(s1==s2); // true
Das sollte man aber nicht für Vergleiche missbrauchen. Nach dieser kleinen Änderungen sind die Strings schon nicht mehr identisch
byte Array nach String
Immer den Zeichensatz angeben damit die Abbildung überall gleich ist
String s=new String(b, "ISO-8859-15"); // Foo€
Arrays
Foo[] x;
int i[][]; // == int [][] i == int[] i[]
int[] i, x[], y; // == int [] i; int [][]x; int[] y;
int[] prim = { 5, 7, 7 + 4, };
Array mit vorgegebener Größe:
int[] hundredElements;
hunderdElements = new int[ 100 ];
int[] prim;
prim = new int[]{ 5, 7, 11, };
Arrays überschreiben toString() nicht. Wenn z.B.
String.valueOf()
einen Array lesbar ausgeben, dann liegt das daran, dass sie selbst entsprechende Logik eingebaut haben.
Arraygröße
Mehrdimensionale Arrays
Rechteckiges Array
Beliebiges mehrdimensionales Array
i[0]=new int[7];
i[1]=new int[19];
i[2]=new int[2];
i[3]=new int[100];
Anonyme Arrays
Arrays durchlaufen
double sum = 0;
double[] myarray;
List<Double> mylist=new LinkedList<Double>();
// (1)
for ( int i = 0; i < myarray.length; i++ )
{
sum += myarray[i];
}
// (1b)
for ( double n : mylist )
{
sum += n;
}
// (2)
for (ListIterator<Double> i = mylist.listIterator(); i.hasNext(); )
{
sum+=i.next();
}
// (2b)
for ( double n : myarray )
{
sum += n;
}
(1) Normaler Array Durchlauf (1b) Kurzschreibweise für (1) (2) Durchlauf eines Containers mit einem Iterator (2b) Kurzschreibweise für (2). Man verliert aber den Vorteil gegebenfalls einen Iterator zur Hand zu haben (z.B. zum Löschen von Elementen).
Unnötige Methodenaufrufe in for Schleifen sparen Häufig sehen for Schleifen so aus
Wenn sich der Rückgabewerte der Methode aber für die Dauer des Schleifendurchlaufes nicht ändert und die Methode eine nennenswerte Laufzeit hat, kann man die Schleife über eine weitere Variable deutlich beschleunigen
a<b;
a++);
Teile eines Arrays in einen anderen Array oder in sich selbst kopieren
int[] y = Arrays.copyOfRange( x, 6, 9 );
Einen Array in einen Vector wandeln
So kann man einen String Array in einen String Vector wandeln
Array Vergleichen
Arrays.deepEquals( a1, a2 ); // (2)
// (1) Referenzen der Elemente im Array müssen gleich sein // (2) Inhalt muss gleich sein // (2)
enum
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Der Zugriff erfolgt so:
In den case Statements einer switch Anweisung dagegen so:
Über Enums kann man iterieren