Commit 150e39cb by Patryk Czarnik

Przykład "sprzedaz" - wersja bez optymalizacji

parent 493f3352
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/* Dla każdego miasta występującego w pliku z danymi
oblicz sumę wartości transakcji w tym mieście.
*/
/* W tej wersji najpierw ustalamy zbiór miast bez powtórzeń,
a następnie dla każdego miasta przeglądamy wszystkie rekordy z listy i filtrujemy.
Ma to niską wydajność. Złożoność: liczba miast × licza wszystkich rekordów.
*/
public class Grupowanie0 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Set<String> miasta = new TreeSet<>();
for(Transakcja r : lista) {
miasta.add(r.miasto());
}
System.out.println(miasta);
for (String miasto : miasta) {
BigDecimal suma = BigDecimal.ZERO;
for (Transakcja r : lista) {
if(r.miasto().equals(miasto)) {
suma = suma.add(r.wartosc());
}
}
System.out.printf("%-10s : %12s%n", miasto, suma);
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.*;
/* W tej wersji widzimy często stosowany schemat "grupowanie z wykorzystaniem słownika.
Korzystamy z operacji dostępnych od Java 5.
*/
public class Grupowanie1 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, BigDecimal> slownik = new TreeMap<>();
for(Transakcja r : lista) {
if(slownik.containsKey(r.miasto())) {
// to jest kolejny rekord z danego miasta → do obliczonej sumy dodajemy kolejny składnik
BigDecimal suma = slownik.get(r.miasto());
slownik.put(r.miasto(), suma.add(r.wartosc()));
} else {
// to jest pierwszy rekord z danego miasta → dodajemy pierwszy wpis
slownik.put(r.miasto(), r.wartosc());
}
}
// System.out.println(slownik);
// Przeglądanie zawartości słownika jako kluczy z wartościami
// wersja "Java 5":
for (Map.Entry<String, BigDecimal> entry : slownik.entrySet()) {
System.out.printf("%-10s : %12s%n", entry.getKey(), entry.getValue());
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/* Operacja dostępna od Java 8: getOrDefault */
public class Grupowanie2 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, BigDecimal> slownik = new TreeMap<>();
for(Transakcja r : lista) {
BigDecimal suma = slownik.getOrDefault(r.miasto(), BigDecimal.ZERO);
slownik.put(r.miasto(), suma.add(r.wartosc()));
}
for (Map.Entry<String, BigDecimal> entry : slownik.entrySet()) {
System.out.printf("%-10s : %12s%n", entry.getKey(), entry.getValue());
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/* Operacje "funkcyjne" na słownikach dostępne od Java 8:
compute, computeIfPresent, computeIfAbsent, putIfAbsent, ...
*/
public class Grupowanie3 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, BigDecimal> slownik = new TreeMap<>();
for(Transakcja r : lista) {
slownik.putIfAbsent(r.miasto(), BigDecimal.ZERO);
slownik.computeIfPresent(r.miasto(), (k, suma) -> suma.add(r.wartosc()));
// Funkcja na podstawie klucza i starej wartości ma zwrócić nową wartość.
}
for (Map.Entry<String, BigDecimal> entry : slownik.entrySet()) {
System.out.printf("%-10s : %12s%n", entry.getKey(), entry.getValue());
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/* Operacja "funkcyjna" na słownikach dostępna od Java 8: merge
Jest to operacja dokładnie implementująca ten schemat, który realizujemy w naszym zadaniu.
slownik.merge(KLUCZ, WARTOŚĆ, FUNKCJA)
Jeśli pod KLUCZem jeszcze niczego nie ma, to wpisywana jest WARTOŚĆ
Jeśli pod KLUCZem jest już STARA_WARTOŚĆ, to uruchamiana jest FUNKCJA
NOWA_WARTOŚĆ := FUNKCJA(STARA_WARTOŚĆ, WARTOŚĆ)
np. jeśli funkcją jest operacja dodawania, to będzie tak:
NOWA_WARTOŚĆ := STARA_WARTOŚĆ + WARTOŚĆ
*/
public class Grupowanie4 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, BigDecimal> slownik = new TreeMap<>();
for(Transakcja r : lista) {
slownik.merge(r.miasto(), r.wartosc(), BigDecimal::add);
}
for (Map.Entry<String, BigDecimal> entry : slownik.entrySet()) {
System.out.printf("%-10s : %12s%n", entry.getKey(), entry.getValue());
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Grupowanie5 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
final Map<String, StatystykiDlaMiasta> mapa = new TreeMap<>();
for (Transakcja transakcja : lista) {
final String miasto = transakcja.miasto();
StatystykiDlaMiasta statystyki = mapa.get(miasto);
if (statystyki == null) {
statystyki = new StatystykiDlaMiasta(miasto);
mapa.put(miasto, statystyki);
}
statystyki.update(transakcja);
// nie trzeba robić put, bo modyfikuję ten obiekt, który już jest w słowniku
}
for (StatystykiDlaMiasta statystykiZMiasta : mapa.values()) {
System.out.println(statystykiZMiasta);
}
}
// Klasa zgnieżdżona - tylko dla przykładu.
// Równie dobrze mógłbym tę klase zdefiniować w osobnym pliku.
// Obiekt tej klasy przechowuje statystyki job
private static class StatystykiDlaMiasta {
final String miasto;
int count = 0;
BigDecimal sum = BigDecimal.ZERO;
BigDecimal min = null;
BigDecimal max = null;
StatystykiDlaMiasta(String miasto) {
this.miasto = miasto;
}
void update(Transakcja transakcja) {
BigDecimal wartosc = transakcja.wartosc();
count++;
sum = sum.add(wartosc);
if (min == null || wartosc.compareTo(min) < 0) {
min = wartosc;
}
if (max == null || wartosc.compareTo(max) > 0) {
max = wartosc;
}
}
BigDecimal avg() {
return sum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_EVEN);
}
@Override
public String toString() {
return "jobTitle=" + miasto + ", count=" + count + ", sum=" + sum + ", min=" + min + ", max="
+ max + ", avg=" + avg();
}
}
}
package sprzedaz;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Grupowanie6 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, List<Transakcja>> grupy = lista.stream()
.collect(Collectors.groupingBy(Transakcja::miasto));
// Tak wywołany kolektor groupingBy zwraca słownik, w którym dla każdej wartości klucz (czyli miasta)
// zapisana jest lista wszystkich obiektów należących do tej grupy (czyli rekordów z tego miasta).
// Tutaj też zoabczymy przeglądanie słownika z sposób oparty o wyrażenia lambda, od Java 8
grupy.forEach((miasto, rekordy) -> {
System.out.println("Miasto " + miasto + ", liczba transakcji: " + rekordy.size());
rekordy.forEach(r -> System.out.println(" * " + r.towar() + " za " + r.wartosc()));
System.out.println();
});
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Grupowanie7 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, BigDecimal> sumy = lista.stream()
.collect(Collectors.groupingBy(Transakcja::miasto,
Collectors.mapping(Transakcja::wartosc,
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
sumy.forEach((miasto, suma) -> System.out.printf("%-10s : %12s%n", miasto, suma));
}
}
package sprzedaz;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Grupowanie8 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
// W tej wersji do kolektora groupingBy przekazujemy kolejny parametr,
// też kolektor mówiący "co robić każdą grupą".
Map<String, Double> sumy = lista.stream()
.collect(Collectors.groupingBy(Transakcja::miasto,
Collectors.summingDouble(Transakcja::wartoscDouble)));
// sumy.forEach((miasto, suma) -> System.out.printf("%-10s : %12s%n", miasto, suma));
sumy.forEach((miasto, suma) -> System.out.printf("%-10s : %12.2f%n", miasto, suma));
}
}
package sprzedaz;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
// Wersja z liczeniem statystyk, czyli coint, sum, avg, min i max jednocześnie
public class Grupowanie9 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
Map<String, DoubleSummaryStatistics> sumy = lista.stream()
.collect(Collectors.groupingBy(Transakcja::miasto,
Collectors.summarizingDouble(Transakcja::wartoscDouble)));
sumy.forEach((miasto, stats) -> System.out.printf("%-10s : %s%n", miasto, stats));
}
}
package sprzedaz;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class ObslugaCsvTransakcji {
public static List<Transakcja> wczytaj() {
return wczytaj("pliki/sprzedaz.csv");
}
public static List<Transakcja> wczytaj(String sciezka) {
return wczytaj(new File(sciezka));
}
public static List<Transakcja> wczytaj(File plik) {
List<Transakcja> lista = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(plik))) {
reader.readLine();
String linia;
while((linia = reader.readLine()) != null) {
String[] t = linia.split(",", -1);
Transakcja transakcja = new Transakcja(LocalDate.parse(t[0]),
t[1], t[2], t[3],t[4],
new BigDecimal(t[5]), Integer.parseInt(t[6]));
lista.add(transakcja);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return lista;
}
}
package sprzedaz;
import javax.swing.*;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Opt1 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
String miasto = JOptionPane.showInputDialog("Podaj miasto");
Optional<Transakcja> wynik = lista.stream()
.filter(r -> r.miasto().equalsIgnoreCase(miasto))
.max(Comparator.comparing(Transakcja::wartosc));
System.out.println("Obiekt wynikowy: " + wynik);
// sposoby obsługi Optionala
// klasyczny if
if(wynik.isPresent()) {
Transakcja r = wynik.get();
JOptionPane.showMessageDialog(null,
"Transakcja z dnia " + r.data() + " o wartości " + r.wartosc()
+ "\n" + r.sztuk() + " sztuk towaru " + r.towar());
} else {
JOptionPane.showMessageDialog(null, "Nie ma transakcji w mieście " + miasto,
"Brak danych", JOptionPane.ERROR_MESSAGE);
}
}
}
package sprzedaz;
import javax.swing.*;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Opt2 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
String miasto = JOptionPane.showInputDialog("Podaj miasto");
// Tutaj operacja max zwraca Optional i od razu stosujemy operację ifPresent
// Kod podany w ifPresent wykona się tylko jeśli wynik został znaleziony
// lista.stream()
// .filter(r -> r.miasto().equalsIgnoreCase(miasto))
// .max(Comparator.comparing(Rekord::wartosc))
// .ifPresent(r -> JOptionPane.showMessageDialog(null,
// "Transakcja z dnia " + r.data() + " o wartości " + r.wartosc()
// + "\n" + r.sztuk() + " sztuk towaru " + r.towar()));
lista.stream()
.filter(r -> r.miasto().equalsIgnoreCase(miasto))
.max(Comparator.comparing(Transakcja::wartosc))
.ifPresentOrElse(
r -> JOptionPane.showMessageDialog(null,
"Transakcja z dnia " + r.data() + " o wartości " + r.wartosc()
+ "\n" + r.sztuk() + " sztuk towaru " + r.towar())
, () -> JOptionPane.showMessageDialog(null, "Nie ma transakcji w mieście " + miasto,
"Brak danych", JOptionPane.ERROR_MESSAGE)
);
}
}
package sprzedaz;
import javax.swing.*;
import java.util.Comparator;
import java.util.List;
public class Opt3 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
String miasto = JOptionPane.showInputDialog("Podaj miasto");
// Operacja map wykonana na obiekcie Optional zwraca w wyniku Optional
// który jest pusty, jeśli ten pierwszy był pusty, a zawiera wynik funkcji mapującej, jeśli pierwszy zawierał dane.
// W tym przypadku z Optional<Rekord> powstaje Optional<String>
// Operacja orElse odczytuje wartość, która jest w Optionalu (w tym przypadku string "Transakcja z dnia....")
// a zwraca podaną wartość domyślną, jeśli w Optionalu nie było niczego.
// Tak pisany orElse ma sens wtedy, gdy wartość domyślna jest zawsze taka sama (nie wymaga obliczeń).
// Gdy uzyskanie tej wartości wymaga obliczeń, zalecane jest stosowanie orElseGet (zob. następna wersja)
String tekst = lista.stream()
.filter(r -> r.miasto().equalsIgnoreCase(miasto))
.max(Comparator.comparing(Transakcja::wartosc))
.map(r -> "Transakcja z dnia " + r.data() + " o wartości " + r.wartosc()
+ "\n" + r.sztuk() + " sztuk towaru " + r.towar())
.orElse("Nie znaleziono rekordów w podanym mieście");
JOptionPane.showMessageDialog(null, tekst);
}
}
package sprzedaz;
import javax.swing.*;
import java.util.Comparator;
import java.util.List;
public class Opt4 {
public static void main(String[] args) {
List<Transakcja> lista = ObslugaCsvTransakcji.wczytaj();
String miasto = JOptionPane.showInputDialog("Podaj miasto");
// orElseGet(() -> wzrór na wynik)
// wykona obliczenie tworzące wynik tylko wtedy i dopiero wtedy, gdy jest to potrzebne
String tekst = lista.stream()
.filter(r -> r.miasto().equalsIgnoreCase(miasto))
.max(Comparator.comparing(Transakcja::wartosc))
.map(r -> "Transakcja z dnia " + r.data() + " o wartości " + r.wartosc()
+ "\n" + r.sztuk() + " sztuk towaru " + r.towar())
.orElseGet(() -> "Nie znaleziono rekordów w mieście " + miasto);
JOptionPane.showMessageDialog(null, tekst);
}
}
package sprzedaz;
import java.util.List;
public class P0_WypiszObiekty {
public static void main(String[] args) {
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
System.out.println("Liczba transakcji: " + transakcje.size());
for(Transakcja transakcja : transakcje) {
System.out.println(transakcja);
}
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.time.LocalDate;
public class PrzykladyUzyciaRekordu {
public static void main(String[] args) {
Transakcja transakcja = new Transakcja(LocalDate.now(), "Mszczonów", "Kebabik u Zbyszka",
"jedzenie", "kebab rollo", new BigDecimal("16.00"), 5);
System.out.println(transakcja);
// zauważmy, że nie rekord.towar ani nie rekord.getTowar()
System.out.println(transakcja.towar() + " kosztuje " + transakcja.cena());
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Funkcyjnie1 {
// W tej wersji obliczamy sumę jako BigDecimal, funkcyjnie, wykorzystując tylko dostęp do pól rekordu.
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
BigDecimal suma = transakcje.stream()
.filter(tr -> tr.miasto().equalsIgnoreCase(miasto))
.map(tr -> tr.cena().multiply(BigDecimal.valueOf(tr.sztuk())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Funkcyjnie2 {
// W tej wersji obliczamy sumę jako BigDecimal, funkcyjnie, wykorzystując metodę wartosc().
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
BigDecimal suma = transakcje.stream()
.filter(tr -> tr.miasto().equalsIgnoreCase(miasto))
.map(Transakcja::wartosc)
.reduce(BigDecimal.ZERO, BigDecimal::add);
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Funkcyjnie3 {
// Użytkownik wprowadza nazwę miasta, a program oblicza sumę wartości transacji z tego miasta.
// W tej wersji stosujemy strumienie i podejście "funkcyjne".
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
double suma = transakcje.stream()
.filter(tr -> tr.miasto().equalsIgnoreCase(miasto))
.mapToDouble(Transakcja::wartoscDouble)
.sum();
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Imperatywnie1 {
// Użytkownik wprowadza nazwę miasta, a program oblicza sumę wartości transacji z tego miasta.
// W tej wersji stosujemy zwykłe pętle i ify,
// a sumę liczymy jako BigDecimal, wykorzystując tylko dostęp do pól w rekordach.
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
BigDecimal suma = BigDecimal.ZERO;
for(Transakcja tr : transakcje) {
if(tr.miasto().equalsIgnoreCase(miasto)) {
suma = suma.add(tr.cena().multiply(BigDecimal.valueOf(tr.sztuk())));
}
}
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Imperatywnie2 {
// Użytkownik wprowadza nazwę miasta, a program oblicza sumę wartości transacji z tego miasta.
// W tej wersji stosujemy zwykłe pętle i ify,
// a sumę liczymy jako BigDecimal, wykorzystując pomocniczą metodę wartosc() zdefiniowaną w rekordzie.
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
BigDecimal suma = BigDecimal.ZERO;
for(Transakcja tr : transakcje) {
if(tr.miasto().equalsIgnoreCase(miasto)) {
suma = suma.add(tr.wartosc());
}
}
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.util.List;
import javax.swing.JOptionPane;
public class SumaJednegoMiasta_Imperatywnie3 {
// Użytkownik wprowadza nazwę miasta, a program oblicza sumę wartości transacji z tego miasta.
// W tej wersji stosujemy zwykłe pętle i ify,
// a sumę liczymy jako double.
public static void main(String[] args) {
String miasto = JOptionPane.showInputDialog("Podaj miasto:");
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
double suma = 0;
for(Transakcja tr : transakcje) {
if(tr.miasto().equalsIgnoreCase(miasto)) {
suma += tr.wartoscDouble();
}
}
JOptionPane.showMessageDialog(null, "Suma: " + suma);
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.util.List;
public class SumaWszystkichTransakcji {
public static void main(String[] args) {
List<Transakcja> transakcje = ObslugaCsvTransakcji.wczytaj();
BigDecimal suma = BigDecimal.ZERO;
for(Transakcja tr : transakcje) {
suma = suma.add(tr.wartosc());
}
System.out.println("Suma wszystkich transakcji: " + suma);
}
}
package sprzedaz;
import java.math.BigDecimal;
import java.time.LocalDate;
/* Record to taka specjalna wersja klasy (podobnie jak enum jest specjalną wersją klasy w języku Java),
która zawiera rzeczy generowane automatycnzie na podstawie parametrów podanych w nawiasach.
Automatycznie tworzone są:
- pola private final
- konstruktor z takimi parametrami
- metody dostępowe służące do odczytu. Nie mają one w nazwie "get", tylko nazywają się tak samo jak pola
- toString, equals i hashCode (przypominające standardowe implementacje generowane przez IDE)
*/
public record Transakcja(
LocalDate data,
String miasto,
String sklep,
String kategoria,
String towar,
BigDecimal cena,
int sztuk) {
// W rekordach można definiować dodatkowe metody. Duży sens mają takie, które zwracają wyniki obliczone na podstawie wartości pól
public BigDecimal wartosc() {
return cena.multiply(BigDecimal.valueOf(sztuk));
}
public double wartoscDouble() {
return wartosc().doubleValue();
}
public int rok() {
return data.getYear();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment