Java Collections, Streams, I/O, Threads & JavaFX

klikni na metódu → zobrazí sa príklad

collections Vytvorenie kolekcií
new HashSet<>() množina bez duplikátov, nezoradená +
príklad
Set<String> s = new HashSet<>();
s.add("Java"); s.add("Kawa"); s.add("Java");
System.out.println(s); // [Java, Kawa]  — duplikát ignorovaný

// Java 9 – immutable konštanta (nie je modifikovateľná!)
Set<Integer> r = Set.of(1, 2, 3);
new TreeSet<>() množina utriedená podľa Comparable +
príklad
TreeSet<String> ts = new TreeSet<>(
    Arrays.asList("pat", "dva", "tri"));
System.out.println(ts); // [dva, pat, tri]  — abecedne
ts.first(); // "dva"
ts.last();  // "tri"
new ArrayList<>() zoznam s indexmi, duplikáty povolené +
príklad
List<String> l = new ArrayList<>();
l.add("a"); l.add("b"); l.add("a"); // [a, b, a]

// Java 9 – immutable
List<String> imm = List.of("a", "b", "c");
// mutable kópia:
List<String> m = new ArrayList<>(List.of("a", "b"));
new HashMap<>() zobrazenie kľúč→hodnota (dictionary) +
príklad
Map<String, Integer> m = new HashMap<>();
m.put("x", 1); m.put("y", 2);

// Java 9 – immutable
Map<String, Integer> r = Map.of("one", 1, "two", 2);
new TreeMap<>() mapa utriedená podľa kľúčov +
príklad
TreeMap<String,String> europe = new TreeMap<>();
europe.put("SLOVAKIA", "Bratislava");
europe.put("CZECHIA",  "Prague");
System.out.println(europe);
// {CZECHIA=Prague, SLOVAKIA=Bratislava}  — podľa kľúča
set Set — operácie
s.add(e) pridá prvok, vracia false ak už existuje +
príklad
Set<String> s = new HashSet<>();
boolean ok1 = s.add("Java"); // true  – pridané
boolean ok2 = s.add("Java"); // false – už existuje
s.contains(e) overí existenciu prvku +
príklad
s.add("Java");
s.contains("Java");  // true
s.contains("Python"); // false
s.remove(e) odstráni prvok +
príklad
s.remove("Java"); // vracia true ak prvok bol v sete
s.addAll(c) / removeAll(c) / containsAll(c) množinové operácie: zjednotenie / rozdiel / podmnožina +
príklad
Set<Integer> a = new HashSet<>(Set.of(1,2,3));
Set<Integer> b = new HashSet<>(Set.of(3,4,5));

a.addAll(b);      // zjednotenie: a = {1,2,3,4,5}
a.removeAll(b);   // rozdiel:     a = {1,2}
a.containsAll(b); // je b podmnožinou a?
ts.subSet(from, to) SortedSet – prvky >= from a < to +
príklad
TreeSet<String> ts = new TreeSet<>(
    Arrays.asList("devat","dva","jedna","osem","pat","tri".split(",")));
// [devat, dva, jedna, osem, pat, tri]

ts.subSet("dva", "pat");  // [dva, jedna, osem]  (from incl, to excl)
ts.headSet("osem");       // [devat, dva, jedna]
ts.tailSet("osem");       // [osem, pat, tri]
ts.first(); // "devat"
ts.last();  // "tri"
list List — operácie
l.get(i) / l.set(i, e) prístup a zápis podľa indexu +
príklad
List<String> l = new ArrayList<>(List.of("a","b","c"));
l.get(1);        // "b"
l.set(1, "99"); // [a, 99, c]
l.add(e) / l.add(i, e) pridá na koniec alebo na daný index +
príklad
l.add("d");      // pridá na koniec: [a, 99, c, d]
l.add(1, "X");  // vloží na index 1: [a, X, 99, c, d]
l.remove(i) / l.remove(obj) vyhodí podľa indexu alebo podľa hodnoty +
príklad
l.remove(2);         // vyhodí prvok na indexe 2
l.remove("a");       // vyhodí prvý výskyt "a"
// ⚠️ remove(Integer) vs remove(int):
List<Integer> nums = new ArrayList<>(List.of(10,20,30));
nums.remove(1);           // vyhodí INDEX 1  → [10, 30]
nums.remove(Integer.valueOf(10)); // vyhodí HODNOTU 10
l.indexOf(e) / l.lastIndexOf(e) index prvého / posledného výskytu +
príklad
List<String> l = List.of("a","b","a");
l.indexOf("a");     // 0
l.lastIndexOf("a"); // 2
l.indexOf("z");     // -1 (nenájdený)
l.subList(from, to) pohľad na podzoznam [from, to) +
príklad
List<String> l = List.of("a","b","c","d");
l.subList(1, 3); // [b, c]  (index 1 až 2)
map Map — operácie
m.put(k, v) pridá/prepíše dvojicu kľúč→hodnota +
príklad
Map<String,Integer> m = new HashMap<>();
m.put("a", 1);
m.put("a", 99); // prepíše: m = {a=99}
m.get(k) vráti hodnotu pre kľúč, alebo null +
príklad — frekvenčná tabuľka (klasický pattern)
Map<String,Integer> freq = new HashMap<>();
for (String w : words) {
  Integer c = freq.get(w);
  freq.put(w, c == null ? 1 : c + 1);
}
// java HashMapDemo x d o k o l a o k o l o k o l a
// {a=2, d=1, x=1, k=3, l=3, o=6}
m.containsKey(k) / m.containsValue(v) overí existenciu kľúča / hodnoty +
príklad
m.containsKey("Java");   // true/false
m.containsValue(42);     // true/false  (prechádza všetky hodnoty – pomalé!)
m.keySet() / m.values() / m.entrySet() pohľady na kľúče / hodnoty / páry +
príklad — prechod cez mapu
// Jednoducho cez kľúče:
for (String k : m.keySet())
  System.out.println(k + " → " + m.get(k));

// Cez páry (entrySet) – efektívnejšie:
for (Map.Entry<String,Integer> e : m.entrySet())
  System.out.println(e.getKey() + " → " + e.getValue());

// Lambda forEach (Java 8):
m.forEach((k, v) -> System.out.println(k + "=" + v));
m.putAll(other) zlúči dve mapy +
príklad — inverzia mapy
// Inverzia: krajina→hlavné mesto  →→  hlavné mesto→krajina
TreeMap<String,String> inv = new TreeMap<>();
for (String state : europe.keySet())
  inv.put(europe.get(state), state);
// {Bratislava=SLOVAKIA, Prague=CZECHIA}
iterácia Iterácia cez kolekcie
for (E e : collection) for-each – najčastejší spôsob +
príklad
for (String s : list)
  System.out.println(s);

// forEach s lambdou (Java 8):
list.forEach(System.out::println);
list.forEach(e -> System.out.println(e + e));
Iterator<E> it = c.iterator() iterátor – môžeš volať it.remove() +
príklad — bezpečné mazanie počas iterácie
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
  String val = it.next();
  if (val.isEmpty()) it.remove(); // ✅ bezpečné
  // ❌ list.remove(val) počas for-each → ConcurrentModificationException
}
ListIterator – it.previous() / hasPrevious() iterátor pre List – vie aj dozadu +
príklad — výpis zoznamu opačne
for (ListIterator<String> it = list.listIterator(list.size());
     it.hasPrevious(); )
  System.out.println(it.previous());
⚠️ tricky equals, hashCode, compareTo — vlastné objekty
⚠️ Pre HashSet/HashMap musíš implementovať OBE: equals() aj hashCode(). Pre TreeSet/TreeMap stačí Comparable (compareTo).
@Override equals(Object obj) porovnanie obsahu (nie referencie) +
príklad
class Hruska {
  int velkost;

  @Override
  public boolean equals(Object obj) {
    return (obj instanceof Hruska)
        && velkost == ((Hruska) obj).velkost;
  }
}
// Bez equals: h1.equals(h2) porovnáva referencie → false
@Override hashCode() HashSet: rovnaké objekty MUSIA mať rovnaký hash +
príklad
@Override
public int hashCode() {
  return velkost;        // ✅ jednoduché, funguje
  // return 1984;        ← ✅ korektné ale degraduje na O(n)!
  // return super.hashCode(); ← ❌ každý objekt iný hash → duplikáty v sete
}
// HashSet volá: hashCode() → nájde "bucket" → porovná equals()
implements Comparable<T> { compareTo(T o) } potrebné pre TreeSet a TreeMap +
príklad
class Hruska implements Comparable<Hruska> {
  int velkost;

  @Override
  public int compareTo(Hruska o) {
    return Integer.compare(velkost, o.velkost); // ✅
    // return velkost - o.velkost;  ← ❌ môže pretiecť!
  }
}
// compareTo vracia: záporné (this < o), 0 (rovnaké), kladné (this > o)
// TreeSet: objekty s compareTo==0 sú ROVNAKÉ → nie sú pridané
vytvorenie Vytvorenie streamu
collection.stream() sekvenčný stream z kolekcie +
príklad
List<Integer> lst = List.of(1,2,3,4,5);
lst.stream()
   .filter(e -> e % 2 == 0)
   .toList(); // [2, 4]
IntStream.range(0, n) celé čísla 0..n-1 (bez boxovania) +
príklad
IntStream.range(0, 5).forEach(System.out::print);
// 01234

IntStream.rangeClosed(1, 5).forEach(System.out::print);
// 12345

// Konverzia na Stream<Integer>
IntStream.range(0, 100).boxed().toList();
Stream.iterate(seed, pred, next) generuje sekvenciu kým platí predikát +
príklad
// Bez predikátu – rastúce "a", treba limit()
Stream.iterate("", s -> "a"+s).limit(10).toList();
// [, a, aa, aaa, aaaa, aaaaa, aaaaaa, aaaaaaa, aaaaaaaa, aaaaaaaaa]

// Mocniny 2 menšie ako 1 000 000 (2 spôsoby)
Stream.iterate(1L, n -> 2*n).takeWhile(n -> n < 1_000_000).toList();
Stream.iterate(1L, n -> n < 1_000_000, n -> 2*n).toList();
// [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288]

// Delenie desiatimi (postupné odrezávanie číslic sprava)
Stream.iterate(918972645, n -> n > 0, n -> n/10).toList();
// [918972645, 91897264, 9189726, 918972, 91897, 9189, 918, 91, 9]

// Samotné číslice odzadu
Stream.iterate(918972645, n -> n > 0, n -> n/10).map(n -> n%10).toList();
// [5, 4, 6, 2, 7, 9, 8, 1, 9]

// Zloženie späť pomocou reduce
Stream.iterate(918972645, n -> n > 0, n -> n/10)
      .map(n -> n%10)
      .reduce((a, y) -> 10*a + y); // Optional[546279819]

Stream.iterate(918972645, n -> n > 0, n -> n/10)
      .map(n -> n%10)
      .reduce(0, (a, y) -> 10*a + y); // 546279819
Stream.concat(s1, s2) spojí dva streamy za sebou +
príklad — kombinatorika
// Kombinácie bez opakovania – základ je Stream.concat
Stream<String> kbo(int k, int n) {
  if (k > n) return Stream.of();
  if (k == 0) return Stream.of("");
  return Stream.concat(
    kbo(k, n-1),
    kbo(k-1, n-1).map(s -> s + (n-1))
  );
}
intermediate Intermediate operácie — lazy, vracajú Stream
💡 Intermediárne operácie sa nevykonajú, kým nezavoláš terminálnu operáciu (toList, forEach, count…). Hovorí sa tomu lenivosť (laziness).
.filter(e -> podmienka) ponechá iba prvky spĺňajúce podmienku +
príklad
lst.stream()
   .filter(e -> e % 2 == 0)
   .forEach(System.out::print);
// 02468101214...

lst.stream().filter(e -> e > 0).count(); // 99
.map(e -> transformácia) transformuje každý prvok na niečo iné +
príklad
lst.stream()
   .map(e -> e*e)
   .forEach(System.out::print);
// 01491625...

// Zmena typu: Integer → String
lst.stream()
   .map(x -> Integer.toString(x))
   .toList(); // ["0","1","2",...]
.flatMap(e -> Stream) rozbalí Stream<Stream<T>> na Stream<T> +
príklad
// Splošti vnorené zoznamy
List<List<String>> l2 = List.of(
    List.of("Palo", "Jana"),
    List.of("Peter", "Kamil"));
l2.stream()
  .flatMap(Collection::stream)
  .toList();
// [Palo, Jana, Peter, Kamil]

// IntStream.flatMap – triangulárna matica
IntStream.range(0,5)
  .flatMap(e -> IntStream.range(0, e))
  .forEach(System.out::print);
// 001012012301234
.sorted() / .sorted(Comparator) zoradenie (prirodzené alebo vlastné) +
príklad
lst.stream().sorted().toList(); // prirodzené (Comparable)

// Zostupne
lst.stream().sorted((a, b) -> Integer.compare(b, a)).toList();

// Podľa dĺžky reťazca
words.stream()
     .sorted(Comparator.comparingInt(String::length))
     .toList();
.distinct() / .limit(n) / .skip(n) bez duplikátov / prvých n / preskočiť n +
príklad
lst.stream().map(i -> i%10).distinct().toList();
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

lst.stream().limit(5).toList();  // [0, 1, 2, 3, 4]
lst.stream().skip(95).toList(); // [95, 96, 97, 98, 99]
.takeWhile(pred) / .dropWhile(pred) berie / háže kým platí podmienka (Java 9) +
príklad
Stream.iterate(1L, n -> n*2)
      .takeWhile(n -> n < 100)
      .toList();
// [1, 2, 4, 8, 16, 32, 64]
.mapToObj(e -> ...) / .boxed() konverzia IntStream → Stream<T> +
príklad
// IntStream → Stream<Character>
IntStream.range(0,10)
  .mapToObj(e -> (char)('@'+e))
  .forEach(System.out::print);
// @ABCDEFGHI

// IntStream → Stream<Integer>
IntStream.range(0, 100).boxed().toList();
terminal Terminal operácie — spotrebujú stream
⚠️ Stream sa dá použiť IBA RAZ. Po zavolaní terminálnej operácie je vyčerpaný.
.toList() / .collect(Collectors.toList()) zberie do List +
príklad
List<Integer> even = lst.stream()
    .filter(e -> e % 2 == 0)
    .toList(); // Java 16+

// Staršia verzia:
.collect(Collectors.toList())
.forEach(e -> ...) / .forEachOrdered(...) vykoná akciu pre každý prvok +
príklad
lst.stream().forEach(System.out::println);
lst.stream().forEach(e -> System.out.println(e + e));

// forEachOrdered: zachová poradie aj pri parallelStream
lst.parallelStream().forEachOrdered(System.out::print);
.count() počet prvkov v streame +
príklad
lst.stream().count();                    // 100
lst.stream().filter(e -> e > 0).count(); // 99
.reduce(identity, (a,b) -> ...) zloží stream na jednu hodnotu +
príklad
lst.stream().reduce(0, Integer::sum);       // 4950
lst.stream().reduce(0, (a, b) -> a + b);    // 4950

// Bez identity → vracia Optional
lst.stream().reduce(Integer::max);          // Optional[99]

// Konkatenácia stringov
lst.stream()
   .map(x -> Integer.toString(x))
   .reduce("", (acc, x) -> acc + x);
// "0123456789101112..."
.findFirst() / .findAny() vráti Optional s prvým / ľubovoľným prvkom +
príklad
lst.stream().findFirst().isPresent(); // true
lst.stream().findFirst().get();       // 0
lst.stream().findFirst().orElse(-1); // 0 alebo -1 ak prázdny

// findAny je rýchlejší pri parallelStream, ale nedeterministický
lst.parallelStream().findAny().get(); // nejaké číslo
.min(cmp) / .max(cmp) minimum / maximum – vracia Optional +
príklad
lst.stream().min(Integer::compare).get(); // 0
lst.stream().max(Integer::compare).get(); // 99
.anyMatch(p) / .allMatch(p) / .noneMatch(p) existenčné dotazy (∃ / ∀ / ¬∃) +
príklad
lst.stream().anyMatch(e -> e == 51);    // true  — existuje 51?
lst.stream().anyMatch(e -> e*e == e);   // true  — ∃e: e²=e (e=0 alebo 1)
lst.stream().noneMatch(e -> e > 100);  // true  — žiaden > 100
lst.stream().noneMatch(e -> e+e == e);  // false — 0+0=0
lst.stream().allMatch(e -> e >= 0);   // true  — všetky >= 0
collectors Collectors — do .collect()
Collectors.toSet() zberie do HashSet (bez duplikátov) +
príklad
lst.stream()
   .map(x -> x%10)
   .collect(Collectors.toSet());
// {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Collectors.toMap(keyFn, valFn) zberie do Map; ⚠️ chyba pri duplicitných kľúčoch +
príklad
// Číslo → jeho binárna reprezentácia
lst.stream().collect(Collectors.toMap(
    x -> Integer.toBinaryString(x),   // kľúč
    x -> Integer.toBinaryString(x).length() // hodnota
));
// {"0"=1, "1"=1, "10"=2, ...}

// ⚠️ duplicitný kľúč → hodí výnimku:
// Duplicate key 0 (attempted merging values 0 and 10)
lst.stream().collect(Collectors.toMap(x -> x%10, x -> x)); // CHYBA!
Collectors.groupingBy(e -> ...) rozdelí do skupín podľa kľúča → Map<K, List<E>> +
príklad
// Skupiny podľa počtu číslic
lst.stream().collect(Collectors.groupingBy(
    e -> String.valueOf(e).length()));
// {1=[0,1,2,...,9], 2=[10,11,...,99]}

// Skupiny po dekádach
lst.stream().collect(Collectors.groupingBy(
    e -> e/10, Collectors.counting()));
// {0=10, 1=10, ..., 9=10}

// Priemerná známka podľa kurzu
db.stream().collect(Collectors.groupingBy(
    Student::courseID,
    Collectors.averagingDouble(Student::grade)));
// {C1=2.5, C2=3.0, ...}
Collectors.partitioningBy(pred) rozdelí na 2 skupiny: true / false +
príklad
lst.stream().collect(
    Collectors.partitioningBy(e -> e % 3 == 0));
// {false=[1,2,4,5,7,8,...], true=[0,3,6,9,12,...]}
Collectors.reducing(identity, mapper, combiner) generalizovaný reduce pre collect() +
príklad
Long count = lst.parallelStream().collect(
    Collectors.reducing(0L, e -> 1L, Long::sum));
// 100

Long sum = lst.parallelStream().collect(
    Collectors.reducing(0L, e -> new Long(e), Long::sum));
// 4950

// Jednoduchší ekvivalent pre int:
int s = lst.parallelStream().reduce(0, Integer::sum); // 4950
.entrySet().stream() → max/min cez mapu získanie max/min záznamu z výslednej mapy +
príklad — kurz s najhorším priemerom
db.stream()
  .collect(Collectors.groupingBy(
      Student::courseID,
      Collectors.averagingDouble(Student::grade)))
  .entrySet().stream()
  .max(Comparator.comparingDouble(Map.Entry::getValue))
  .get().getKey();  // napr. "C2"
// .getValue() by vrátilo priemer
kombinatorika Kombinatorika — binaries, perms, kbo, kso, vso
binaries(n) → 2ⁿ reťazcov binárne vektory {0,1}ⁿ pomocou flatMap +
implementácia
Stream<String> binaries(int n) {
  return (n == 0)
    ? Stream.of("")
    : binaries(n-1).flatMap(
        s -> Stream.of(s+"0", s+"1"));
}
binaries(3).toList();
// [000, 001, 010, 011, 100, 101, 110, 111]
perms(n) → n! permutácií vkladanie čísla na každú pozíciu +
implementácia
Stream<String> perms(int n) {
  return (n <= 0)
    ? Stream.of("")
    : perms(n-1).flatMap(s ->
        IntStream.range(0, n)
          .mapToObj(i -> insert(i, n, s)));
}
static String insert(int i, int n, String s) {
  return s.substring(0,i) + n + s.substring(i);
}
perms(3).count(); // 6  (= 3!)
kbo(k, n) → C(n,k) kombinácií bez opakovania Stream.concat – buď bez n-1, alebo s n-1 +
implementácia
Stream<String> kbo(int k, int n) {
  if (k > n) return Stream.of();    // base: k > n
  if (k == 0) return Stream.of("");  // base: prázdna kombinácia
  return Stream.concat(
    kbo(k, n-1),                         // bez prvku (n-1)
    kbo(k-1, n-1).map(s -> s + (n-1))  // s prvkom (n-1)
  );
}
kbo(2, 4).toList();
// [01, 02, 12, 03, 13, 23]  — 6 = C(4,2)
kso(k, n) → C(n+k-1,k) kombinácií s opakovaním podobné kbo, ale kso(k-1, n) nie kso(k-1, n-1) +
implementácia
Stream<String> kso(int k, int n) {
  if (n == 0) return Stream.of();
  if (k == 0) return Stream.of("");
  return Stream.concat(
    kso(k, n-1),
    kso(k-1, n).map(s -> s + (n-1))  // kso(k-1, n) – nie n-1!
  );
}
kso(2, 3).toList();
// [01, 11, 02, 12, 22]
vso(n, k) → kⁿ variácií s opakovaním flatMap s IntStream.range – každý prvok rozvetví k-krát +
implementácia
static Stream<String> vso(int n, int k) {
  return (n == 0)
    ? Stream.of("")
    : vso(n-1, k).flatMap(s ->
        IntStream.range(0, k)
          .mapToObj(i -> s + i));
}
vso(2, 3).toList();
// [00, 01, 02, 10, 11, 12, 20, 21, 22]  — 9 = 3²
lambdy Lambdy a functional interface
💡 Pre lambdu potrebuješ typ s presne jednou abstraktnou metódou (functional interface).
@FunctionalInterface interface Operacia { int apply(int a, int b); } vlastný interface pre lambdu +
príklad
@FunctionalInterface
interface Operacia {
  int apply(int a, int b);
}

Operacia sucet = (a, b) -> a + b;
Operacia max   = (int a, int b) -> Math.max(a, b);

System.out.println(sucet.apply(2, 3)); // 5
System.out.println(max.apply(2, 3));   // 3
public class Matfyzak { ... } kompletná šablóna: interface + lambda + volanie +
príklad — celé v jednom súbore
public class Matfyzak {

  @FunctionalInterface
  interface Pozdrav {
    void run(String meno);
  }

  public static void main(String[] args) {
    Pozdrav p = m -> System.out.println("Ahoj " + m + "!");
    p.run("Matej");
  }
}
Runnable / Comparator / Predicate<T> často netreba vlastný interface, stačia vstavané +
príklad
Runnable r = () -> System.out.println("bezi");
Comparator<String> cmp = (a, b) -> a.length() - b.length();
Predicate<Integer> parne = x -> x % 2 == 0;
i/o I/O streamy — byte vs char
💡 I/O stream je jednosmerný sekvenčný tok dát. Nemá nič spoločné so Stream API z Java 8.
InputStream / OutputStream 8-bitový byte stream: súbory, pole bajtov, pipe, sekvencia streamov +
základné metódy
abstract class InputStream {
  int read();                              // 1 byte alebo -1 pri EOF
  int read(byte[] pole);
  int read(byte[] pole, int offset, int length);
}

abstract class OutputStream {
  void write(int i);
  void write(byte[] pole);
  void write(byte[] pole, int offset, int length);
  void flush();                             // vyprázdni buffer
  void close();                             // zavrie stream
}
FileInputStream / FileOutputStream kopírovanie binárnych dát bajt po bajte alebo blokom +
kopírovanie súboru
try (
  FileInputStream in = new FileInputStream("a.txt");
  FileOutputStream out = new FileOutputStream("b.txt")
) {
  int c;
  while ((c = in.read()) != -1) {
    out.write(c);
  }
} catch (IOException e) {
  e.printStackTrace();
}
Reader / Writer 16-bitový znakový stream; vhodný na text +
základné metódy
abstract class Reader {
  int read();                              // znak alebo -1 pri EOF
  int read(char[] pole);
  int read(char[] pole, int offset, int pocet);
}

abstract class Writer {
  void write(int i);
  void write(char[] pole);
  void write(String retazec);
  void write(String retazec, int offset, int pocet);
}
console Konzola, PrintStream a výnimky
System.in / System.out / System.err konzolový vstup, bežný výstup, chybový výstup +
čítanie jedného znaku
try {
  System.out.println("Klepni znak");
  int tmp = System.in.read();
  if (tmp != -1) {
    char ch = (char) tmp;
    System.out.println("klepol si: " + ch);
  }
} catch (IOException e) {
  System.err.println("chyba pri citani");
  e.printStackTrace();
}
print() / println() / printf() PrintStream rozširuje OutputStream o pohodlný textový výstup +
výstup
System.out.print("bez noveho riadku");
System.out.println("s novym riadkom");
System.out.printf("int = %9d%n", 1234567);
System.out.printf("float = %9.3f%n", 983.6);

// %n je prenositeľný koniec riadku, lepší než pevné "\n"
catch vs throws výnimku buď ošetríš na mieste, alebo propaguješ vyššie +
ošetrenie a propagovanie
static void citajNaMieste() {
  try {
    char c = (char) System.in.read();
  } catch (IOException e) {
    System.err.println(e.getMessage());
  }
}

static void citajVyssie() throws IOException {
  char c = (char) System.in.read();
}

// Java 7: jeden catch pre viac typov
catch (IOException | SQLException e) {
  e.printStackTrace();
}
try-with-resources AutoCloseable zdroje sa zatvoria automaticky +
bez finally
try (
  BufferedReader br = Files.newBufferedReader(Path.of("a.txt"));
  BufferedWriter bw = Files.newBufferedWriter(Path.of("b.txt"))
) {
  String line;
  while ((line = br.readLine()) != null) {
    bw.write(line);
    bw.newLine();
  }
}
text Obaľovanie, BufferedReader, kódovanie a NIO
InputStreamReader + BufferedReader byte stream → char stream → riadky cez readLine() +
čítanie riadkov zo štandardného vstupu
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(in);

int sum = 0;
String line;
while ((line = br.readLine()) != null) {      // Ctrl-D / Ctrl-Z = koniec vstupu
  for (int i = 0; i < line.length(); i++) {
    if (line.charAt(i) == '*') sum++;
  }
}
System.out.println("pocet * = " + sum);
InputStreamReader(..., charset) explicitné kódovanie pri konverzii medzi byte a char streamom +
konverzia UTF-8 → cp1250
try (
  BufferedReader br = new BufferedReader(
      new InputStreamReader(new FileInputStream("a_utf8.txt"), "UTF-8"));
  BufferedWriter bw = new BufferedWriter(
      new OutputStreamWriter(new FileOutputStream("a_cp1250.txt"), "cp1250"))
) {
  String line;
  while ((line = br.readLine()) != null) {
    bw.write(line);
    bw.newLine();
  }
}
BufferedReader + FileReader riadkové načítanie programu zo súboru; prázdne riadky ignoruj +
inštrukcie pre korytnačku
class Instruction {
  String name;
  int value;
}

List<Instruction> program = new ArrayList<>();
List<MovingObject> objects = new ArrayList<>();

void loadProgram(String fileName) {
  try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
    String line;

    while ((line = br.readLine()) != null) {
      line = line.trim();

      if (line.isEmpty()) {
        continue;
      }

      String[] parts = line.split("\\s+");

      if (parts.length >= 1) {
        Instruction ins = new Instruction();
        ins.name = parts[0].toUpperCase();

        if (parts.length >= 2) {
          ins.value = Integer.parseInt(parts[1]);
        }

        program.add(ins);
      }
    }

    // Vytvoríme jednu korytnačku do stredu.
    MovingObject turtle = new MovingObject();
    turtle.x = canvas.getWidth() / 2;
    turtle.y = canvas.getHeight() / 2;
    turtle.radius = 12;
    turtle.color = Color.GREEN;
    turtle.angle = 0;
    turtle.stepSize = 5;

    objects.add(turtle);

    System.out.println("Nacitany program, instrukcii: " + program.size());
  } catch (Exception e) {
    System.out.println("Chyba pri citani suboru: " + fileName);
    e.printStackTrace();
  }
}
Files.readAllLines(Path) java.nio: rýchle načítanie celého textového súboru do List<String> +
riadky + stream operácie
try {
  List<String> lines = Files.readAllLines(Paths.get("subor.txt"));

  lines.forEach(System.out::println);

  int znaky = lines.stream()
      .mapToInt(String::length)
      .sum();

  long hviezdicky = lines.stream()
      .mapToLong(line -> line.chars().filter(ch -> ch == '*').count())
      .sum();
} catch (IOException e) {
  e.printStackTrace();
}
scanner Scanner, oddeľovače a formátovaný výstup
Scanner.hasNextX() / nextX() formátovaný vstup: int, double/float, boolean, tokeny +
čítanie typov zo vstupu
Scanner sc = new Scanner(System.in);

while (true) {
  if (sc.hasNextInt()) {
    System.out.println("Ivstup " + sc.nextInt());
  } else if (sc.hasNextFloat()) {
    System.out.println("Fvstup " + sc.nextFloat());
  } else if (sc.hasNextBoolean()) {
    System.out.println("Bvstup " + sc.nextBoolean());
  } else {
    break;
  }
}
Scanner.useDelimiter("[;\\r\\n]") CSV/oddelené hodnoty; delimiter môže byť regulárny výraz +
priemery z CSV
Scanner sc = new Scanner(new FileInputStream("body.csv"));
sc.useDelimiter("[;\\r\\n]");

double[] sucty = {0, 0, 0, 0, 0, 0};
int pocet = 0;

while (sc.hasNextLine()) {
  sc.next();                              // meno
  sc.next();                              // priezvisko
  int i = 0;
  while (i < sucty.length && sc.hasNextDouble()) {
    sucty[i++] += sc.nextDouble();
  }
  sc.nextLine();
  pocet++;
}
String.split(";") jednoduchá alternatíva k Scanneru pre riadkové CSV +
riadok na časti
String line = "Matej;Novak;19.6;7;0.9";
String[] parts = line.split(";");

String meno = parts[0];
double body = Double.valueOf(parts[2].trim());
Formatter / printf / String.format %[argument_index$][width][.precision]conversion +
formátovaný výstup
Formatter f = new Formatter(System.out);
f.format("boolean = %9b%n", false);
f.format("int = %9d%n", 1234567);
f.format("float = %9.3f%n", 983.6);
f.flush();

String s = String.format("%4$s %3$s %2$s", "a", "b", "c", "d");
// "d c b"
files Súbory, adresáre, serializácia a špeciálne streamy
new File(...) descriptor súboru/adresára; samotný objekt súbor ešte nemusí vytvoriť +
vlastnosti a operácie
String aktDir = System.getProperty("user.dir");

File f = new File("TMP" + File.separator + "a.txt");
f.getAbsolutePath();
f.getName();
f.getParent();
f.exists();
f.isFile();
f.length();
f.lastModified();

f.createNewFile();
f.renameTo(new File("b.txt"));
f.delete();
list() / listFiles() / FilenameFilter výpis a filtrovanie adresára podľa prípony alebo veľkosti +
adresár a filter
File dir = new File(System.getProperty("user.dir"));

String[] javaMena = dir.list((d, name) -> name.endsWith(".java"));
File[] velkeSubory = dir.listFiles((d, name) ->
    new File(d, name).length() > 2048);

if (javaMena != null) {
  for (String meno : javaMena) System.out.println(meno);
}
ObjectOutputStream / ObjectInputStream serializácia: uloženie a spätné načítanie objektu +
save/load objektu
class PiskyStav implements Serializable {
  char[][] plocha = new char[10][10];
  boolean xNaTahu = true;

  void save(String fileName) throws Exception {
    try (ObjectOutputStream out =
        new ObjectOutputStream(new FileOutputStream(fileName))) {
      out.writeObject(this);
    }
  }

  static PiskyStav load(String fileName) throws Exception {
    try (ObjectInputStream in =
        new ObjectInputStream(new FileInputStream(fileName))) {
      return (PiskyStav) in.readObject();
    }
  }
}
RandomAccessFile priamy prístup k pozícii v súbore +
seek a append
File f = new File("data.bin");
try (RandomAccessFile raf = new RandomAccessFile(f, "rw")) {
  raf.seek(0);
  char ch = raf.readChar();

  raf.seek(f.length());                   // skok na koniec
  raf.writeChars("<" + ch + ">");
  long pos = raf.getFilePointer();
}
SequenceInputStream / ZipOutputStream zreťazenie vstupov a zápis ZIP archívu +
sekvencia a zip
// a.txt + b.txt ako jeden vstup
try (SequenceInputStream s = new SequenceInputStream(
    new FileInputStream("a.txt"),
    new FileInputStream("b.txt"))) {
  int c;
  while ((c = s.read()) != -1) System.out.write(c);
}

// ZIP: pre každý súbor putNextEntry(), write(), closeEntry()
try (ZipOutputStream out =
    new ZipOutputStream(new FileOutputStream("file.zip"))) {
  out.putNextEntry(new ZipEntry("a.txt"));
  Files.copy(Path.of("a.txt"), out);
  out.closeEntry();
}
threads Vlákna — vytvorenie, start/run, sleep, yield, join
💡 Každý Java program má aspoň `main thread`. Nové vlákno spustíš cez `start()`, nie priamym volaním `run()`.
class X extends Thread vlastné vlákno prepíše metódu run() +
extends Thread
class SimpleThread extends Thread {
  private final int id;

  SimpleThread(int id) {
    this.id = id;
  }

  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      System.out.println("#" + id + ": " + i);
    }
  }
}

for (int i = 0; i < 15; i++) {
  new SimpleThread(i).start();       // start vytvorí nové vlákno
}
new Thread(Runnable).start() ak už dedíš inú triedu, použi Runnable/lambdu +
Runnable
Runnable job = () -> {
  Thread t = Thread.currentThread();
  System.out.println(t.getName() + " bezi");
};

Thread t = new Thread(job, "worker-1");
t.start();

// t.run();  // nie je nové vlákno, iba obyčajné volanie metódy
sleep(ms) / yield() / setPriority(...) plánovač rozhoduje, kedy vlákno naozaj pobeží +
pozastavenie a priority
try {
  Thread.sleep(1000);              // uspí aktuálne vlákno na 1 s
} catch (InterruptedException e) {
  Thread.currentThread().interrupt();
}

Thread.yield();                     // dobrovoľne uvoľní CPU pre iné runnable vlákna

Thread low = new Thread(job);
low.setPriority(Thread.MIN_PRIORITY);
Thread high = new Thread(job);
high.setPriority(Thread.MAX_PRIORITY);
join() / interrupt() čakanie na dokončenie vlákna a korektné zastavenie +
čakanie na vlákno
Thread worker = new Thread(() -> {
  while (!Thread.currentThread().isInterrupted()) {
    pracujKrok();
  }
});

worker.start();
worker.join();                            // aktuálne vlákno čaká na worker

worker.interrupt();                       // pošli žiadosť o ukončenie
states Stavy vlákna, monitor, sleep vs wait
new / runnable / blocked / waiting / dead typické stavy z prednášky +
prehľad
// new       - objekt Thread existuje, ale ešte nebol start()
// runnable  - môže bežať, keď mu plánovač pridelí CPU
// blocked   - čaká na monitor/synchronized, I/O alebo pipe
// waiting   - čaká cez wait(), join(), prípadne notify/notifyAll
// timed     - spí cez sleep(ms) alebo wait(ms)
// dead      - run() skončila

Thread.State st = worker.getState();
sleep(ms) vs objekt.wait(ms) wait musí byť v synchronized bloku a uvoľňuje monitor +
rozdiel
private static final Object lock = new Object();

Thread.sleep(5000);               // netreba synchronized, monitor neuvoľňuje

synchronized (lock) {
  lock.wait(7000);                  // čaká na notify alebo timeout
}

synchronized (lock) {
  lock.notify();                     // zobudí jedno čakajúce vlákno
  // lock.notifyAll();              // zobudí všetky čakajúce vlákna
}
monitor objektu - vstup do kritickej oblasti získanie monitoru = vstup do kritickej oblasti; naraz iba jedno vlákno +
kritická oblasť
private final Object lock = new Object();
private int counter = 0;

void inc() {
  synchronized (lock) {              // vstup do kritickej oblasti cez monitor lock
    counter++;                        // čítanie + zmena + zápis ako jeden chránený celok
  }
}
sync Synchronizácia — synchronized, volatile, wait/notify
⚠️ `volatile` rieši viditeľnosť hodnoty medzi vláknami, ale zo zloženej operácie ako `x++` neurobí kritickú sekciu.
synchronized method nie je možné súčasne volať synchronized metódy toho istého objektu +
semafór ako monitor
class SynchronizedSemaphore {
  private int semaphore = 0;

  public synchronized boolean available() {
    return semaphore == 0;
  }

  public synchronized void acquire() {
    ++semaphore;
  }

  public synchronized void release() {
    --semaphore;
  }
}
synchronized (objekt) { ... } synchronizovaná sekcia pre konkrétny zámok +
check + acquire spolu
while (true) {
  synchronized (semaphore) {
    if (semaphore.available()) {
      semaphore.acquire();
      try {
        pouziZdroj();
      } finally {
        semaphore.release();
      }
    }
  }
}
synchronized / wait() / notify() pasívne čakanie vlákna namiesto prázdneho while cyklu +
pause / run
private boolean suspended = false;

synchronized void waitIfSuspended() throws InterruptedException {
  while (suspended) wait();        // while, nie if: chráni pred falošným zobudením
}

synchronized void pauseThread() {
  suspended = true;
}

synchronized void restartThread() {
  if (suspended) {
    suspended = false;
    notify();                           // zobudí vlákno vo waitIfSuspended()
  }
}
BoundedBuffer<E> producer-consumer: plný buffer čaká v put, prázdny čaká v get +
ohraničený buffer
class BoundedBuffer<E> {
  private final Object[] buf;
  private int in = 0, out = 0, count = 0;

  BoundedBuffer(int size) { buf = new Object[size]; }

  public synchronized void put(E o) throws InterruptedException {
    while (count == buf.length) wait();
    buf[in] = o;
    in = (in + 1) % buf.length;
    count++;
    notifyAll();
  }

  public synchronized E get() throws InterruptedException {
    while (count == 0) wait();
    E o = (E) buf[out];
    buf[out] = null;
    out = (out + 1) % buf.length;
    count--;
    notifyAll();
    return o;
  }
}
comm Komunikácia medzi vláknami — pipe, Semaphore, shared memory
PipedWriter / PipedReader kanál: jedno vlákno píše, druhé číta +
rúra
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(out);

new Thread(() -> {
  try {
    out.write("sprava\n");
    out.close();
  } catch (IOException e) {
    e.printStackTrace();
  }
}).start();

new Thread(() -> {
  try {
    int c;
    while ((c = in.read()) != -1) System.out.print((char) c);
  } catch (IOException e) {
    e.printStackTrace();
  }
}).start();
PipedInputStream + ObjectOutputStream keď správa nie je char, môžeš serializovať objekt do byte pipe +
objekt cez pipe
record Message(int from, String text) implements Serializable {}

PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);

ObjectOutputStream oos = new ObjectOutputStream(pos);
ObjectInputStream ois = new ObjectInputStream(pis);

oos.writeObject(new Message(1, "ahoj"));
Message msg = (Message) ois.readObject();
java.util.concurrent.Semaphore obmedzený počet zdrojov; fér semafór cez new Semaphore(N, true) +
lopaty
Semaphore sem = new Semaphore(N, true);

class Robotnik extends Thread {
  public void run() {
    while (odrobene < 10000) {
      boolean acquired = false;
      try {
        sem.acquire();                    // čaká na lopatu
        acquired = true;
        pracuj();
      } catch (InterruptedException e) {
        interrupt();
        break;
      } finally {
        if (acquired) sem.release();       // vráti zdroj
      }
    }
  }
}
javafx JavaFX aplikácia — Application, Stage, Scene, Node
💡 `launch(args)` vytvorí JavaFX runtime a zavolá `start(Stage)`. Do `main` už zvyčajne nič ďalšie nepíš.
extends Application minimálna JavaFX aplikácia +
šablóna
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {
  @Override
  public void start(Stage stage) {
    Button btn = new Button("Press me!");
    Pane root = new Pane(btn);
    Scene scene = new Scene(root, 400, 400, Color.ORANGE);

    stage.setTitle("JavaFX demo");
    stage.setScene(scene);
    stage.show();
  }

  public static void main(String[] args) {
    launch(args);
  }
}
Scene(Parent root, w, h, Paint fill) scéna je koreň stromu prvkov typu Node/Parent +
vrstvy
Parent root = new Pane();
Scene scene1 = new Scene(root);
Scene scene2 = new Scene(root, 400, 400);
Scene scene3 = new Scene(root, 400, 400, Color.BLACK);

// Node: Button, Label, Canvas, Circle, Rectangle, ImageView...
// Parent/Pane drží deti cez getChildren().add(...)
setStyle / setBackground keď komponent nevidno, zafarbi ho a skontroluj rozmery +
debug štýlu
Pane root = new Pane();

root.setStyle("-fx-background-color: #FFFF00");

root.setBackground(new Background(
    new BackgroundFill(Color.YELLOW, null, null)));

// CSS zo súboru:
scene.getStylesheets().add(
    getClass().getResource("application.css").toExternalForm());
new Stage() viac okien v jednej aplikácii +
multi-stage
public void start(Stage primaryStage) {
  primaryStage.setTitle("Primary");
  primaryStage.setScene(new Scene(getMainPanel(), 400, 400));
  primaryStage.show();

  Stage second = new Stage();
  second.setTitle("Timeline");
  second.setScene(new Scene(getTimeLinePanel(), 400, 400));
  second.show();
}
layout Layouty a controls — BorderPane, GridPane, HBox, Button
BorderPane typický rámec aplikácie: top, center, bottom, left, right +
scéna hry
BorderPane bp = new BorderPane();

HBox labelPane = new HBox(
    new Label("Score:"), lbScore = new Label("0"),
    new Label("Elapsed time:"), lbTime = new Label("0"),
    new Label("Next:"), lbOnMove = new Label("o"));
labelPane.setSpacing(20);

HBox buttons = new HBox(
    btnLoad = new Button("Load"),
    btnSave = new Button("Save"),
    btnQuit = new Button("Quit"));
buttons.setSpacing(50);

bp.setTop(labelPane);
bp.setCenter(pg);                         // GridPane alebo Canvas
bp.setBottom(buttons);
FlowPane / HBox / VBox / StackPane jednoduché rozkladanie komponentov +
príklady
FlowPane flow = new FlowPane(
    new Button("Prvý"),
    new Button("Druhý"),
    new Button("Tretí"));

HBox row = new HBox(10, new Label("Meno:"), new TextField());
VBox col = new VBox(8, row, new Button("OK"));

StackPane centered = new StackPane(new Label("stred"));
StackPane.setAlignment(centered.getChildren().get(0), Pos.CENTER);
GridPane.add(node, col, row) mriežka pre šachovnice, pexeso, hru 15, piškvorky +
4x4 tlačidlá
final int SIZE = 4;
GridPane gp = new GridPane();

for (int i = 0; i < SIZE * SIZE; i++) {
  Button b = (i == SIZE * SIZE - 1)
      ? new Button("")
      : new Button("" + (i + 1));
  gp.add(b, i % SIZE, i / SIZE);
}
Button.setGraphic(ImageView) Button môže niesť obrázok namiesto textu +
ikona v bunke
class PiskyCell extends Button {
  int i, j;
  ImageView imageX = new ImageView(new Image("file:x.gif"));

  PiskyCell(int i, int j) {
    this.i = i;
    this.j = j;
    setPrefSize(50, 50);
    setOnAction(event -> setGraphic(imageX));
  }
}
Pane + ImageView obrázok ako samostatný Node; vieš ho posúvať, rotovať a odstraňovať +
obrázok v Pane
Pane layer = new Pane();

// zo súboru vedľa projektu / working directory:
Image turtleImage = new Image("file:turtle.png");

// alebo z resources pri triede:
Image shipImage = new Image(
    getClass().getResource("namornik.gif").toExternalForm());

ImageView turtleView = new ImageView(turtleImage);
turtleView.setFitWidth(30);
turtleView.setFitHeight(30);
turtleView.setPreserveRatio(true);

// ImageView je Node, preto sa v Pane ovláda cez layout a rotate:
turtleView.setLayoutX(x - turtleView.getFitWidth() / 2);
turtleView.setLayoutY(y - turtleView.getFitHeight() / 2);
turtleView.setRotate(angle + 90);

layer.getChildren().add(turtleView);

// neskôr pri pohybe:
turtleView.setLayoutX(x - turtleView.getFitWidth() / 2);
turtleView.setLayoutY(y - turtleView.getFitHeight() / 2);
turtleView.setRotate(angle + 90);

// keď objekt skončí:
layer.getChildren().remove(turtleView);
events Eventy, myš, klávesnica a FXML controller
setOnAction(event -> ...) lambda namiesto anonymného EventHandlera +
button handler
Button btn = new Button("OK");

btn.setOnAction(event -> {
  System.out.println("klik");
  event.consume();
});

// starší ekvivalent:
btn.setOnAction(new EventHandler<ActionEvent>() {
  @Override
  public void handle(ActionEvent event) {
    System.out.println("klik");
  }
});
scene.setOnMouseMoved(...) / setOnMouseClicked(...) súradnice myši, kreslenie stopy alebo bodov cez Polyline +
myš
Vector<Double> positions = new Vector<>();

private void addPoint(MouseEvent event) {
  if (positions.size() >= 200) {
    positions.removeElementAt(0);
    positions.removeElementAt(0);
  }
  positions.add(event.getX());
  positions.add(event.getY());
  p.paint();
  event.consume();
}

scene.setOnMouseMoved(event -> addPoint(event));    // stopa pri pohybe
scene.setOnMouseClicked(event -> addPoint(event));  // bod po kliknutí

Polyline pl = new Polyline();
pl.setStroke(Color.YELLOW);
pl.getPoints().addAll(positions.toArray(new Double[]{}));
scene.setOnKeyPressed(...) ovládanie šípkami; scéna musí mať focus +
klávesnica
int x = 100, y = 100;

scene.setOnKeyPressed(event -> {
  if (event.getCode() == KeyCode.UP) y -= 5;
  if (event.getCode() == KeyCode.DOWN) y += 5;
  if (event.getCode() == KeyCode.LEFT) x -= 5;
  if (event.getCode() == KeyCode.RIGHT) x += 5;
  p.paint();
  event.consume();
});
@FXML Controller FXML prvky sa napoja na polia/metódy v controlleri +
controller kostra
public class Controller {
  @FXML private TextField input;
  @FXML private Label output;

  @FXML
  private void initialize() {
    output.setText("ready");
  }

  @FXML
  private void handleButton() {
    output.setText(input.getText());
  }
}
draw Kreslenie — Shapes, Pane vs Canvas, GraphicsContext
Pane + Shape nodes každý tvar je Node, môže mať vlastné eventy +
prekreslenie cez getChildren()
class ShapePane extends Pane {
  void paint() {
    double w = getWidth();
    double h = getHeight();
    getChildren().clear();

    Ellipse c = new Ellipse(w / 2, h / 2, w / 4, h / 4);
    c.setStroke(Color.BLACK);
    c.setFill(Color.RED);

    Rectangle r = new Rectangle(w / 2, h / 2, w / 4, h / 4);
    r.setArcWidth(10);
    r.setArcHeight(10);
    r.setOnMouseClicked(ev -> System.out.println("rectangle"));

    getChildren().addAll(c, r);
  }
}
Canvas + GraphicsContext kreslíš pixely do jedného Node; dobré pre hry a veľa objektov +
základné kreslenie
Canvas canvas = new Canvas(700, 700);
canvas.setFocusTraversable(true); // ak má Canvas zachytávať klávesnicu
GraphicsContext gc = canvas.getGraphicsContext2D();

gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.setFill(Color.RED);
gc.setStroke(Color.BLUE);
gc.setLineWidth(3);

gc.strokeLine(x, y, x, y + 30);
gc.fillOval(x += 50, y, 30, 30);
gc.strokeOval(x += 50, y, 30, 30);
gc.fillRect(x += 50, y, 30, 30);
gc.strokeText("STRED", 335, 370);
gc.drawImage(new Image(...), x, y, w, h) obrázky v Canvas; veľkosť môžeš prispôsobiť bunke +
obrázky
Image img = new Image(
    getClass().getResource("namornik.gif").toExternalForm());
gc.drawImage(img, x, y);

// Canvas si nepamätá ImageView/Node; obrázok je len nakreslený pixelmi.
// Pri animácii najprv vymaž scénu a potom znova vykresli všetko zo stavu.
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(img, x - img.getWidth() / 2, y - img.getHeight() / 2);

// verzia s cieľovým rozmerom:
Image imageX = new Image("file:x.gif", cellWidth() - 2, cellHeight() - 2, false, false);
gc.drawImage(imageX, col * cellWidth() + 1, row * cellHeight() + 1);

// verzia s explicitným rozmerom pri kreslení:
gc.drawImage(img, x, y, 40, 40);
thread Animácia a vlákna — Timeline, AnimationTimer, Platform.runLater
⚠️ GUI sa má meniť na JavaFX Application Thread. Z bežného `Thread` posielaj zmeny cez `Platform.runLater(...)`.
Timeline + KeyFrame periodické kroky, napr. každých 10 ms alebo každú sekundu +
animácia
Timeline animation = new Timeline(
    new KeyFrame(Duration.millis(10), e -> {
      balls.update();
      balls.paintBallPane();
    }));

animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
AnimationTimer.handle(long now) volá sa pri render frame; vhodné pre plynulé hry +
frame loop
AnimationTimer at = new AnimationTimer() {
  long last = 0;
  int frames = 0;

  @Override
  public void handle(long now) {       // nanosekundy
    if (now > last + 1_000_000_000L) {
      System.out.println(frames + " fps");
      frames = 0;
      last = now;
    }
    balls.update();
    balls.paintBallPane();
    frames++;
  }
};
at.start();
new Thread(...).start() + Platform.runLater ak počítaš mimo GUI vlákna, GUI update zaradíš do event fronty +
bezpečný update GUI
Thread th = new Thread(() -> {
  while (true) {
    double x = computeX();
    double y = computeY();

    Platform.runLater(() -> {
      node.setTranslateX(x);
      node.setTranslateY(y);
    });

    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      break;
    }
  }
});
th.start();
game Hracie plochy, škálovanie, bindings, save/load a timer
1 veľký Canvas musíš riešiť transformáciu pixel ↔ bunka +
transformácie
class Piskyground extends Canvas {
  private double cellWidth()  { return getWidth() / SIZE; }
  private double cellHeight() { return getHeight() / SIZE; }

  private int getRow(double pixelY) { return (int) (pixelY / cellHeight()); }
  private int getCol(double pixelX) { return (int) (pixelX / cellWidth()); }

  Piskyground() {
    setFocusTraversable(true); // Canvas potom vie prijímať KeyPressed eventy

    setOnMouseClicked(event -> {
      int col = getCol(event.getX());
      int row = getRow(event.getY());
      if (ps.playground[col][row] != 0) return;
      ps.playground[col][row] = ps.nextPlayerIsX ? 1 : -1;
      paintCell(col, row);
      ps.nextPlayerIsX = !ps.nextPlayerIsX;
    });
  }
}
GridPane + Button / Canvas bunky každá bunka si pamätá svoje i,j a má lokálny handler +
grid buniek
class Piskyground extends GridPane {
  Piskyground() {
    for (int i = 0; i < SIZE; i++) {
      for (int j = 0; j < SIZE; j++) {
        add(new PiskyCell(i, j), j, i);
      }
    }
  }
}

class PiskyCell extends Canvas {
  int i, j;
  PiskyCell(int i, int j) {
    this.i = i;
    this.j = j;
    setOnMouseClicked(event -> {
      if (ps.playground[i][j] != 0) return;
      ps.playground[i][j] = ps.nextPlayerIsX ? 1 : -1;
      paintCell();
      ps.nextPlayerIsX = !ps.nextPlayerIsX;
    });
  }
}
widthProperty().bind(...) škálovateľný Canvas: pri zmene rozmeru prekresli +
resizable canvas
class Playground extends Canvas {
  Playground() {
    widthProperty().addListener(event -> paint());
    heightProperty().addListener(event -> paint());
  }

  private void paint() {
    double w = getWidth(), h = getHeight();
    GraphicsContext gc = getGraphicsContext2D();
    gc.clearRect(0, 0, w, h);
    for (int i = 0; i <= SIZE; i++) {
      gc.strokeLine(0, i * h / SIZE, w, i * h / SIZE);
      gc.strokeLine(i * w / SIZE, 0, i * w / SIZE, h);
    }
  }
}

Pane p = new Pane(pg);
pg.widthProperty().bind(p.widthProperty());
pg.heightProperty().bind(p.heightProperty());
Properties & Bindings hodnota sa dopočíta automaticky z inej property +
real bindings
DoubleProperty polomer = new SimpleDoubleProperty();
DoubleProperty priemer = new SimpleDoubleProperty();
DoubleProperty obvod = new SimpleDoubleProperty();

priemer.bind(polomer.multiply(2));
obvod.bind(polomer.multiply(2).multiply(Math.PI));

NumberBinding stvorec = Bindings.multiply(polomer, polomer);
DoubleProperty obsah = new SimpleDoubleProperty();
obsah.bind(stvorec.multiply(Math.PI));

polomer.set(1.5);
// priemer=3.0, obvod≈9.42, obsah≈7.07
layoutBoundsProperty().addListener(...) pri zmene veľkosti GridPane prepočítaj veľkosť buniek +
škálovanie button gridu
gp.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
  double cellHeight = newBounds.getHeight() / ROWS;
  double cellWidth = newBounds.getWidth() / COLS;

  for (final Node child : gp.getChildren()) {
    final Control tile = (Control) child;
    tile.setPrefSize(cellWidth, cellHeight);
  }
});

imageX.fitWidthProperty().bind(widthProperty().subtract(4));
imageX.fitHeightProperty().bind(heightProperty().subtract(4));
Serializable state + Save/Load serializuj stav hry, nie JavaFX objekty ako Image alebo Node +
konfigurácia hry
class PiskyState implements Serializable {
  int[][] playground = new int[SIZE][SIZE];
  boolean nextPlayerIsX = false;
  long elapsedTime = 0;
}

btnLoad.setOnAction(event -> {
  try (ObjectInputStream in =
      new ObjectInputStream(new FileInputStream("p.cfg"))) {
    ps = (PiskyState) in.readObject();
    pg.paintAll();
  } catch (Exception e) {
    e.printStackTrace();
  }
});

btnSave.setOnAction(event -> {
  try (ObjectOutputStream out =
      new ObjectOutputStream(new FileOutputStream("p.cfg"))) {
    out.writeObject(ps);
  } catch (Exception e) {
    e.printStackTrace();
  }
});
Timeline ako timer hry každú sekundu zvýš elapsedTime a aktualizuj Label +
časovač
public class BasicGame extends Application {
  State ps = new State();

  Label lbTime = new Label("0");
  Button btnQuit = new Button("Quit");

  @Override
  public void start(Stage stage) {
    BorderPane root = new BorderPane();

    HBox top = new HBox(
        new Label("Elapsed time: "),
        lbTime
    );

    HBox bottom = new HBox(btnQuit);

    root.setTop(top);
    root.setBottom(bottom);

    // časomiera
    Timeline tl = new Timeline();
    tl.setCycleCount(Timeline.INDEFINITE);
    tl.getKeyFrames().add(new KeyFrame(Duration.seconds(1), event -> {
      ps.elapsedTime++;
      lbTime.setText("" + ps.elapsedTime);
    }));
    tl.play();

    // quit button
    btnQuit.setOnAction(event -> Platform.exit());

    Scene scene = new Scene(root, 400, 400);
    stage.setScene(scene);
    stage.show();
  }
}

class State {
  long elapsedTime = 0;
}
⚠️ tricky Časté chyby a dôležité veci
Stream sa dá použiť iba RAZ IllegaStateException: stream has already been operated upon +
príklad chyby
Stream<Integer> s = lst.stream().filter(e -> e > 50);
s.count();  // OK
s.toList(); // ❌ CHYBA – stream je vyčerpaný!

// ✅ Riešenie: vytvor nový stream:
lst.stream().filter(e -> e > 50).count();
lst.stream().filter(e -> e > 50).toList();
parallelStream + side-effects = problém funkcie musia byť "slušné" (bez globálnych premenných) +
príklad
int[] global = {0};
lst.parallelStream()
   .map(e -> e + (++global[0]))  // ❌ side-effect → nedeterministický výsledok
   .toList();

// ✅ Slušná funkcia – závisí iba od vstupov:
lst.parallelStream()
   .map(e -> e * 2)  // ✅ vždy rovnaký výsledok
   .toList();
map().filter() vs filter().map() poradie operácií ovplyvňuje výsledok! +
príklad
// map(e+e) → filter(e%3!=0)
lst.stream().map(e -> e+e).filter(e -> e%3 != 0).count();

// filter(e%3!=0) → map(e+e)  – ROVNAKÝ výsledok lebo:
// (2e) % 3 != 0  ⟺  e % 3 != 0

// POZOR – neplatí vždy! Napr. filter(e>8) → map(e+1) vs map(e+1) → filter(e>8):
karty.stream().map(k -> new Karta(k.getHodnota()+1,...))
              .filter(k -> k.getHodnota() > 8).toList();
// iný výsledok ako filter → map!
List.of() je IMMUTABLE add/remove hodí UnsupportedOperationException +
príklad
List<String> imm = List.of("a", "b");
imm.add("c"); // ❌ UnsupportedOperationException

// ✅ Ak potrebuješ modifikovateľný zoznam:
List<String> mutable = new ArrayList<>(List.of("a", "b"));
mutable.add("c"); // ✅ OK
var — typová inferencia (Java 10) typ sa odvodí z pravej strany, Java zostáva staticky typovaná +
príklad
var a    = 0;                      // int
var list = new ArrayList<String>(); // ArrayList<String>
var map  = new HashMap<String, Integer>();
var q    = Set.of(true, false);    // Set<Boolean>

// Hlavne pomáha pri ťažkopádnych typoch:
var it = m.entrySet().iterator(); // namiesto Iterator<Map.Entry<String,Integer>>

žiadne výsledky pre ""