Java. Фильтрация Enum

Часто приходится работать с Enum. Во время работы с Enum иногда приходится его фильтровать.

У нас есть Enum UserTypeEnum.
Для фильтрации данных в можно добавить в Enum такой метод, который принимает предикат.

public static List<UserTypeEnum> userTypeFilter(Predicate<UserTypeEnum> p) {
		List<UserTypeEnum> userTypeEnum = new ArrayList<>(Arrays.asList(UserTypeEnum.values()));
		return userTypeEnum.stream().filter(p).collect(Collectors.toList());
}

В итоге код Enum будет такой:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public enum UserTypeEnum {
	// admins group
	ADMIN(true, true, true), 
	MAIN_MODERATOR(true, true, true),
	MODERATOR(true, true, false),
	// users group
	BUYER(false, false, false), 
	SELLER(false, true, false);

	private boolean isEditUserComments;
	private boolean isEditModeratorComments;
	private boolean isAddProduct;
	
	public static List<UserTypeEnum> userTypeFilter(Predicate<UserTypeEnum> p) {
		List<UserTypeEnum> userTypeEnum = new ArrayList<>(Arrays.asList(UserTypeEnum.values()));
		return userTypeEnum.stream().filter(p).collect(Collectors.toList());
	}

	private UserTypeEnum(boolean isEditUserComments, boolean isEditModeratorComments, boolean isAddProduct) {
		this.isEditUserComments = isEditUserComments;
		this.isEditModeratorComments = isEditModeratorComments;
		this.isAddProduct = isAddProduct;
	}

	public boolean isEditUserComments() {
		return isEditUserComments;
	}

	public boolean isEditModeratorComments() {
		return isEditModeratorComments;
	}

	public boolean isAddProduct() {
		return isAddProduct;
	}
	
	

}

Вызов такой:

List<UserTypeEnum> addProd = UserTypeEnum.userTypeFilter(u -> u.isAddProduct());
System.out.println(addProd);
List<UserTypeEnum> dontAddProd = UserTypeEnum.userTypeFilter(u -> !u.isAddProduct());
System.out.println(dontAddProd);

Вывод на консоль:
[ADMIN, MAIN_MODERATOR]
[MODERATOR, BUYER, SELLER]

Реклама

Java8 StreamAPI для группировки (groupingBy)

Нужно сделать группировку по полю brand
Раньше группировка делалась достаточно сложно:

public Map<String, List<Product>> groupByBrand() {

	Map<String, List<Product>> result = new HashMap<>();

	for (Product product : prodCollection) {
		if (result.containsKey(product.getBrand())) {
			result.get(product.getBrand()).add(product);
		} else {
			ArrayList<Product> products = new ArrayList<>();
			products.add(product);
			result.put(product.getBrand(), products);

		}
	}
		
		

	return result;
}

Но с приходом Java8 стало намного проще:

public Map<String, List<Product>> groupByBrandSteramApi() {
	return products().collect(Collectors.groupingBy(Product::getBrand));	
}

Java8. Метод join, StringJoiner, joining

В класс String добавили метод join, который служит для соединения строк с заданным разделителем.

String s = String.join("-", "2017", "08", "23");
System.out.println(s);

Output:
2017-08-23

Также можно передавать коллекцию:

List<String> cities  = Arrays.asList("Minsk", "Moscow" , "Kiev", "Ljubljana");
String citiesWithComma = String.join("," , cities);
System.out.println(citiesWithComma);

Output:
Minsk,Moscow,Kiev,Ljubljana

Добавили также специальный класс StringJoiner. Тут мы в конструктор просто передали разделитель запятую:

StringJoiner joiner = new StringJoiner(",");
joiner.add("Minsk");
joiner.add("Moscow");
joiner.add("Kiev");
joiner.add("Ljubljana");
System.out.println(joiner);

Output:
Minsk,Moscow,Kiev,Ljubljana

Благо, метод add возвращает this, а не void. Поэтому проще написать так:

StringJoiner joiner2 = new StringJoiner(",");
joiner2.add("Minsk")
.add("Moscow")
.add("Kiev")
.add("Ljubljana");
System.out.println(joiner2);

Output:
Minsk,Moscow,Kiev,Ljubljana

Также в конструктор в StringJoiner можно передавать не только разделитель, но и начало и
конец строки:

StringJoiner joiner3 = new StringJoiner(",", "[", "]");
joiner3.merge(joiner2);
System.out.println(joiner3);

Output:
[Minsk,Moscow,Kiev,Ljubljana]

Здесь, чтобы писать меньше кода я соединил joiner3 и joiner2 с помощью метода merge.

Еще в StringJoiner есть метод length — возвращает длину строки и setEmptyValue — т.е. если в joiner ничего не добавлено, то
там хранится введенное значение.

Больше никаких методов в StringJoiner нет.

Также у класса Collectors есть метод joining.
Тут тоже все примитивно(линк на гитхаб https://github.com/VictorSem/ProductJava8/blob/master/ProductJava8/src/main/java/org/example/main/MainClasss.java):

String testJoining = pc.products().map(x->x.getBrand()).distinct().collect(Collectors.joining(",", "[", "]"));
log.info(testJoining);

Output:
[Наши традиции,Ахмад,Беседа,Lipton]

Java8. Match функции

Существуют 3 булевы функции:
anyMatch — возвращает true, если существует лишь хоть ОДИН элемент в списке, удовлетворяющий условию.

noneMatch — возвращает true, если НЕ существует ни одного элемента в списке, удовлетворяющего условию.

allMatch — возвращает true, если ВСЕ элементы в списке, удовлетворяют условию.

Код :

public boolean isAnyProductPriceGreaterOrEqual(Integer productPrice) {
	return products().anyMatch(p -> p.getPrice() >= productPrice);
}
	
public boolean isNoneProductPriceGreaterOrEqual(Integer productPrice) {
	return products().noneMatch(p -> p.getPrice() >= productPrice);
}
	
public boolean isAllProductPriceGreaterOrEqual(Integer productPrice) {
	return products().allMatch(p -> p.getPrice() >= productPrice);
}

Весь код на гитхаб:
https://github.com/VictorSem/ProductJava8

Java8. Класс TemporalAdjusters. Методы next/previous/nextOrSame/previousOrSame

Продолжаем рассматривать Java8 и новое API для работы с датами.
Класс TemporalAdjusters.
Например, нам надо найти дату следующей пятницы. Сегодня пятница 16.06.2017, значит следующая пятница будет
23.06.2017.

public static LocalDate calcNextFriday() {
	return LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
}

Абсолютно аналогично находится дата предыдущей пятницы:

public static LocalDate calcPreviousFriday() {
	  return LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}

В классе TemporalAdjusters есть также методы nextOrSame и previousOrSame. Т.е. если сегодня пятница, то должна вернуться
сегодняшняя дата.

Вот весь код:

public static LocalDate calcNextFriday() {
	  return LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
}
	
public static LocalDate calcNextOrSameFriday() {
	  return LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
}

public static LocalDate calcPreviousFriday() {
	  return LocalDate.now().with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}	

public static LocalDate calcPreviousOrSame() {
	  return LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY));
}

Кстати, использую импорт статических методов, код становится вообще таким(для IDE Eclipse нужно стать курсоров на нужным метод и нажать Ctrl + Shift + m):

public static LocalDate calcNextFriday() {
	return LocalDate.now().with((FRIDAY));
}
	
public static LocalDate calcOrSameFriday() {
	return LocalDate.now().with(nextOrSame(FRIDAY));
}
	
public static LocalDate calcPreviousFriday() {
	return LocalDate.now().with(previous(FRIDAY));
}	

public static LocalDate calcPreviousOrSame() {
	return LocalDate.now().with(previousOrSame(FRIDAY));
}

Java8. Работа с коллекциями объектов

Когда-то давно писал статью
https://user12vv.wordpress.com/2016/10/19/java-работа-с-коллекциями-объектов/
В Java8 это можно сделать намного проще:

Есть обычный класс Product

package org.example.pojo;

public class Product {
	
	private String name;
	private Integer price;	
	private String brand;
		
	
	
	public Integer getPrice() {
		return price;
	}

	public Product setPrice(Integer price) {
		this.price = price;
		return this;
	}
	
	public String getName() {
		return name;
	}
	public Product setName(String name) {
		this.name = name;
		return this;
	}
	
	public String getBrand() {
		return brand;
	}
	public Product setBrand(String brand) {
		this.brand = brand;
		return this;
	}

	@Override
	public String toString() {
		return "Product [name=" + name + ", price=" + price + ", brand=" + brand + "]";
	}
	
	
	
}

Далее создаем класс ProductCollections для удобства работы с коллекциями:

package org.example.pojoCollections;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.log4j.Logger;
import org.example.pojo.Product;

public class ProductCollections {

	private static final Logger log = Logger.getLogger(ProductCollections.class);

	private ArrayList<Product> prodCollection = new ArrayList<>();

	private Stream<Product> products() {
		return getProdCollection().stream();
	}

	public int size() {
		return prodCollection.size();
	}

	public Product get(int i) {
		return prodCollection.get(i);
	}

	public boolean isEmpty() { 
		return prodCollection.isEmpty();
	}

	public ArrayList<Product> getProdCollection() {
		if (isEmpty()) {
			prodCollection.add(new Product().setName("Икра красная \"Вкуснота \"").setBrand("Наши традиции").setPrice(25));
			prodCollection.add(new Product().setName("Чай зеленый \"Принцесса Нури\"").setBrand("Ахмад").setPrice(15));
			prodCollection.add(new Product().setName("Чай ароматный с бергамотом.").setBrand("Беседа").setPrice(140));
			prodCollection.add(new Product().setName("Чай ароматный с бергамотом.").setBrand("Беседа").setPrice(65));
			prodCollection.add(new Product().setName("Lipton Yellow Label").setBrand("Lipton").setPrice(50));
			prodCollection.add(new Product().setName("Magic aroma").setBrand("Lipton").setPrice(18));
			prodCollection.add(new Product().setName("English Breakfast").setBrand("Lipton").setPrice(12));
			prodCollection.add(new Product().setName("Royal Ceylon").setBrand("Lipton").setPrice(14));
		}

		return prodCollection;
	}
	
	

	@Override
	public String toString() {
		return "ProductCollections [prodCollection=" + prodCollection + "]";
	}

	public List<Product> sortByName() {
		return products().sorted(Comparator.comparing(Product::getName)).collect(Collectors.toList());
	}

	public List<Product> sortByPrice() {
		return products().sorted(Comparator.comparingInt(Product::getPrice)).collect(Collectors.toList());

	}

	public Product prodWithMaxPrice() {
		return products().max(Comparator.comparing(Product::getPrice)).get();
	}

	public Product prodWithMinPrice() {
		return products().min(Comparator.comparingInt(Product::getPrice)).get();
	}

	public List<Product> findBy(Predicate<Product> predicate) {
		List<Product> list = products().filter(predicate)
				.collect(Collectors.toList());

		if (list.isEmpty()) {
			log.info("Dont find element.");
		}
		return list;
	}
	
	
	public static Predicate<Product> productName(String productName) {
		return p -> p.getName().contains(productName);
	}
	
	public static Predicate<Product> productBrand(String productBrand) {
		return p -> p.getBrand().contains(productBrand);
	}
	
	public static Predicate<Product> productPrice(Integer productPrice) {
		return p -> p.getPrice().equals(productPrice);
	}

}

Ну и класс для проверки:

package org.example.main;

import org.apache.log4j.Logger;
import org.example.pojoCollections.ProductCollections;
import static org.example.pojoCollections.ProductCollections.productBrand;
import static org.example.pojoCollections.ProductCollections.productName;
import static org.example.pojoCollections.ProductCollections.productPrice;

public class MainClasss {
	
	private static final Logger log = Logger.getLogger(MainClasss.class);
	
	public static void main(String[] args) {
		
		
		
		ProductCollections pc = new ProductCollections();	
		
		log.info(pc.prodWithMaxPrice()); 
		log.info(pc.prodWithMinPrice());
		
		System.out.println("=========================");
		
		log.info(pc.findBy(productBrand("Беседа")));
		log.info(pc.findBy(productName("Ceylon")));
		log.info(pc.findBy(productPrice(1000)));
		
		
	
		
		
	}

}

Весь код на гитхаб:
https://github.com/VictorSem/ProductJava8

Java8 Рефакторинг работы с датами

Очень давно была статья:
https://user12vv.wordpress.com/2013/07/08/java-основа-работы-с-датами/

Ужасный legacy code, что еще можно сказать.

Сейчас есть Java8, с крутым API для работы с датами:

Метод countDaysBetween2dates считает количество целых дней между двумя датами, а метод getLocalDate возвращает
дату в формате ДД.ММ.ГГГГ типа String.

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public final class DateUtils {
	
	private static final String FORMAT_DATE = "dd.MM.yyyy";
	
	public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern(FORMAT_DATE);
	
	public static long countDaysBetween2dates(String date1, String date2) {
		LocalDate ld1 = LocalDate.parse(date1, formatter);
		LocalDate ld2 =LocalDate.parse(date2, formatter);
		
		return Math.abs(ChronoUnit.DAYS.between(ld1, ld2));
	}
	
	
	
	public static String getLocalDate(int n) {		
		return formatter.format(LocalDate.now().plusDays(n));
	}

	public static void main(String[] args) { 
		// сегодняшняя дата;
		System.out.println(getLocalDate(0));
		// послезавтра;
		System.out.println(getLocalDate(2));
		
		System.out.println(countDaysBetween2dates("22.06.2013", "04.06.2013"));				

		System.out.println(countDaysBetween2dates("15.06.2013", "04.06.2013"));
	}	
}

Вывод на консоль:

09.06.2017
11.06.2017
18
11

Java8. Работа с датами. Методы minus/plus и minusDays/plusDays

В Java8 есть классное API для работы с датами.
Рассмотрим мелкий пример, мы хотим создать два простейших метода типа LocalDate, которые
возвращают завтрашний и сегодняшний день.

import static java.time.temporal.ChronoUnit.DAYS;

....................



public static LocalDate tomorrow() {
	return LocalDate.now().plus(1, DAYS);
}
	
public static LocalDate yesterday() {
	return LocalDate.now().minus(1, DAYS);
}

Не понятно, зачем делать так ? Зачем этот статический импорт DAYS. Ведь можно сделать намного
более читабельнее:

public static LocalDate tomorrow() {
	return LocalDate.now().plusDays(1);
}
	
public static LocalDate yesterday() {
	return LocalDate.now().minusDays(1);
}

Сортировка объектов в Java8. Comparator, comparing, reversed

Когда-то давно, была статья про сортировку объектов:
https://user12vv.wordpress.com/2015/04/06/java-сортировка-объектов/

Но в Java8 это можно делать намного проще:

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

public class Main {
	
	
	public static List<Worker> getWorkers() {
		 List<Worker> workers = new ArrayList<>();      
         
		 workers.add(new Worker("Alan", 85, "alan4@gmail.com"));
		 workers.add(new Worker("Petrov", 30, "petrov@gmail.com"));
		 workers.add(new Worker("Sidorov", 55, "sidorov@gmail.com"));
		 workers.add(new Worker("Ivanov", 22, "ivanov@gmail.com"));
		 workers.add(new Worker("Alan", 37, "alan1@gmail.com"));
		 return workers;	
	}
	
	
	 public static void main(String args[]) {
	           
		 	List<Worker> workers = getWorkers();
	        System.out.println("dont sort");
	        System.out.println(workers);
	         
	        System.out.println("sort by name");
	      //  workers.sort((w1, w2) -> w1.getName().compareTo(w2.getName()));
	        workers.sort(Comparator.comparing(Worker::getName));
	        System.out.println(workers);
	         
	        System.out.println("sort by age");
	     
	        workers.sort(Comparator.comparing(Worker::getAge));
	        System.out.println(workers);
	         
	        System.out.println("sort by name and age");
	        workers.sort(Comparator.comparing(Worker::getName).thenComparing(Worker::getAge));
	        System.out.println(workers);
	 
	     
	    }
}


Код становится намного более простым. А все потому, что в интрефейс List добавили очень крутой метод sort, который на вход принимает компаратор:

 default void sort(Comparator<? super E> c)

Мы можем делать такие вещи — сортировать по имени, а потом и по возрасту(используем thenComparing):

 workers.sort(Comparator.comparing(Worker::getName).thenComparing(Worker::getAge));

Можно спросить, а как сделать сортировку в обратном порядке. Т.е. я хочу сортировку по возрасту выполнить по убыванию.
Опять же все примитивно, т.к. есть метод reversed:

System.out.println("sort by age reversed");	     
workers.sort(Comparator.comparing(Worker::getAge).reversed());
System.out.println(workers);

Фильтрация коллекций на null

Иногда надо удалить из коллекции все null-элементы.
Т.к. я использую Java8 и стримы, то делал так:

List<String> cities  = Arrays.asList("Minsk", "Moscow" , "Kiev",null, "Ljubljana", null, null);
cities = cities.stream().filter(x -> x!=null).collect(Collectors.toList());
System.out.println(cities);

Вывод на консоль:
[Minsk, Moscow, Kiev, Ljubljana]

Но можно также делать и по-другому (имхо более красиво):

List<String> cities  = Arrays.asList("Minsk", "Moscow" , "Kiev",null, "Ljubljana", null, null);
cities = cities.stream().filter(Objects::nonNull).collect(Collectors.toList());
System.out.println(cities);