Showing posts with label Stream API. Show all posts
Showing posts with label Stream API. Show all posts

Saturday, October 21, 2017

Exercises to study Java Stream API


In this blog post, we will study Stream API of Java 9 with several exercise questions. In these exercises, we will use two different domains: 
  • IMDB Movies
  • World Countries
Before we dive into the exercise questions, let's take a look at these domains:

IMDB Movies: We have three domain classes: Movie, Director and Genre. Each movie has a title, a year and a unique id assigned by Internet Movie DataBase (IMDB). Each movie has been directed by at least one director, and each director has many movies. Each movie belongs to at least one genre.

package com.example.domain;

import java.util.ArrayList;
import java.util.List;

public class Movie {
private int id;
   private String title;
   private int year;
   private String imdb;
   private List<Genre> genres;
   private List<Director> directors;

   {
      genres = new ArrayList<>();
      directors = new ArrayList<>();
   }

   public Movie() {
   }

   public Movie(int id, String title, int year, String imdb) {
      this.id = id;
      this.title = title;
      this.year = year;
      this.imdb = imdb;
   }

   // getters and setters

   @Override   
   public String toString() {
      return "Movie [title=" + title + ", year=" + year + "]";
   }
}


package com.example.domain;
import java.util.ArrayList;
import java.util.List;

public class Director {
   private int id;
   private String name;
   private String imdb;
   private List<Movie> movies= new ArrayList<>();

   public Director() {
   }

   public Director(int id, String name, String imdb) {
      this.id = id;
      this.name = name;
      this.imdb = imdb;
   }

   // getters and setters
   @Override   
   public String toString() {
      return "Director [id=" + id + ", name=" + name + ", imdb=" + imdb + "]";
   }
}

package com.example.domain;
public class Genre {
   private int id;
   private String name;

   public Genre() {
   }

   public Genre(int id, String name) {
      this.id = id;
      this.name = name;
   }

   // getters and setters
@Override
   public String toString() {
      return "Genre [id=" + id + ", name=" + name + "]";
   }

}

World Countries: There are two domain classes: Country and City. Each city belongs to a country defined by the attribute, countryCode. Each country has a unique code and has many cities.

package com.example.domain;

import java.util.ArrayList;
import java.util.List;

public class Country {
   private String code;
   private String name;
   private String continent;
   private double surfaceArea;
   private int population;
   private double gnp;
   private int capital;
   private List<City> cities;
   {
      cities = new ArrayList<>();
   }

   public Country() {
   }

   public Country(String code, String name, String continent, int population,
         double surfaceArea, double gnp, int capital) {
      this.code = code;
      this.name = name;
      this.continent = continent;
      this.surfaceArea = surfaceArea;
      this.population = population;
      this.capital = capital;
      this.gnp = gnp;
   }

   // getters and setters
   @Override   
   public String toString() {
      return "Country [ name=" + name + ", population=" + population + "]";
   }

}

package com.example.domain;

public class City {
   private int id;
   private String name;
   private int population;
   private String countryCode;

   public City() {
   }

   public City(int id, String name, String countryCode, int population) {
      this.id = id;
      this.name = name;
      this.population = population;
      this.countryCode = countryCode;
   }

   // getters and setters
   
   @Override   
   public String toString() {
      return "City [id=" + id + ", name=" + name + ", population=" + population + ", countryCode=" + countryCode + "]";
   };

}

EXERCISE #1

Find the highest populated city of each country:

package com.example.exercise;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.example.dao.CountryDao;
import com.example.dao.InMemoryWorldDao;
import com.example.domain.City;

public class Exercise1 {

   public static void main(String[] args) {
      CountryDao countryDao= InMemoryWorldDao.getInstance();
      List<City> highPopulatedCitiesOfCountries = countryDao.findAllCountries()
                .stream()
                .map( country -> country.getCities().stream().max(Comparator.comparing(City::getPopulation)))
                .filter(Optional::isPresent)
                .map(Optional::get)
                  .collect(Collectors.toList());
      highPopulatedCitiesOfCountries.forEach(System.out::println);
   }

}

The code given above will produce the following output:

City [id=3494, name=Auckland, population=381800, countryCode=NZL]
City [id=764, name=Suva, population=77366, countryCode=FJI]
City [id=2884, name=Port Moresby, population=247000, countryCode=PNG]
City [id=918, name=Les Abymes, population=62947, countryCode=GLP]
.
.
.
City [id=4067, name=Charlotte Amalie, population=13000, countryCode=VIR]
City [id=712, name=Cape Town, population=2352121, countryCode=ZAF]
City [id=538, name=Bandar Seri Begawan, population=21484, countryCode=BRN]
City [id=933, name=Tegucigalpa, population=813900, countryCode=HND]

EXERCISE #2

Find the most populated city of each continent:

package com.example.exercise;

import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import com.example.dao.CountryDao;
import com.example.dao.InMemoryWorldDao;
import com.example.domain.City;

public class Exercise2 {

    public static void main(String[] args) {
        CountryDao countryDao = InMemoryWorldDao.getInstance();
        final Predicate<Entry<String, Optional<City>>> isPresent = entry -> entry.getValue().isPresent();
        final BiConsumer<String, Optional<City>> printEntry =
                (k,v) -> {
                    City city = v.get();
                    System.out.println(k + ": City [ name= " + city.getName() + ", population= " + city.getPopulation() + " ]");
                };
        Collector<City, ?, Map<String, Optional<City>>> groupingHighPopulatedCitiesByContinent = Collectors.groupingBy(city -> countryDao.findCountryByCode(city.getCountryCode()).getContinent(), Collectors.maxBy(Comparator.comparing(City::getPopulation)));
        Map<String, Optional<City>> highPopulatedCitiesByContinent = countryDao.findAllCountries()
                .stream()
                .flatMap(country -> country.getCities().stream())
                .collect(groupingHighPopulatedCitiesByContinent);
        highPopulatedCitiesByContinent.forEach(printEntry);

    }

}

The code given above will produce the following output:

South America: City [ name= SÆo Paulo, population= 9968485 ]
Asia: City [ name= Mumbai (Bombay), population= 10500000 ]
Europe: City [ name= Moscow, population= 8389200 ]
Africa: City [ name= Cairo, population= 6789479 ]
North America: City [ name= Ciudad de M‚xico, population= 8591309 ]
Oceania: City [ name= Sydney, population= 3276207 ]


EXERCISE #3

Find the number of movies of each director: Try to solve this problem by assuming that Director class has not the member movies

package com.example.exercise;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.example.domain.Director;
import com.example.domain.Movie;
import com.example.service.InMemoryMovieService;
import com.example.service.MovieService;

public class Exercise3 {

    public static void main(String[] args) {
        MovieService movieService = InMemoryMovieService.getInstance();
        Collection<Movie> movies = movieService.findAllMovies();
        Map<String, Long> directorMovieCounts =
                movies.stream()
                        .map(Movie::getDirectors)
                        .flatMap(List::stream)
                        .collect(Collectors.groupingBy(Director::getName, Collectors.counting()));
        directorMovieCounts.entrySet().forEach(System.out::println);
    }

}


The code given above will produce the following output:

Sam Taylor Wood=1
F. Gary Gray=1
Oliver Hirschbiegel=1
Scott Cooper=1
Katherine Dieckmann=1
Kevin Macdonald=1
Peter Jackson=2
Andy Tennant=1
.
.
.
Isabel Coixet=1
Jason Reitman=1
Louie Psihoyos=1
Brian De Palma=1
Jay DiPietro=1

EXERCISE #4


Find the number of genres of each director's movies:

package com.example.exercise;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Stream;

import com.example.domain.Director;
import com.example.domain.Genre;
import com.example.domain.Movie;
import com.example.service.InMemoryMovieService;
import com.example.service.MovieService;

import static java.util.stream.Collectors.*;

public class Exercise4 {

    public static void main(String[] args) {
        MovieService movieService = InMemoryMovieService.getInstance();
        Collection<Director> directors = movieService.findAllDirectors();
        Stream<DirectorGenre> stream =
                directors.stream()
                        .flatMap(director -> director.getMovies()
                                .stream()
                                .map(Movie::getGenres)
                                .flatMap(Collection::stream)
                                .map(genre -> new DirectorGenre(director, genre))
                                .collect(toList()).stream()
                        );
        Map<Director, Map<Genre, Long>> directorGenreList =
                stream.collect(
                        groupingBy(
                                DirectorGenre::getKey,
                                groupingBy(DirectorGenre::getValue, counting())
                        )
                );
        directorGenreList.forEach(
                (k1,v1) -> {
                    System.out.println(k1.getName());
                    v1.forEach( (k2,v2) -> {
                                System.out.println(String.format("\t%-12s: %2d", k2.getName(), v2));
                            });
                    System.out.println();
                }
        );
    }

}

class DirectorGenre implements Map.Entry<Director, Genre> {
    private Director director;
    private Genre genre;

    public DirectorGenre(Director director, Genre genre) {
        this.director = director;
        this.genre = genre;
    }

    @Override    public Director getKey() {
        return director;
    }

    @Override    public Genre getValue() {
        return genre;
    }

    @Override    public Genre setValue(Genre genre) {
        this.genre = genre;
        return genre;
    }

}

The code given above will produce the following output:

Marc Webb
 Comedy      :  1
 Drama       :  1
 Romance     :  1

Peter Hyams
 Drama       :  1
 Mystery     :  1

Mark Neveldine
 Action      :  1
 Sci-Fi      :  1
 Thriller    :  1

Brian Taylor
 Action      :  1
 Sci-Fi      :  1
 Thriller    :  1

.
.
.

Ryûhei Kitamura
 Fantasy     :  1
 Drama       :  1
 Action      :  1
 Thriller    :  1
 Adventure   :  1

Shusuke Kaneko
 Action      :  1

Gregor Jordan
 Drama       :  1
 Thriller    :  1

EXERCISE #5


Find the highest populated capital city:

package com.example.exercise;

import com.example.dao.CityDao;
import com.example.dao.CountryDao;
import com.example.dao.InMemoryWorldDao;
import com.example.domain.City;
import com.example.domain.Country;

import java.util.Objects;
import java.util.Optional;

import static java.lang.System.out;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.maxBy;

public class Exercise5 {

    public static void main(String[] args) {
        CountryDao countryDao = InMemoryWorldDao.getInstance();
        CityDao cityDao = InMemoryWorldDao.getInstance();
        Optional<City> capital = countryDao.findAllCountries()
                .stream()
                .map(Country::getCapital)
                .map(cityDao::findCityById)
                .filter(Objects::nonNull)
                .collect(maxBy(comparing(City::getPopulation)));
        capital.ifPresent(out::println);
    }

}

The code given above will produce the following output:

City [id=2331, name=Seoul, population=9981619, countryCode=KOR]

EXERCISE #6


Find the highest populated capital city of each continent:

package com.example.exercise;

import com.example.dao.CityDao;
import com.example.dao.CountryDao;
import com.example.dao.InMemoryWorldDao;
import com.example.domain.City;

import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import static java.lang.System.out;
import static java.util.Comparator.*;
import static java.util.stream.Collectors.*;

public class Exercise6 {

    public static void main(String[] args) {
        CountryDao countryDao = InMemoryWorldDao.getInstance();
        CityDao cityDao = InMemoryWorldDao.getInstance();
        Map<String, Optional<ContinentPopulatedCity>> continentsCapitals = countryDao.findAllCountries()
                .stream()
                .filter(country -> country.getCapital() > 0)
                .map(country -> new ContinentPopulatedCity(country.getContinent(), cityDao.findCityById(country.getCapital())))
                .collect(groupingBy(ContinentPopulatedCity::getKey, maxBy(comparing(cpc -> cpc.getValue().getPopulation()))));
        BiConsumer<String, Optional<ContinentPopulatedCity>> print= (k, v) -> {
            Consumer<ContinentPopulatedCity> continentPopulatedCityConsumer = cpc -> out.println(cpc.getKey() + ": " + v.get().getValue());
            v.ifPresent(continentPopulatedCityConsumer);
        };
        continentsCapitals.forEach(print);
    }

}

class ContinentPopulatedCity implements Map.Entry<String, City> {
    private String continent;
    private City city;

    public ContinentPopulatedCity(String continent, City city) {
        this.continent = continent;
        this.city = city;
    }

    @Override    public String getKey() {
        return continent;
    }

    @Override    public City getValue() {
        return city;
    }

    @Override    public City setValue(City city) {
        this.city = city;
        return city;
    }

}

The code given above will produce the following output:

South America: City [id=2890, name=Lima, population=6464693, countryCode=PER]
Asia: City [id=2331, name=Seoul, population=9981619, countryCode=KOR]
Europe: City [id=3580, name=Moscow, population=8389200, countryCode=RUS]
Africa: City [id=608, name=Cairo, population=6789479, countryCode=EGY]
North America: City [id=2515, name=Ciudad de M‚xico, population=8591309, countryCode=MEX]
Oceania: City [id=135, name=Canberra, population=322723, countryCode=AUS]

EXERCISE #7


Sort the countries by number of their cities in desending order:

package com.example.exercise;

import com.example.dao.CountryDao;
import com.example.dao.InMemoryWorldDao;
import com.example.domain.Country;

import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;

import static java.lang.String.format;
import static java.lang.System.out;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

public class Exercise7 {

    public static void main(String[] args) {
        CountryDao countryDao = InMemoryWorldDao.getInstance();
        Comparator<Country> sortByNumOfCities = comparing(country -> country.getCities().size());
        Predicate<Country> countriesHavingNoCities = country -> country.getCities().isEmpty();
        List<Country> countries = countryDao.findAllCountries()
                .stream()
                .filter(countriesHavingNoCities.negate())
                .sorted(sortByNumOfCities.reversed())
                .collect(toList());
        countries.forEach(country -> out.println(format("%38s %3d", country.getName(), country.getCities().size())));
    }

}

The code given above will produce the following output:

                               China 363
                               India 341
                       United States 274
                              Brazil 250
                               Japan 248
                  Russian Federation 189
                              Mexico 173
                                       .
                                       .
                                       .
                            Barbados   1
                              Tuvalu   1
                                Niue   1
                Virgin Islands, U.S.   1
                              Brunei   1

EXERCISE #8

Find the list of movies having the genres "Drama" and "Comedy" only:

package com.example.exercise;

import com.example.domain.Genre;
import com.example.domain.Movie;
import com.example.service.InMemoryMovieService;
import com.example.service.MovieService;

import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

import static java.lang.String.format;
import static java.lang.System.out;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

public class Exercise8 {

    public static void main(String[] args) {
        MovieService movieService = InMemoryMovieService.getInstance();
        Collection<Movie> movies = movieService.findAllMovies();
        Predicate<Movie> drama = movie -> movie.getGenres().stream().anyMatch(genre -> genre.getName().equals("Drama"));
        Predicate<Movie> comedy = movie -> movie.getGenres().stream().anyMatch(genre -> genre.getName().equals("Comedy"));
        Predicate<Movie> havingTwoGenresOnly = movie -> movie.getGenres().size() == 2;
        List<Movie> dramaAndComedyMovies = movies.stream()
                .filter(havingTwoGenresOnly.and(drama.and(comedy)))
                .collect(toList());
        dramaAndComedyMovies.forEach(movie -> out.println(format("%-32s: %12s", movie.getTitle(), movie.getGenres().stream().map(Genre::getName).collect(joining(",")))));
    }

}

The code given above will produce the following output:

Away We Go                      : Comedy,Drama
A Serious Man                   : Comedy,Drama
High Life                       : Comedy,Drama
Cold Souls                      : Comedy,Drama
Worlds Greatest Dad             : Comedy,Drama
My One and Only                 : Comedy,Drama
Sunshine Cleaning               : Comedy,Drama
The Vicious Kind                : Comedy,Drama
Defendor                        : Comedy,Drama
I Love You Phillip Morris       : Comedy,Drama

EXERCISE #9

Group the movies by the year and list them:

package com.example.exercise;

import com.example.domain.Movie;
import com.example.service.InMemoryMovieService;
import com.example.service.MovieService;

import java.util.Collection;
import java.util.Map;

import static java.lang.String.format;
import static java.lang.System.out;
import static java.util.Comparator.comparing;
import static java.util.Map.*;
import static java.util.stream.Collectors.*;

public class Exercise9 {

    public static void main(String[] args) {
        MovieService movieService = InMemoryMovieService.getInstance();
        Collection<Movie> movies = movieService.findAllMovies();
        Map<Integer, String> moviesByYear = movies.stream().collect(groupingBy(Movie::getYear, mapping(Movie::getTitle, joining(","))));
        moviesByYear.entrySet().stream().sorted(comparing(Entry::getKey)).forEach(entry -> out.println(format("%4d: %s", entry.getKey(), entry.getValue())));
    }

}

The code given above will produce the following output:

1940: The Return of Frank James
1944: Double Indemnity
1948: The Treasure of the Sierra Madre
1950: Sunset Blvd.
1951: A Streetcar Named Desire
1953: Stalag 17
1954: Them!,Shichinin no samurai,Dial M for Murder
1958: Vertigo
1960: Psycho
1963: The Great Escape
1968: The Party
1969: Butch Cassidy and the Sundance Kid,Easy Rider,The Wild Brunch
1973: Le magnifique
1975: Dog Day Afternoon
1976: Network,The Little Girl Who Lives Down the Lane
1977: Der amerikanische Freund,The Last Wave
1983: Rembetiko,Danton,The Outsiders,Scarface
1987: Empire of the Sun
1988: The Accused
1989: My Left Foot: The Story of Christy Brown
1991: My Own Private Idaho
1992: Of Mice and Men
1993: Tombstone,Germinal
1994: Before the Rain,Heavenly Creatures
1996: L appartement
1997: Bacheha-Ye aseman,The Rainmaker
2000: Almost Famous
2001: Yeopgijeogin geunyeo
2002: Ice Age
2003: Bom yeoreum gaeul gyeoul geurigo bom,Oldboy,Jeux Denfants,Keulraesik,Azumi
2004: Bin-jip,Nae meorisokui jiwoogae,Samaria,Wicker Park,2046,Voditel dlya Very
2005: Hiroshima,Hwal,Just Like Heaven,Azumi 2: Death or Love
2006: Deiji,Ice Age: The Meltdown,La Sconosciuta
2007: Paranormal Activity,Before the Devil Knows You are Dead,Broken English,Ex Drummer
2008: Adam Resurrected,Nothing But the Truth,100 Feet,Nordwand,The Other Man,God on Trial,Sunshine Cleaning,Oorlogswinter,Bin-mong,Pazar - Bir Ticaret Masalı,The Hurt Locker,My Only Sunshine,Karamazovi,Ghost Town,To Verdener,Sonbahar,Yip Man,Elegy,What Doesnt Kill You,Faubourg 36
2009: 500 Days Of Summer,Beyond a Reasonable Doubt,Gamer,Cheri,Dorian Gray,Inglourious Basterds,Invictus,Julie and Julia,Los abrazos rotos,Armored,Bornova Bornova,Coco avant Chanel,Nefes: Vatan sağolsun,Up,Whiteout,The Time Travelers Wife,Whatever Works,Anonyma - Eine Frau in Berlin,Zombieland,Weather Girl,Watchmen,Angels and Deamons,Away We Go,Last Ride,The Boys Are Back,The Tournament,A Serious Man,Saw VI,Ne te retourne pas,District 9,Extract,Five Minutes of Haven,High Life,The Proposal,Veronika Decides to Die,The Goods: Live Hard, Sell Hard,The Hangover,Public Enemies,Creation,Amelia,The Rebound,Powder Blue,The Men Who Stare at Goats,Bright Star,Case 39,Cold Souls,Moon,Worlds Greatest Dad,State of Play,The Brothers Bloom,My One and Only,Man Som Hatar Kvinnor,Mary and Max,The Limits of Control,A Perfect Getaway,My Sisters Keeper,Planet 51,I Love You, Man,Amelia,The Damned United,New York, I Love You,Fish Tank,The Informant!,The Courageous Heart of Irena Sendler,Storm,Triangle,2012,The Cry of the Owl,13B,El secreto de sus ojos,Surrogates,Kimssi pyoryugi,Uzak İhtimal,Daybreakers,Cairo Time,The Cove,Tenderness,Hachiko: A Dogs Story,The Box,Everybodys Fine,Peter and Vandy,Women in Trouble,Un prophete,The Vicious Kind,Bakjwi,Up in the air,Law Abiding Citizen,Nine,The Soloist,Agora,Motherhood,Neşeli Hayat,The Greatest,The Boondock Saints II: All Saints Day,The Private Lives of Pippa Lee,The Imaginarium of Doctor Parnassus,The Men Who Stare at Goats,Cloudy with a Chance of Meatballs,The Princess and the Frog,An Education,Avatar,Avatar 3D,Precious: Based on the Novel Push by Sapphire,The Blind Side,New Moon,Fantastic Mr. Fox,Sherlock Holmes,The Road,Man som hatar kvinnor,The Collector,Leaves of Grass,Brooklyns Finest,Alice,Duplicity,Harry Brown,Defendor,Brothers,Crazy Heart,Kıskanmak,Das weisse Band - Eine deutsche Kindergeschichte,The Lovely Bones,Eastern Plays,Cargo,Glorious 39,Fifty Dead Men Walking,Grey Gardens,Vavien,Lebanon,Harry Potter and the Half-Blood Prince,Slovenka,9:06,2081,The Electric Mist,Serious Moonlight,Ice Age: Dawn of the Dinosaurs,La doppia ora,A Single Man,Cracks,The Missing Person,Nowhere Boy,Chloe,Drag Me to Hell,Eloise's Lover,Başka Dilde Aşk,Air Doll
2010: From Paris with Love,Edge of Darkness,Shutter Island,The Bounty Hunter,Dear John,Extraordinary Measures,Leap Year,Yahşi Batı,I Love You Phillip Morris,You Dont Know Jack,Yip Man 2: Chung si chuen kei,Alice in Wonderland,Romantik Komedi,Veda,Sin Nombre,The Book of Eli,Unthinkable,Shrek Forever After


EXERCISE #10

Sort the countries by their population densities in descending order ignoring zero population countries:

package com.example.exercise;

import com.example.dao.InMemoryWorldDao;
import com.example.dao.WorldDao;
import com.example.domain.Country;

import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;

import static java.lang.System.out;
import static java.util.Comparator.comparingDouble;

public class Exercise10 {

    public static void main(String[] args) {
        WorldDao worldDao = InMemoryWorldDao.getInstance();
        Collection<Country> countries = worldDao.findAllCountries();
        Comparator<Country> populationDensityComparator = comparingDouble(country -> country.getPopulation() / country.getSurfaceArea());
        Predicate<Country> livesNobody = country -> country.getPopulation() == 0L;
        countries.stream().filter(livesNobody.negate()).sorted(populationDensityComparator.reversed())
                .forEach(out::println);
    }

}

The code given above will produce the following output:

Country [ name=Macao, population=473000]
Country [ name=Monaco, population=34000]
Country [ name=Hong Kong, population=6782000]
Country [ name=Singapore, population=3567000]
Country [ name=Gibraltar, population=25000]
.
.
.

Country [ name=Western Sahara, population=293000]
Country [ name=Pitcairn, population=50]
Country [ name=Falkland Islands, population=2000]
Country [ name=Svalbard and Jan Mayen, population=3200]
Country [ name=Greenland, population=56000]

You can download the source of the domain through this link.

Wednesday, August 2, 2017

Java'da Dizin Arşivleme, Sıkıştırma ve Açma İşlemleri


Java 7 ile birlikte üçüncü parti bir kütüphane kullanmadan ZIP API yardımı ile arşivleme, sıkıştırma ve sıkıştırılmış dosyayı geri açma işlemlerini kolaylıkla gerçekleştirebiliyoruz. ZIP API, NIO.2 ve Stream API ile yakın işbirliği olan bir API. ZIP API, bize Java 7'de ve Java 8'de gelen yenilikleri kullandırıyor. Bu yazıda ZIP API'nin yeteneklerini örnek problemler üzerinden inceleyeceğiz.

1. Java Nesnelerini Sıkıştırılmış Zip Dosyası Olarak Saklamak

Bir dizi Customer sınıfı nesnesini zip dosyası içinde customers.dat kaydıyla saklamak istiyoruz:

Customer.java:

package com.example.zip;

import java.io.Serializable;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Customer implements Serializable {
   private String identityNo;
   private String firstName;
   private String lastName;

        public Customer(String identityNo, String firstName, String lastName) {
            this.identityNo = identityNo;
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getIdentityNo() {
            return identityNo;
        }

        public void setIdentityNo(String identityNo) {
            this.identityNo = identityNo;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

    @Override
    public String toString() {
        return "Customer{" + "identityNo=" + identityNo + ", firstName=" + firstName + ", lastName=" + lastName + '}';
    }

}

CompressJavaObjects.java:

package com.example.zip;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CompressJavaObjects {
    public static void main(String[] args) throws Exception {
        final File zipFile = new File("c:/tmp","customers.zip");
        List<Customer> customers= List.of(
                new Customer("1", "jack", "shephard"),
                new Customer("2", "kate", "austen"),
                new Customer("3", "james", "sawyer"),
                new Customer("4", "ben", "linus"),
                new Customer("5", "jin", "kwon")
        );
        try(
            final FileOutputStream fos = new FileOutputStream(zipFile);
            final ZipOutputStream zos= new ZipOutputStream(fos);                
            final BufferedOutputStream bos= new BufferedOutputStream(zos);
            final ObjectOutputStream oos= new ObjectOutputStream(bos);
        ){
            final ZipEntry zipEntry = new ZipEntry("customers.dat");
            zos.putNextEntry(zipEntry);
            oos.writeObject(customers);
        }
    }
}

2. Java Nesnelerini Sıkıştırılmış Dosyadan Geri Kazanmak

Bir dizi Customer sınıfı nesnesini zip dosyası içindeki customers.dat kaydından geri almak istiyoruz:

package com.example.zip;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class DecompressJavaObjects {
    public static void main(String[] args) throws Exception {
        final Path path = Paths.get("c:/tmp", "customers.zip");
        final File file = path.toFile();
        ZipFile zipFile= new ZipFile(file);
        try(
            final FileInputStream fis = new FileInputStream(file);
            final ZipInputStream zis= new ZipInputStream(fis);                
        ){
            final ZipEntry zipEntry = zis.getNextEntry();
            final BufferedInputStream bis= new BufferedInputStream(zipFile.getInputStream(zipEntry));
            final ObjectInputStream ois= new ObjectInputStream(bis);
            List<Customer> customers= (List<Customer>) ois.readObject();
            ois.close();
            bis.close();
        }
    }
}

3. Zip Dosya İçindeki Dizinleri Sözlük Sırasına Göre Sıralı Olarak Listelemek

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ListDirectoriesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());        
        List<String> directories= zipFile.stream()
                .filter(ZipEntry::isDirectory)
                .map(ZipEntry::getName)
                .map(path -> path.substring(0, path.length()-1))
                .sorted(String::compareTo)
                .collect(Collectors.toList());
        directories.forEach(System.out::println);
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

mastermind-game
mastermind-game/.settings
mastermind-game/src
mastermind-game/src/main
mastermind-game/src/main/java
mastermind-game/src/main/java/com
mastermind-game/src/main/java/com/example
mastermind-game/src/main/java/com/example/service
mastermind-game/src/main/java/com/example/service/impl
mastermind-game/src/main/java/com/example/validation
mastermind-game/src/main/java/com/example/web
mastermind-game/src/main/java/com/example/web/controller
mastermind-game/src/main/java/com/example/web/model
mastermind-game/src/main/java/com/example/web/viewmodel
mastermind-game/src/main/resources
mastermind-game/src/main/webapp
mastermind-game/src/main/webapp/WEB-INF
mastermind-game/src/main/webapp/WEB-INF/pages
mastermind-game/src/main/webapp/WEB-INF/tags
mastermind-game/src/main/webapp/resources
mastermind-game/src/main/webapp/resources/css
mastermind-game/src/main/webapp/resources/images

4. Zip Dosya İçindeki Dizin ve Dosyaları Saymak

Zip dosya içindeki toplam dizin ve sıradan dosya sayısını bulmak istiyoruz. Bunu yaparken ayrıca her bir dizindeki dosya sayısını da elde etmek istiyoruz.

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CountFilesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());
        
        final Function<ZipEntry, String> groupByParentDirectory = entry -> entry.getName().substring(0, entry.getName().lastIndexOf('/'));
        final Predicate<ZipEntry> isDirectory = ZipEntry::isDirectory;
        final Predicate<ZipEntry> isFile = isDirectory.negate();
        
        Map<String,Long> fileCounts= 
            zipFile.stream()
                   .filter(isFile)
                   .collect(Collectors.groupingBy(groupByParentDirectory,Collectors.counting()));
        fileCounts.entrySet().forEach( 
            pair -> System.out.println(
                String.format("There are %d files in the directory %s",pair.getValue(),pair.getKey())
            )
        );
                  
        long numberOfFiles= fileCounts.values().stream().mapToLong(Long::longValue).sum();
        System.out.println("Total number of files: "+numberOfFiles);
        System.out.println("Total number of directories: "+fileCounts.size());
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

There are 10 files in the directory mastermind-game/.settings
There are 2 files in the directory mastermind-game/src/main/webapp/resources/images
There are 4 files in the directory mastermind-game/src/main/java/com/example/web/model
There are 2 files in the directory mastermind-game/src/main/webapp/resources/css
There are 4 files in the directory mastermind-game
There are 1 files in the directory mastermind-game/src/main/java/com/example/service/impl
There are 1 files in the directory mastermind-game/src/main/java/com/example/service
There are 4 files in the directory mastermind-game/src/main/webapp/WEB-INF
There are 2 files in the directory mastermind-game/src/main/java/com/example/web/controller
There are 5 files in the directory mastermind-game/src/main/webapp/WEB-INF/pages
There are 5 files in the directory mastermind-game/src/main/resources
There are 1 files in the directory mastermind-game/src/main/java/com/example/web/viewmodel
There are 6 files in the directory mastermind-game/src/main/java/com/example/validation
There are 7 files in the directory mastermind-game/src/main/webapp/WEB-INF/tags
Total number of files: 54
Total number of directories: 14

5. Zip Dosya İçindeki java Uzantılı Dosyaları Listelemek

package com.example.zip;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ListJavaFilesInZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "mastermind-game.zip").toFile());
        final Predicate<ZipEntry> isDirectory = ZipEntry::isDirectory;        
        final Predicate<ZipEntry> isFile = isDirectory.negate();
        final Comparator<ZipEntry> sortByName = (z1,z2) -> Paths.get(z1.getName()).toFile().getName().compareTo(Paths.get(z2.getName()).toFile().getName());
        Predicate<ZipEntry> isJavaFile= entry -> entry.getName().endsWith(".java");
        List<ZipEntry> javaFiles= 
                zipFile.stream()
                       .filter(isFile.and(isJavaFile))
                       .sorted(sortByName)
                       .collect(Collectors.toList());
        javaFiles.forEach(System.out::println);
    }
}

Yukarıdaki uygulamayı çalıştırdığımızda aşağıda verildiği gibi bir ekran çıktısı elde ediyoruz:

mastermind-game/src/main/java/com/example/service/AuthenticationService.java
mastermind-game/src/main/java/com/example/validation/Email.java
mastermind-game/src/main/java/com/example/web/model/Game.java
mastermind-game/src/main/java/com/example/web/controller/GameController.java
mastermind-game/src/main/java/com/example/web/model/GameStatus.java
mastermind-game/src/main/java/com/example/validation/Iban.java
mastermind-game/src/main/java/com/example/validation/IbanValidator.java
mastermind-game/src/main/java/com/example/web/controller/LogonController.java
mastermind-game/src/main/java/com/example/web/viewmodel/LogonViewModel.java
mastermind-game/src/main/java/com/example/web/model/Move.java
mastermind-game/src/main/java/com/example/web/model/Player.java
mastermind-game/src/main/java/com/example/service/impl/SimpleAuthenticationService.java
mastermind-game/src/main/java/com/example/validation/StrongPassword.java
mastermind-game/src/main/java/com/example/validation/TcKimlikNo.java
mastermind-game/src/main/java/com/example/validation/TcKimlikNoValidator.java

6. Bir Dizini Özyinelemeli (=Recursive) Olarak Zip Formatında Sıkıştırmak


c:/tmp/figures dizini altındaki, ne kadar derinlikte olursa olsun tüm dosya ve dizinleri Zip dosyasında arşivlemek ve sıkıştırılmış olarak saklamak istiyoruz:

package com.example.zip;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class CreateZipFileRecursivelyFromDirectory {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path directory = Paths.get("c:/tmp/figures");
        final File zipFile = new File("c:/tmp","figures.zip");
        try(
            final FileOutputStream fos = new FileOutputStream(zipFile);
            final ZipOutputStream zos= new ZipOutputStream(fos);                                
        ) {
            Files.walkFileTree(directory, new ZipperVisitor(zos));
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }        
    }
}

class ZipperVisitor implements FileVisitor<Path>{
    final ZipOutputStream zos;                

    public ZipperVisitor(ZipOutputStream zos) {
        this.zos = zos;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        ZipEntry zipEntry= new ZipEntry(file.toFile().getPath());
        zos.putNextEntry(zipEntry);
        if (file.toFile().isFile()){
            Files.copy(file, zos);        
        }            
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;        
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        return FileVisitResult.CONTINUE;
    }
}

7. Zip Dosyasını Bir Dizine Açmak

package com.example.zip;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ExtractZipFile {
    public static void main(String[] args) throws IOException {
        ZipFile zipFile= new ZipFile(Paths.get("c:/tmp", "figures.zip").toFile());
        Consumer<ZipEntry> extractFile= zipEntry -> {
            File file= new File(zipEntry.getName());
            if (zipEntry.isDirectory()){
                file.mkdirs();
            } else {
                try {
                    Files.copy(zipFile.getInputStream(zipEntry), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
                } catch (IOException ex) {
                    Logger.getLogger(ExtractZipFile.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        };
        zipFile.stream().forEach(extractFile); 
    }
}

8. Zip dosyası Üzerinde Değişiklik Yapmak

Mevcut bir Zip dosyası üzerinde dosya silmek, dosya eklemek ve dosya içeriğini güncellemek gibi değişiklikler yapmak istiyoruz:

package com.example.zip;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class ModifyZipFile {

    public static void main(String[] args) throws IOException {
        final File originalFile = new File("c:/tmp", "figures.zip");
        final File tempFile = new File("c:/tmp", "figures-temp.zip");
        final List<String> filesToAdd = List.of("c:\\tmp\\figures\\jquery-fig1.png", "c:\\tmp\\figures\\jquery-fig2.png", "c:\\tmp\\figures\\jquery-fig3.png");
        final List<String> filesToDelete = List.of("c:\\tmp\\figures\\jboss-scanner1.png", "c:\\tmp\\figures\\jboss-scanner2.png", "c:\\tmp\\figures\\jboss-scanner3.png");
        final List<String> filesToUpdate = List.of("c:\\tmp\\figures\\mongo-1.png", "c:\\tmp\\figures\\mongo-2.png", "c:\\tmp\\figures/mongo-3.png");
        final ZipFile originalZipFile = new ZipFile(originalFile);
        final FileOutputStream tempFileOutputStream = new FileOutputStream(tempFile);
        final ZipOutputStream zos = new ZipOutputStream(tempFileOutputStream);
        final Predicate<ZipEntry> deleted = zipEntry -> filesToDelete.contains(zipEntry.getName());
        final Predicate<ZipEntry> notDeleted = deleted.negate();
        final Predicate<ZipEntry> updated = zipEntry -> filesToUpdate.contains(zipEntry.getName());
        final Predicate<ZipEntry> notUpdated = updated.negate();
        final Consumer<ZipEntry> copyToTempZipFile = zipEntry -> {
            try {
                ZipEntry newZipEntry = new ZipEntry(zipEntry);
                zos.putNextEntry(zipEntry);
                originalZipFile.getInputStream(zipEntry).transferTo(zos);
            } catch (IOException ex) {
                Logger.getLogger(CreateZipFileFromDirectory.class.getName()).log(Level.SEVERE, null, ex);
            }
        };
        originalZipFile.stream()
                .filter(notDeleted.and(notUpdated))
                .forEach(copyToTempZipFile);
        final Consumer<String> addToZipFile = file -> {
            try {
                ZipEntry zipEntry= new ZipEntry(file);
                zos.putNextEntry(zipEntry);
                Files.copy(Paths.get(file), zos);
            } catch (IOException ex) {
                Logger.getLogger(CreateZipFileFromDirectory.class.getName()).log(Level.SEVERE, null, ex);
            }
        };
        
        filesToAdd.forEach( addToZipFile );
        filesToUpdate.forEach( addToZipFile );
        originalZipFile.close();
        zos.close();
        Files.move(tempFile.toPath(), originalFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    }
}

9. LZMA2 Algoritması ile Dosya Sıkıştırmak ve Açmak

LZMA2 algoritması ile daha fazla kayıpsız sıkıştırma elde etmek mümkündür. En yüksek sıkıştırma oranı sağlayan seçeneklerle çalıştırıldığında, sıkıştırma süresi uzun ve bellek kullanımı yüksek olsa da büyük dosyaları sıkıştırmak istiyorsanız LZMA2'yı tercih etmelisiniz. LZMA2 ile birlikte sıkıştırma dosyasının uzantısı xz olacaktır. Java uygulamasından bu formatta dosya sıkıştırmak için tukaani kütüphanesini kullanabilirsiniz. Bu amaçla geliştireceğiniz maven projesinde pom.xml dosyasına aşağıdaki bağımlılığı eklemeniz gerekir:

<dependency>
   <groupId>org.tukaani</groupId>
   <artifactId>xz</artifactId>
   <version>1.6</version>
</dependency>

Java'da Heap yığın (=Heap dump) dosyaları diskte bolca yer kaplar. Bu dosyaları diskten yer kazanabilmek amacıyla xz formatında saklayabiliriz:

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example1 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path heapDumpFile = Paths.get("c:/tmp/dump.hprof");
        final File xzHeapDumpFile = new File("c:/tmp", "dump.hprof.xz");
        final FileOutputStream xzOutput = new FileOutputStream(xzHeapDumpFile);
        final LZMA2Options lzmA2Options = new LZMA2Options(LZMA2Options.PRESET_DEFAULT);
        try (XZOutputStream xzos = new XZOutputStream(xzOutput, lzmA2Options)) {
            Files.copy(heapDumpFile, xzos);
            long originalFileSize = heapDumpFile.toFile().length();
            long xzFileSize = xzHeapDumpFile.length();
            System.err.println("Heap dump file size: " + (originalFileSize / (1024 * 1024)) + "MB.");
            System.err.println("xz file size: " + (xzFileSize / (1024 * 1024)) + "MB.");
            System.err.println("Compression ratio: " + ((xzFileSize * 100) / originalFileSize) + "%.");
        }
    }
}

Uygulamayı çalıştırdığımızda ekran çıktısı aşağıda verildiği gibidir:

Heap dump file size: 256MB.
xz file size: 33MB.
Compression ratio: 12%

Java uygulamasından xz formatındaki sıkıştırılmış dosyadan geri açmak için aşağıdaki örnekten yararlanabilirsiniz:

package com.example;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.tukaani.xz.XZInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example2 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        final Path heapDumpFile = Paths.get("c:/tmp/dump.hprof");
        final File xzHeapDumpFile = new File("c:/tmp", "dump.hprof.xz");
        final FileInputStream xzInput = new FileInputStream(xzHeapDumpFile);
        try (XZInputStream xzis = new XZInputStream(xzInput)) {
            Files.copy(xzis,heapDumpFile);
        }
    }
}

10. Apache Commons Compress ile Dizini Arşivlemek ve LZMA2 Algoritması ile Sıkıştırmak

LZMA2 sıkıştırma algoritması bir dizini arşivlemek için kullanılamaz. Arşivlemek için TAR formatından yararlanabiliriz. TAR birden fazla dizin ve dosyayı tek bir dosyada birleştirerek bir dizini arşivlememizi sağlar. TAR bir sıkıştırma formatı ya da algoritması değildir. Aşağıda verilen kodda önce Apache Commons Compress kütüphanesini kullanarak bir dizini ve dizin içindeki dosyaları, TAR formatında arşivleyeceğiz. TAR formatında arşivlediğimiz dosyayı xz formatında sıkıştırıracağız ve böylelikle diskte daha az yer kaplayacak. Maven projesi olarak geliştirdiğimiz projenin pom.xml dosyasına aşağıdaki bağımlılıkları eklememiz gerekir:

<dependency>
   <groupId>org.tukaani</groupId>
   <artifactId>xz</artifactId>
   <version>1.6</version>
</dependency>
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-compress</artifactId>
   <version>1.14</version>
</dependency>

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example3 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        FileOutputStream fos = new FileOutputStream(tarXZFile);
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(new XZOutputStream(fos, new LZMA2Options(LZMA2Options.PRESET_MIN)))){
            taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            final Path directory = Paths.get("c:/tmp/figures");
            addFilesToTar(taos, directory.toFile());
        }
    }

    private static void addFilesToTar(TarArchiveOutputStream taos, File file) throws IOException {
        taos.putArchiveEntry(new TarArchiveEntry(file));
        if (file.isFile()) {
            Files.copy(file.toPath(), taos);
            taos.closeArchiveEntry();
        } else if (file.isDirectory()) {
            taos.closeArchiveEntry();
            for (File child : file.listFiles()) {
                addFilesToTar(taos, child);
            }
        }
    }
}

LZMA2 ile sıkıştırma işlemini Apache Commons Compress kütüphanesi içinden çıkan XZCompressorOutputStream sınıfı ile de gerçekleştirebiliriz:

package com.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example4 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        FileOutputStream fos = new FileOutputStream(tarXZFile);
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(new XZCompressorOutputStream(fos))){
            taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            final Path directory = Paths.get("c:/tmp/figures");
            addFilesToTar(taos, directory.toFile());
        }
    }

    private static void addFilesToTar(TarArchiveOutputStream taos, File file) throws IOException {
        taos.putArchiveEntry(new TarArchiveEntry(file));
        if (file.isFile()) {
            Files.copy(file.toPath(), taos);
            taos.closeArchiveEntry();
        } else if (file.isDirectory()) {
            taos.closeArchiveEntry();
            for (File child : file.listFiles()) {
                addFilesToTar(taos, child);
            }
        }
    }
}

TAR olarak arşivlenmiş ve LZMA2 ile sıkıştırılmış dosyayı (figures.tar.xz), arşiv dosyasından tekrar geri açmak için aşağıdaki koddan yararlanabilirsiniz:

package com.example;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class Example5 {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        File tarXZFile = new File("c:/tmp", "figures.tar.xz");
        String targetDirectory= "c:/";
        try (
            final FileInputStream fis = new FileInputStream(tarXZFile);
            final BufferedInputStream bis= new BufferedInputStream(fis);
            final XZCompressorInputStream xzCompressorInputStream = new XZCompressorInputStream(bis);
            final TarArchiveInputStream tais = new TarArchiveInputStream(xzCompressorInputStream);
        ) {
            TarArchiveEntry tarEntry = tais.getNextTarEntry();
            while(Objects.nonNull(tarEntry)) {
                final File target= new File(targetDirectory,tarEntry.getName());
                if (tarEntry.isDirectory()) {
                    target.mkdirs();
                }else if (tarEntry.isFile()){
                    Files.copy(tais, target.toPath(),StandardCopyOption.REPLACE_EXISTING );
                }
                tarEntry = tais.getNextTarEntry();                   
            }
        }
    }

}