Streamy primitivních datových typů v Javě

Java kromě Stream<Object> nabízí i streamy primitivních datových typu. Přesněji řečeno má takové streamy tři:

  • IntStream
  • DoubleStream
  • LongStream

Vytvoření streamu primitivních datových typů
K vytvoření streamu primitivních datových typů je možné, tak jako pro stream objektů, použít metody generate() a iterate().

// IntStream obsahující deset sedmiček.
IntStream isg = IntStream.generate(() -> 7).limit(10);
// IntStream s elementy 0, 1, 2, 3, 4.
IntStream isi = IntStream.iterate(0, i -> i + 1).limit(5);

// Nekonečný DoubleStream, který obsahuje náhodná čísla.
DoubleStream dsg = DoubleStream.generate(() -> Math.random());
// DoubleStream s elementy 1.1, 2.2, 4.4, 8.8, 17.6.
DoubleStream dsi = DoubleStream.iterate(1.1, i -> i + i).limit(5);

// Nekonečný LongStream, který obsahuje čísla 9876543210.
LongStream lsg = LongStream.generate(() -> 9876543210L);
// LongStream obsahující čísla (řády) od 1 miliónu po 1 bilión.
LongStream lsi = LongStream.iterate(1_000_000, num -> num * 10).limit(9);

Dalším způsobem je použít statickou metodu of() a do ní uvést výčet hodnot.

IntStream intStream = IntStream.of(1, 3, 5, 7);
DoubleStream doubleStream = DoubleStream.of(0.52, 15.24, 7.879);
LongStream longStream = LongStream.of(14_854, 4_854);

Stream je možné vytvořit taktéž z pole pomocí statické metody java.util.Arrays.stream().

int[] intArray = { 1, 2, 3, 4};
double[] doubleArray = {12.548, 0.99, 103.1};
long[] longArray = {12, 778_755_441_784L, 1_221};

// IntSteam s elementy 1, 2, 3, 4.
IntStream isFromArray = Arrays.stream(intArray);
// DoubleStream s elementy 0.99 a 103.1.
DoubleStream dsFromArray = Arrays.stream(doubleArray, 1, 3);
// LongStream s elementem 1221.
LongStream lsFromArray = Arrays.stream(longArray, 2, 3);

IntStream a LongStream mají navíc k dispozici statické metody range() a rangeClosed().

// IntSteam s elementy 0, 1, 2, 3, 4.
IntStream isFromRange = IntStream.range(0, 5);
// LongStream s elementy 100, 101, 102, 103.
LongStream lsFromRange = LongStream.rangeClosed(100, 103);

Zdroj: HORSTMANN, Cay S. Java SE 8 for the really impatient. Upper Saddle River, NJ: Addison-Wesley, 2014, xv, 215 pages. ISBN 0321927761.

Zrádný LEFT JOIN

V SQL je JOIN klauzule používána pro spojení tabulek. Klauzule LEFT JOIN spojí všechny záznamy z tabulky uvedené nalevo od klauzule se záznamy v tabulce uvedené napravo.

SELECT * FROM tabulka_jedna LEFT JOIN tabulka_dve

Zrádností LEFT JOINu (to samé platí i pro RIGHT JOIN) je to, že vezme všechny záznamy z levé tabulky a přiřadí je k (spojí je s) záznamům v pravé tabulce. Pokud ale v pravé tabulce není záznam, bude sloupec obsahovat null. Takhle to vypadá zřejmě, pojďme se ale podívat na následující příklad (v PostgreSQL).

Vytvoříme si schéma ZLJ (Zrádný Left Join) a v něm si vytvoříme tři tabulky. Tabulka USER bude obsahovat údaje o uživateli jako je jeho id, číslo, jméno. Tabulku AUTH_TYPE bude číselník s typy autorizací (sms, pin, heslo). Jako poslední vytvoříme tabulku AUTHENTCATION, která bude uchovávat údaje týkající se authentikace (typ, kdy byla vytvořena,) a bude provázána s tabulkou USER pomocí cizího klíče. Tabulky si rovnou naplníme daty.

DROP SCHEMA IF EXISTS ZLJ CASCADE;
CREATE SCHEMA ZLJ;

CREATE TABLE ZLJ.USER (
	user_id	serial not null,
	user_number varchar(10) not null,
	username varchar(10) not null,

	primary key(user_id)
);

CREATE TABLE ZLJ.AUTH_TYPE (
	auth_type_id serial not null,
	type varchar(10) not null,

	primary key(auth_type_id)
);

CREATE TABLE ZLJ.AUTHENTICATION (
	authentication_id serial not null,
	user_id int not null,
	auth_type_id int not null,
	created timestamp not null,

	primary key(authentication_id),
	foreign key(user_id) references ZLJ.USER(user_id),
	foreign key(auth_type_id) references ZLJ.AUTH_TYPE(auth_type_id)
);

INSERT INTO ZLJ.USER (user_number, username) VALUES
	('9954258641', 'fnovotny'),
	('9865662384', 'franta03'),
	('9932652322', 'mariem');

INSERT INTO ZLJ.AUTH_TYPE (type) VALUES
	('pin'),
	('sms'),
	('password');

INSERT INTO ZLJ.AUTHENTICATION (user_id, auth_type_id, created) VALUES
	(1, 2, '2016-01-05 21:11:58'),
	(3, 3, '2016-01-05 22:03:17');

Všimněte si jedné věci. Všechny sloupce mají definované omezení NOT NULL. To znamená, že do každého sloupce záznamu je nutné vložit hodnotu. Neexistuje sloupec, ve kterém by mohla být NULL hodnota.

Naši databázi voláme z nějaké aplikace. Tato aplikace požaduje následující údaje: authentication_id (id záznamu v tabulce AUTHENTICATION) a auth_type_id (foreign key na záznam v číselníku). Tyto údaje potřebuje pro určitého uživatele. Jelikož chceme mít ve výsledku obsaženy všechny uživatele, použijem LEFT JOIN.

SELECT authentication_id, auth_type_id 
FROM 
ZLJ.USER LEFT JOIN ZLJ.AUTHENTICATION ON (ZLJ.USER.user_id = ZLJ.AUTHENTICATION.user_id)
WHERE user_number = ?

Za otazník se v prepared statementu dosadí user_number. Jelikož mají všechny sloupce omezení NOT NULL, vždy dostaneme nějakou hodnotu. Chyba. Aplikace začne občas padat. Přesněji řečeno začne padat v momentě, kdy se pokusíme v tabulce AUTH_TYPE dohledat záznam type dle auth_type_id, které získáme  z předchozího selectu. Pokud totiž budeme hledat záznam auth_type_id pro user_number 9865662384, dostaneme NULL.

Dotaz:

SELECT * 
FROM 
ZLJ.USER LEFT JOIN ZLJ.AUTHENTICATION ON (ZLJ.USER.user_id = ZLJ.AUTHENTICATION.user_id)

Výsledek:

postgresql_zradny_left_join

Dotaz:

select * from 
	(SELECT * FROM ZLJ.USER LEFT JOIN ZLJ.AUTHENTICATION ON (ZLJ.USER.user_id = ZLJ.AUTHENTICATION.user_id)) as SEL 
where auth_type_id is null;

Vrátí záznamy, které mají ve sloupci auth_type_id NULL.

postgresql_zradny_left_join_02

Kreslení mřížky v Gimpu

Gimp umožňuje do obrázku vykreslit mřížku. Tato možnost je ale skrytá docela hluboko. Filtry -> Vykreslit -> Vzorek -> Mřížka.

gimp_mrizka

gimp_mrizka_02

Experimentováním s nastavením lze dosáhnout zajímavých efektů.

gimp_mrizka_03

Uvedeného výsledku jsem dosáhl pomocí Mísení (barevný přechod na pozadí)

gimp_mrizka_04

a následujícího nastavení mřížky.

gimp_mrizka_05

Rovná čára v Gimpu

Gimp je výborný grafický editor, ale nemohl jsem přijít na to, jak v něm nakreslit rovnou čáru. Řešení je jednoduché. Stačí zvolit kreslící nástroj (ať už tužku nebo štětec), kliknout na plátno do výchozího bodu, stisknout a držet klávesu Shift, kliknou na koncové místo a pustit klávesu Shift.

gimp_kresleni_rovne_cary

Pokud budete stále držet klávesu Shift, můžete dále pokračovat v kreslení rovných čar. Stačí jen klikat na další místa na plátně. Tímto způsobem se dá vytvořit i objekt a to tím způsobem, že kreslení ukončíte ve výchozím bodu.

gimp_kresleni_rovne_cary_02

Vytvořený objekt je pak možné vybarvit pomocí plechovky.

gimp_kresleni_rovne_cary_03

České online kurzy

Ve světě již nějakou dobu fungují servery, nabízející různé druhy vzdělávacích kurzů. Jako příklad uvedu udemy. Na tomto webu naleznete tisíce kurzů na různá témata. Kurzy jsou jak zdarma tak placené. Většina kurzů až na drobné výjimky je v angličtině. Zajímalo mě, jak jsme na tom v naší české kotlině. Věnoval jsem tedy nějaký čas vyhledávání a zde je výsledek:


e-akademia.cz

Naleznete zde video přednášky, video tutoriály a testy týkající se převážně matematiky. Prozatím to vypadá jako projekt jednoho člověka.

Cílem našeho projektu je zpřístupnit vzdělání všem lidem v ČR, kteří o něj mají zájem. Chceme, aby vzdělání bylo dostupné lidem, jak po stránce geografické, tak po stránce finanční. Řešení této myšlenky vidíme v organizování online video kurzů zaměřených na nějaké konkrétní téma.


kurzyproradost.cz

Aktuálně (1. 1. 2016) nabízí okolo 60 kurzů. Příliš technických kurzů jsem v nabídce nenalezl a vadilo mi, že nebylo možno kurzy vyhledávat dle zaměření. Někdo jiný si tam ale možná vybere.


kurzomanie.cz

Web je graficky velmi hezký. Bohužel ke dnešnímu dni (1. 1. 2016) nenabízí žádný kurz, i když uvádí:

Spuštění portálu je naplánováno na prosinec 2015. Momentálně jednáme s více jak 30 lektory a instruktory na spuštění prvních 50+ vzdělávacích kurzů.

V každém případě bude zajímavé tento web po nějakém čase znovu navštívit a zjistit, zda skutečně splnili to, co slibovali.


seduo.cz

Na tento web jsem velmi zvědavý. Tvrdí totiž o sobě, že je podporován job.cz. Ke dni publikace tohoto příspěvku je v nabídce 15 kurzů z toho 10 zdarma. Určitě se na tento web ještě v budoucnu podívám.


naucmese.cz

Aktuálně (1. 1. 2016) je v nabídce téměř 300 kurzů. Je možné kurzy vyhledávat dle kategorie a též dle města. Vypadá to ale, že se jedná převážně o kurzy, na které je třeba se dostavit osobně, a nikoliv o online kurzy.


onlinekurzyzdarma.cz

K 1. 1. 2016 zde naleznete přes 50 online kurzů zdarma. Kurzy jsou rozděleny do několika kategorii a lze mezi nimi vyhledávat. Tak jako většina zmíněných webů nabízí možnost nechat si na email zasílat novinky.


khanovaskola.cz

Na tomto webu naleznete videolekce k předmětům jako matematika, fyzika, chemie, biologie, ekonomie, dějepis a informatika. Web je graficky velmi povedený a měli byste na něm nalézt více než 2.700 videolekcí zadarmo a pro všechny.

Khanova škola je vzdělávací portál pro děti i dospělé. Jádro tvoří výuková videa, která skládáme do promyšlených návazností tak, abyste si mohli užít souvislou výuku od úplných základů až po komplexní oborové znalosti. Vše zdarma a z pohodlí vašich domovů. 


Na závěr ještě zmíním stránku mimoskolu.cz/katalog-zdroju/ kde je seznam českých i zahraničních vzdělávacích zdrojů.

Java Optional efektivně

Klíčem k efektivnímu využití Optional je používání metod, které dokáží s Optional pracovat. Tyto metody buď zpracují hodnotu zabalenou v Optional, nebo, v případě, že není přítomna, vytvoří náhradní hodnotu (objekt).

Metoda Optional.ifPresent() jako parametr bere funkci java.util.function.Function a v případě, že Optional obsahuje hodnotu (referenci na objekt), je tato hodnota (reference) předána funkci. Pokud hodnotu neobsahuje, nic se nestane.

Stream<Integer> is1 = Stream.of(5, 7, 1, 8, 2);
Optional<Integer> max = is1.max(Integer::compareTo);
Stream<Integer> is2 = Stream.empty();
Optional<Integer> min = is2.min(Integer::compareTo);

Výpis výsledků do konzole (vypíše 8):

max.ifPresent((i) -> System.out.println(i));
min.ifPresent((i) -> System.out.println(i));

Přidání výsledků do listu:

List<Integer> results = new ArrayList<>();
max.ifPresent((i) -> results.add(i));
min.ifPresent((i) -> results.add(i));

Metoda Optional.ifPresent() zpracovává hodnotu v případě, že je přítomna. Dalším způsobem je použít metody, které vytvoří hodnotu (nebo něco provedou) v případě, že hodnota není přítomna. Pro tento případ se hodí metody optionalVariable.orElse(), optionalVariable.orElseGet(), optionalVariable.orElseThrow().

Stream<String> ss1 = Stream.of("lokomotiva", "auto", "letadlo", "raketa", "vzducholoď");
Optional<String> firstWithL = ss1.filter((word) -> word.startsWith("l")).findFirst();
Stream<String> ss2 = Stream.of("lokomotiva", "auto", "letadlo", "raketa", "vzducholoď");
Optional<String> anyWithQ = ss2.filter((word) -> word.startsWith("q")).findAny();

String withL = firstWithL.orElse("");
String withQ1 = anyWithQ.orElse("");

System.out.println(withL);
System.out.println(withQ1);

try {
	String withQ2 = anyWithQ.orElseThrow(Exception::new);
} catch (Exception e) {
	e.printStackTrace();
}

Výpis:

lokomotiva

java.lang.Exception
	at java.util.Optional.orElseThrow(...)

Metoda Optional.orElse() vrátí v případě, že Optional obsahuje null, náhradní hodnotu zadanou jako parametr. Pokud Optional obsahuje referenci, vrátí objekt. Metoda Optional.orElseThrow() vyhodí danou výjimku (pokud Optional obsahuje null).


Zdroj: HORSTMANN, Cay S. Java SE 8 for the really impatient. Upper Saddle River, NJ: Addison-Wesley, 2014, xv, 215 pages. ISBN 0321927761.

Proč používat Java Optional?

Není příliš velký rozdíl mezi:

Optional<T> op = ...;
op.get().someMethod();

vs

T val = ...;
val.someMethod();

Pokud se chceme vyhnout výjimce:

if (op.isPresent()) {
    T val = op.get();
    val.someMethod();
}

vs

if (val != null) {
    val.someMethod();
}

Pokud programátor Optional nepoužívá, ušetří dokonce pár znaků při psaní (nemusí volat get()). Proč tedy používat Optional?

Předchozí příspěvek na téma Optional se týkal třídy Optional z knihovny Guava. Tento příspěvek se věnuje třídě java.util.Optional, která je dostupná od Javy 8. Důvody pro jejich použití jsou ale obdobné. Guava Optional se používá v případě, že máte starší verzi Javy. V případě, že používáte Javu 8, knihovnu Guava nepotřebujete. 

Důvodem pro používání Optional je to, že null může mít více významů. Null může být prázdná hodnota, může představovat chybu, může to být též neinicializovaná proměnná. Pokud ale máme Optional<T> (Optional vlastně zabaluje hodnotu T), tak buď máme referenci na objekt typu T (Optional.isPresent() == true) a nebo referenci nemáme (Optional.isPresent() == false). V případě, že referenci nemáme, odpovídalo by to případu, kdy null vyjadřuje prázdnou hodnotu.

Další výhodou Optional je to, že bychom si při použití Optional měli uvědomit, že se jedná o hodnotu, která nepovinná (proto název optional) a podle toho se k ní chovat. U běžné proměnné nás nemusí napadnout, že je třeba provést kontrolu na to, zda třeba není null. To je důvod, proč některé metody vrací Optional. Dávají tím najevo, že nemusí vrátit žádnou hodnotu. Je to bezpečnější způsob práce než s null. Optional například vrací některé metody pracující s Stream (java.util.stream.Stream).

Stream<Integer> is1 = Stream.of(5, 7, 1, 8, 2);
Optional<Integer> max = is1.max(Integer::compareTo);

Stream<Integer> is2 = Stream.empty();
Optional<Integer> min = is2.min(Integer::compareTo);

Stream<String> ss1 = Stream.of("lokomotiva", "auto", "letadlo", "raketa", "vzducholoď");
Optional<String> firstWithL = ss1.filter((word) -> word.startsWith("l")).findFirst();

Stream<String> ss2 = Stream.of("lokomotiva", "auto", "letadlo", "raketa", "vzducholoď");
Optional<String> anyWithQ = ss2.filter((word) -> word.startsWith("q")).findAny();

Stream<String> ss3 = Stream.of("lokomotiva", "auto", "letadlo", "raketa", "vzducholoď");
Optional<String> shortest = ss3.min((word1, word2) -> word1.length() - word2.length());

Metody min(), max(), findAny(), findFirst() vrací Optional, protože Stream, který dostanou může být prázdný a tím pádem žádnou hodnotu nemusí najít (viz is2), nebo žádná taková hodnota nevyhovuje podmínkám (viz ss2.filter() na slova začínající na q -> žádné takové tam není).

Vlastní anotace v Javě

Anotace v Javě jsou způsobem, jak do kódu přidat meta informace (metadata, data o datech). Anotace se používají pro:

  • instrukce pro compiler
  • instrukce pro build-time
  • instrukce pro run-time

Java umožňuje vytvořit si vlastní anotace pomocí klíčového slova @interface.

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

	String value();
	int version() default 1; 
}

Použitá anotace @Retention(RetentionPolicy.RUNTIME) určuje, že tato anotace (@MyAnnotation) má být přístupná i v run-time. Anotace @MyAnnotation má dva elementy (value a version). Element version má nastavenu výchozí hodnotu na 1.

public @interface MyOtherAnnotation {

}

Anotace @MyAnnotation žádné elementy neobsahuje.

@MyOtherAnnotation
@MyAnnotation(value = "abcd")
public class MyClass {

}

Třída MyClass je anotována oběma anotacemi. Anotace @MyAnnotation má nastavenu hodnotu elementu value na „abcd“ a hodnotu version na 1 (default).

import java.lang.annotation.Annotation;

public class App {

	public static void main(String[] args) {
		
		Class clazz = MyClass.class;
		if (clazz.isAnnotationPresent(MyAnnotation.class)) {
			System.out.println("Je anotován: " + MyAnnotation.class.getSimpleName());
			
			MyAnnotation myAnnotation = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
			System.out.println(myAnnotation.value());
			System.out.println(myAnnotation.version());
		}
		if (clazz.isAnnotationPresent(MyOtherAnnotation.class)) {
			System.out.println("Je anotován: " + MyOtherAnnotation.class.getSimpleName());
		}
	}
}

Výstup:

Je anotován: MyAnnotation
abcd
1

Díky tomu, že @MyAnnotation je anotováná @Retention(RetentionPolicy.RUNTIME) je možné k ní pomocí reflexe přistupovat i v run-time. Lze zjistit nejen to, zda daná třída je pomocí určité anotace anotována, ale také hodnoty elementů. Anotace @MyOtherAnnotation v runtime přítomna není.


Zdroj: tutorials.jenkov.com/java/annotations.html

Nekonečný Stream v Javě

Java 8 představila Stream, což je posloupnost hodnot, nad kterou je možné provádět operace. Je možné vytvořit i nekonečný Stream.

Stream<String> stringStream = Stream.generate(() -> UUID.randomUUID().toString());
Stream<String> constantStream = Stream.generate(() -> "word");
Stream<Double> doubleStream = Stream.generate(Math::random);
Stream<Integer> integerStream = Stream.iterate(0, (i) -> i + 1);
Stream<BigDecimal> bigDecimalStream = Stream.iterate(BigDecimal.ZERO, (bd) -> bd.add(BigDecimal.ONE));

Metoda Stream.generate() požaduje parametr typu Supplier<T>, což je funkční rozhraní obsahující metodu T get(). Metoda Stream.iterate() požaduje parametr typu T seed (počáteční element) a UnaryOperator<T>, což je funkční rozhraní dědící z funkčního rozhraní Function (místo typů <R, T> používá <T, T>, metoda apply(T) tedy nevrací typ R ale T).

Pokud nám stačí jen několik prvků streamu, můžeme použít operaci limit(n), která vezme n prvních prvků streamu.

System.out.println(Arrays.toString(stringStream.limit(5).toArray()));
System.out.println(Arrays.toString(constantStream.limit(5).toArray()));
System.out.println(Arrays.toString(doubleStream.limit(5).toArray()));
System.out.println(Arrays.toString(integerStream.limit(5).toArray()));
System.out.println(Arrays.toString(bigDecimalStream.limit(5).toArray()));

Výstup:

[cb2f9562-e468-4409-bd61-cc7f153adbc9, 45582575-fda8-48a4-9ecd-bd8e9ca87f42, 6338c4d5-7b67-4fff-b1ec-6a86e2bacec8, 9ba3c6b0-700a-4e78-b633-e6a8da9fb946, 04951054-8c43-43a6-a177-aeb2da7c7320]
[word, word, word, word, word]
[0.9250003903085224, 0.912794982359122, 0.8636553602757563, 0.5809828517176208, 0.002178508953346925]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]

Pozor na datový typ (na přetečení rozsahu), který je použit v nekonečném proudu.

Stream<Integer> is = Stream.iterate(1, (i) -> i * 10).limit(15);
Stream<BigDecimal> bds = Stream.iterate(BigDecimal.ONE, (bd) -> bd.multiply(new BigDecimal("10"))).limit(15);

System.out.println(Arrays.toString(is.toArray()));
System.out.println(Arrays.toString(bds.toArray()));

Výstup:

[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1410065408, 1215752192, -727379968, 1316134912, 276447232]
[1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000]

Práce s historií v Bash

Velikost historie (počet uchovávaných příkazů) se nastavuje v souboru .bashrc v home adresáři (proměnné HISTSIZE a HISTFILESIZE). Historie v Bash se dá procházet pomocí šipek. Celou historii zobrazíme příkazem history. Výpis historie můžeme omezit přepínačem, pomocí kterého určíme, kolik příkazů z historie chceme zobrazit.

history 10 (zobrazí posledních 10 záznamů)

Pokud chceme provést nějaký příkaz z historie a nechce se nám dlouho listovat, stačí si historii zobrazit (u každého příkazu je číslo) a napsat !číslo_příkazu.

$ history
1 ls /etc
2 nano host
3 ls
4 ls -al
5 nano /etc/host
6 cd apt
7 pwd
8 cd /etc/apt
9 ls
10 nano sources.list
11 exit
12 history

V příkladu výše mám v historii celkem 12 příkazů. Pokud chci rychle provést příkaz nano /etc/host stačí mi napsat !5. Pokud chci vykonat poslední příkaz stačí napsat !!. Pro příkaz nano /etc/host jsem zapomněl uvést sudo. Stačí mi tedy napsat sudo !!.

Zdroj: digitalocean.com/community/tutorials/…