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);

Java8 Класс Random. Методы ints и doubles

В Java8 в класс Random добавили пару интересных методов. Например, нам надо получить коллекцию случайных чисел от 5 до 8. Раньше, надо было
писать метод для получения случайного числа, потом создать коллекцию и туда записывать элементы.

В Java8 можно сделать так, хотя спорно, что это читабельнее, зато без циклов :

public static List<Integer> getRandomIntList(int count, int start, int finish) {
	return Arrays.stream(new Random().ints(count, start, finish + 1).toArray()).boxed().collect(Collectors.toList());
}

Можно также получат double и long. Пример для double (два знака после запятой)

public static List<Double> getRandomDoubleList(int count, int start, int finish) {
		return Arrays.stream(new Random().doubles(count, start, finish + 1).map(d -> (double) Math.round(d*100)/100).
			toArray()).boxed().collect(Collectors.toList());
}

Класс String. Метод regionMatches

В классе String есть булевский метод regionMatches, который сравнивает
подстроку одной строки с подстрокой другой строки.
Метод используется достаточно редко(я на практике использовал только один раз), но
данный метод достаточно интересен.

Есть целых два таких метода.

Первый — зависит от регистра, второй — нет.

Зависит от регистра

 public boolean regionMatches(int toffset, String other, int ooffset, int len)

toffset — с этой позиции мы начинаем отсчет для строки 1.
other — другая строка
ooffset — позиция для другой строки
len — количество символов, которые мы сравниваем.

Пример:

String a = "Мама мыла раму";
String b = "А раму мыла мама";
		 	
System.out.println(a.regionMatches(10, b, 2, 4));

Есть две строки.
Для первой строки мы отсчитываем 10 позиций и берем 4 символа, для второй строки берем 4 символа со второй позиции.
Т.е. получается, что мы сравниваем слова «раму» с первой и второй строки — строки равны между собой, значит результат
true.

Метод

public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 

Точно такой же, только здесь есть пареметр ignoreCase, который позволяет игнорировать регистр.