Метод, который возвращает stream

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

Обычный POJO-класс с конструктором, геттерами и сеттерами

public class Person {
	private String name;
    private Integer age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}

Класс для собирания данных в коллекцию.
Метод getAllPersons собирает данные в коллекцию List. Данные могут храниться где угодно: БД, Excel, в самом коде.

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

public class PersonCollect {
	
	private List<Person> persons = new ArrayList<>();

	public Stream<Person> persons() {
		return getAllPersons().stream();
	}
		
	public List<Person> getAllPersons() {
		if (persons.isEmpty()) {
			persons = Arrays.asList(new Person("Andrew", 17), new Person("Igor", 23),
					new Person("Ira", 23), new Person("Maxim", 24));
		}
		return persons;
	}

}

Неплохая идея, сделать метод persons(), который будет возвращать стрим, т.е. если мы захотим как-то модифицировать коллекцию, то
будем писать меньше кода.

Соответственно, если надо вывести только людей старше 20 лет, то код будет таким:

		 PersonCollect p = new PersonCollect();
		 List<Person> older20 = p.persons().filter(x->x.getAge()>20).collect(Collectors.toList());
		 System.out.println(older20);

Java8 чтение данных из файла

У класса Files есть метод lines, который читает данные из файла.

public static Stream<String> lines(Path path) throws IOException {
        return lines(path, StandardCharsets.UTF_8);
    }

Что здорово, там по дефолу проставляется кодировка UTF_8, т.е. нам её не надо указывать явно.

Т.е. это стрим, то мы можем сделать метод, который данные из каждой строки файла складывает в коллекцию лист.
Тут можно сделать в filter например игнорирование комментариев(считаем, что # это комментарий), удалять дублирующиеся символы
distinct и еще много чего:

public static List<String> listLineFiles(String pathToFile) {
		
		try {
			return  Files.lines(Paths.get(pathToFile)).filter(x->!x.startsWith("#")).distinct().collect(Collectors.toList());
		} catch (IOException e) {
			return null;
		}
	}

Т.е. есть файл с данными
example.org
example2.org
example3.org
example3.org
#для презентаций новым клиентам
exampledemo.org

Если мы вызовем данный метод, передав туда путь к файлу, то в коллекции будут только:
[example.org, example2.org, example3.org, exampledemo.org]

Java8 Метод removeIf для коллекций

В Java8 появился метод removeIf
Т.е. у вас есть некая коллекция элементов и вам надо из этой коллекции удалить данные.
До Java8 использовались Iterator, что было достаточно громоздко и сложно.
Но сейчас же это можно сделать просто в одну строчку

List<String> cities  = new ArrayList<>(Arrays.asList("Minsk", "Moscow" , "Kiev", "Ljubljana"));
	
cities.removeIf(x -> x.length()>7 && x.startsWith("L"));
	
System.out.println(cities);

На консоль НЕ будут выведены те города, длина которых больше 7 символов и который начинаются с буквы L.
Т.е. в данном случае:

[Minsk, Moscow, Kiev]
Метод removeIf он на вход принимает Предикат, т.е.на вход м.б. любое значение, но возвращаться будет тип boolean

Стоит также отметить, что мы модифицируем существующий список, а не возвращаем новый.

Java8. Основы StreamAPI

Лямбды появились в Java8 уже давно, но активно их использовать начал только сейчас.
Задача, найти четные числа от 5 до 50:

List<Integer> l = Arrays.asList(1,4,2,-30, 6, 45, 50,50, 20);
l  = l.stream().filter(s->s%2 == 0 && s>5 && s<=50).collect(Collectors.toList());
System.out.println(l);

Вывод на консоль:
[6, 50, 50, 20]

Найти количество четных чисел от 5 до 50:

List<Integer> l = Arrays.asList(1,4,2,-30, 6, 45, 50,50, 20);
long count  = l.stream().filter(s->s%2 == 0 && s>5 && s<=50).count();
System.out.println(count);

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

Найти количество четных чисел от 5 до 50 исключая дубликаты(distinct):

List<Integer> l = Arrays.asList(1,4,2,-30, 6, 45, 50,50, 20);
long count  = l.stream().filter(s->s%2 == 0 && s>5 && s<=50).distinct().count();
System.out.println(count);

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

enum ChronoUnit. Метод between

У enum ChronoUnit есть метод between, который позволяет находить время между двумя временными промежутками.
Например, сколько минут между временем 15:30 и 16:25

LocalTime t1 = LocalTime.of(15, 30);
LocalTime t2 = LocalTime.of(16, 25);
		
long l = ChronoUnit.MINUTES.between(t1, t2);
System.out.println(l);

Вывод на консоль — 55

Аналогично, можно искать часы, дни и т.п

Java. List методы indexOf и lastIndexOf

Все прекрасно знают методы indexOf и lastIndexOf для String, которые возвращают индекс первого и последнего
вхождения элемента в строку. Нумерация идет с 0, если ничего не найдено — то возвращается -1

String s = "Мама мыла раму";
//1	
System.out.println(s.indexOf("а"));
//11
System.out.println(s.lastIndexOf("а"));

Но аналогичные методы есть и для коллекции List, что еще больше упрощает код:

List<String> cities = new ArrayList<>(Arrays.asList("Minsk", "Moscow", "Brest", "Minsk"));
//0	
System.out.println(cities.indexOf("Minsk"));
//-1, т.к. Paris нет в коллекции
System.out.println(cities.indexOf("Paris"));
//3
System.out.println(cities.lastIndexOf("Minsk"));

Java8. List — метод replaceAll

В Java8 добавили весьма неплохой метод replaceAll, который изменяет все элементы коллекции.

Пример, есть некая коллекция и мы хотим у всех элементов убрать начальные и конечные пробелы.
Раньше мы делали так:

List<String> list = new ArrayList<>(Arrays.asList(" Minsk", "Moscow      ", "   Brest   "));

		
for (int i = 0; i < list.size(); i++)
  list.set(i, list.get(i).trim());

Но с выходом Java8 это можно сделать в одну(!!!) строчку без всяких циклов:

list.replaceAll(String::trim);

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

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

Нам всегда приходится работать с объектами, которые собираются в коллекцию.

Пример, есть обычный класс Product

package org.example.pojo;

public class Product {
	
	private String name = "";
	private String price = "";
	private String brand = "";
	
	
	public String getName() {
		return name;
	}
	public Product setName(String name) {
		this.name = name;
		return this;
	}
	public String getPrice() {
		return price;
	}
	public Product setPrice(String price) {
		this.price = price;
		return this;
	}
	public String getBrand() {
		return brand;
	}
	public Product setBrand(String brand) {
		this.brand = brand;
		return this;
	}
	
	
	@Override
	public String toString() {
		return "AllProduct [name=" + name + ", price=" + price + ", brand=" + brand + "]";
	}
}

Мы видим обычный POJO-класс с измененными сеттерами. Плюс этого класса, что здесь приватные поля с геттерами/сеттарами.
Нам необходимо только знать, какие должны быть поля, все остальное IDE генерит сама.

Далее создаем класс ProductCollections для того, чтобы удобно было работать с коллекциями — т.е. для того чтобы собрать объекты
в группу и потом уже работать с этой группой.

package org.example.pojoCollections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

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

public class ProductCollections {
	
	private static final Logger log = Logger.getLogger(ProductCollections.class);
	
	public static ArrayList<Product> prod = new ArrayList<>();

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

	public static ArrayList<Product>  getAllProduct() {
		ArrayList<Product> products = prod;
		if (prod.isEmpty()) {
			Product pr1 = new Product().setName("Икра красная \"Вкуснота \"").setBrand("Белорусская традиция").setPrice("120");
			Product pr2 = new Product().setName("Чай зеленый \"Принцесса Нури\"").setBrand("Ахмад").setPrice("15");		
			Product pr3 = new Product().setName("Чай ароматный с бергамотом.").setBrand("Беседа").setPrice("221");
			Product pr4 = new Product().setName("Lipton Yellow Label").setBrand("Lipton").setPrice("24");
			Product pr5 = new Product().setName("Magic aroma").setBrand("Lipton").setPrice("18");
			Product pr6 = new Product().setName("English Breakfast").setBrand("Lipton").setPrice("12");
			Product pr7 = new Product().setName("Royal Ceylon").setBrand("Lipton").setPrice("14");
		
		
				
			
			products.add(pr1);
			products.add(pr2);
			products.add(pr3);
			products.add(pr4);
			products.add(pr5);
			products.add(pr6);
			products.add(pr7);
		
		}
		return products;
	}

	public void printAll(ArrayList<Product> prod) {
		for (int i = 0; i < size(); i++) {
			log.info(prod.get(i));
		}
	}

	@Override
	public String toString() {
		for (int i = 0; i < size(); i++) {
			log.info(prod.get(i));
		}
		return "";
	}

	public ArrayList<Product> getProd() {
		return prod;
	}

	public void setProd(ArrayList<Product> prod) {
		this.prod = prod;
	}

	public ArrayList<Product> sortByName() {
		Collections.sort(getAllProduct(), sortByName);
		return prod;
	}

	public ArrayList<Product> sortByPrice(ArrayList<Product> prod) {
		Collections.sort(prod, sortByPrice);
		return prod;

	}

	public Product prodWithMaxPrice() {
		return Collections.max(getAllProduct(), sortByPrice );
	}

	public Product prodWithMinPrice() {
		return Collections.min(getAllProduct(), sortByPrice );
	}

	public ArrayList<Product> findByName(String productName) {

		ArrayList<Product> productWithEqualsName = new ArrayList<>();
		for (int i = 0; i < size(); i++) {
			if (prod.get(i).getName().equalsIgnoreCase(productName)) {
				productWithEqualsName.add(getAllProduct().get(i));
			}
		}

		if (productWithEqualsName.isEmpty()) {
			log.info("Didnt find elemets with product name \"" + productName + "\".");
		}
		return productWithEqualsName;
	}

	private  Comparator<Product> sortByName = new Comparator<Product>() {

		@Override
		public int compare(Product p1, Product p2) {
			return p1.getName().compareTo(p2.getName());
		}
	};

	private  Comparator<Product> sortByPrice = new Comparator<Product>() {

		@Override
		public int compare(Product p1, Product p2) {
			return Integer.parseInt(p1.getPrice()) - Integer.parseInt(p2.getPrice());
		}
	};
}


Ну и главный класс для просмотра результатов работы:

package org.example.main;

import org.apache.log4j.Logger;
import org.example.pojoCollections.ProductCollections;

public class MainClass {
	
	private static final Logger log = Logger.getLogger(MainClass.class);
	
	public static void main(String[] args) {
		ProductCollections cP = new ProductCollections();
		log.info("Max price is = " + cP.prodWithMaxPrice().getPrice());
		log.info("Size is " + cP.size());
		log.info("Min price is = " + cP.prodWithMinPrice().getPrice());
		log.info(cP.findByName("Milk 4"));
		log.info(cP.sortByName());		
	}

}


Весь код можно найти на гитхаб —

https://github.com/VictorSem/Product

Регулярки и ?

? означает, что число повторений 0 или 1. Т.е. символ не обязателен, может быть, а может и нет.

Пример
Есть некая строка найти в ней все числа. Числа м.б. как целые, так и дробные.

String str = "12321 74232.47 11111 2";
ArrayList<String> list = new ArrayList<>();
Pattern p = Pattern.compile("\\d+(\\.\\d+)?");
Matcher m = p.matcher(str);
		

while(m.find()) {
    list.add(m.group());
}
		 
System.out.println("The data found " + list);
System.out.println("List length is " + list.size());

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

The data found [12321, 74232.47, 11111, 2]
List length is 4

Рассмотрим теперь такое понятие как «жадность», которое по дефолту использует Java.

String str = "<Doc> <DocumentID>231F9</DocumentID>  <userName>Ivanov</userName></Doc>";

Выражение

<.*>

найдет все строку, т.к. строка начинается с <, далее любое количество символов, а заканчивается >.
Что же найдет выражение

<.*?> 

Мы просто найдем все теги, т.к. работаем не по «жадному» алгоритму

 [<Doc>, <DocumentID>, </DocumentID>, <userName>, </userName>, </Doc>]

Основы регулярок

Регулярные выражения служат для нахождения некоторого шаблона текста.
Они есть во всех языках программирования, но в данном случае мы будем говорить про Java.
Регулярки лежат в пакете import java.util.regex.*;
Два класса для работы с регулярками — это Pattern(переводится на русский как Шаблон) и Matcher(соответствие, совпадение.)
Эти два класса имеют модификатор final, т.е. от них НЕЛЬЗЯ наследоваться. Также у этих классов нет КОНСТРУКТОРОВ.

Пример регулярок — классы символов:

[a-z] — один строчный символ от a до z
[A-Z] — один прописной символ от A-Z
[a-zA-Z] — один строчный или один прописной символ от а-z
1[23456] — соответсвтует числам 12, 13, 14, 15, 16. Но не соответствует числу 17
1[2-6] — более короткая запись 1[23456]
2[38] — соответсвтует числам 23 и 28
[0-9] любая цифра 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
\\d — тоже значит любая цифра, сокращенная запись [0-9]
\\D — не ЦИФРА
\\s — пробел
\\S — не пробел

Пример, есть некая строка:

String str = "Steven Bernson, Attorney at Law 523 Broadway, suite 152 New York, NY, 10120, USA";

Необходимо достать из строки только строчные буквы:

ArrayList<String> list = new ArrayList<>();
		
String str = "Steven Bernson, Attorney at Law 523 Broadway, suite 152 New York, NY, 10120, USA";
		
Pattern p = Pattern.compile("[a-z]");
Matcher m = p.matcher(str);
while(m.find()) {
    list.add(m.group());
}
System.out.println(list);

Сначала мы создаём коллекцию list — в данную коллекцию будем добавлять результаты
Создадим экземпляр класса Pattern. Для этого вызовем статический метод compile, который на вход принимает регулярку.
Далее мы создаём экземпляр класса Matcher. Для этого в классе Pattern есть метод matcher, куда мы передаем строку str:

public Matcher matcher

Задаем цикл, пока m.find() == true. Конечно, «== true» можно и не писать.
m.find() — возвращает true, если регулярка совпадает с любой из частей текста. Если регулярка совпадает — до в список list мы добавляем
m.group() — возвращае строку, которая соответсвтвует регулярке
В конце, просто печатаем список.

В данном случае вывод на консоль:
[t, e, v, e, n, e, r, n, s, o, n, t, t, o, r, n, e, y, a, t, a, w, r, o, a, d, w, a, y, s, u, i, t, e, e, w, o, r, k]

Чтобы вывести только ПРОПИСНЫЕ буквы, необходимо изменить регулярку [a-z] на [A-Z]
Вывод на консоль:
[S, B, A, L, B, N, Y, N, Y, U, S, A]

Теперь чуть усложним задачу и нам необходимо вывести все слова. Есть регулярка +
+ — если + стоит после символа, то повторение 1 или больше раз

[a-zA-Z]+ — соответствует строкам, которые начинаются с любой буквы английского алфавита и повторяются 1 или больше раз

Вывод на консоль будет:
[Steven, Bernson, Attorney, at, Law, Broadway, suite, New, York, NY, USA]

Важная регулярка «?»

? — после символа означает повторение этого символа или 1 раз или 0 раз

Каким числам это соответсвует:
1[56]?2
152, 162, 12
Соответсвенно на консоль будет выведено для нашей строки str:
[152, 12]
Почему вывелось 12 ? Т.к. наша строка содержит 10120, то отсюда 12 и взялось.