Предлог By
и его положительный вклад в продуктивность.
Работайте с умом, используя намерения, раскрывающие API Java
У всех нас ограниченное время. Все, что экономит время на кодирование и улучшает читаемость нашего кода, прекрасно. Читаемость является ключевым моментом, поскольку мы читаем код гораздо больше, чем пишем его. Многим людям, возможно, придется читать фрагмент кода в течение его срока службы, поэтому экономия времени - настоящий бонус.
Чем можно сэкономить время с коллекциями Eclipse?
Вы можете aggregate
, group
, sum
, count
, min
, max
и преобразовать в другие отсортированные коллекции по применению некоторого Function
. Следующие By
методы доступны для каждого RichIterable
подтипа в Коллекциях Eclipse.
Я покажу три примера из Eclipse Collections Pet Kata, которые иллюстрируют, как эти методы могут сэкономить ваше время на чтение и написание кода.
CountBy
Используйте countBy
, если вы хотите подсчитать набор вещей по атрибуту или результату применения Function
к этим вещам. Метод принимает в качестве параметра тип Function
и возвращает Bag
. Этот метод был добавлен в Eclipse Collections 9.0.
Следующий код - это обязательное решение Java для взятия List
из PetType
и подсчета каждого PetType
с использованием Map
.
List<PetType> petTypes = this.people.flatCollect(Person::getPets).collect(Pet::getType); Map<PetType, Integer> counts = Maps.mutable.empty(); for (PetType petType : petTypes) { Integer count = petTypeCounts.get(petType); if (count == null) { count = 0; } petTypeCounts.put(petType, count + 1); } Assert.assertEquals(Integer.valueOf(2), counts.get(PetType.CAT)); Assert.assertEquals(Integer.valueOf(2), counts.get(PetType.DOG)); // The following test would fail as the Map would return null Assert.assertEquals(Integer.valueOf(0), counts.get(PetType.CONDOR));
Этот код можно упростить, используя Java 8 Streams и groupingBy
и counting
Collectors.
Map<PetType, Long> counts = this.people.stream() .flatMap(person -> person.getPets().stream()) .collect(Collectors.groupingBy(Pet::getType, Collectors.counting())); Assert.assertEquals(Long.valueOf(2), counts.get(PetType.CAT)); Assert.assertEquals(Long.valueOf(2), counts.get(PetType.DOG)); // The following test would fail as the Map would still return null Assert.assertEquals(Long.valueOf(0), counts.get(PetType.CONDOR));
В следующем решении будет использоваться тип Eclipse Collections Bag
вместо Map
. Bag
- это Map
ключей к счетчикам. Реализация HashBag
в Eclipse Collections использует ObjectIntHashMap
для своего хранилища, поэтому не требует упаковки счетчиков как Integer
или Long
.
Bag<PetType> counts = this.people.asLazy() .flatCollect(Person::getPets) .collect(Pet::getType) .toBag(); Assert.assertEquals(2, counts.occurrencesOf(PetType.CAT)); Assert.assertEquals(2, counts.occurrencesOf(PetType.DOG)); Assert.assertEquals(0, counts.occurrencesOf(PetType.CONDOR));
Для чтения этого кода требуется меньше времени, поскольку в нем меньше символов, но метод toBag
сообщит вам о своем намерении только в том случае, если вы уже знаете, что такое Bag
.
Вот небольшая модификация этого решения с использованием countBy
.
Bag<PetType> counts = this.people.asLazy() .flatCollect(Person::getPets) .countBy(Pet::getType); Assert.assertEquals(2, counts.occurrencesOf(PetType.CAT)); Assert.assertEquals(2, counts.occurrencesOf(PetType.DOG)); Assert.assertEquals(0, counts.occurrencesOf(PetType.CONDOR));
CountBy
сокращает код и лучше передает намерения. Считаем всех питомцев по их PetType
. Результатом будет Bag
, так как это лучшая структура данных для возврата.
Группа по
Используйте groupBy
, если вы хотите сгруппировать элементы коллекции с помощью функции для вычисления ключа для каждого элемента. Метод принимает тип Function
в качестве параметра и возвращает Multimap
.
Следующий код - это обязательное решение Java, позволяющее взять List
из Person
и сгруппировать их по фамилиям.
Map<String, List<Person>> lastNamesToPeople = Maps.mutable.empty(); for (Person person : this.people) { String lastName = person.getLastName(); List<Person> peopleWithLastName = lastNamesToPeople.get(lastName); if (peopleWithLastName == null) { peopleWithLastName = Lists.mutable.empty(); lastNamesToPeople.put(lastName, peopleWithLastName); } peopleWithLastName.add(person); } Assert.assertEquals(3, lastNamesToPeople.get("Smith").size());
Этот код можно упростить, используя Java 8 Streams и метод groupingBy
на Collectors
.
Map<String, List<Person>> lastNamesToPeople = this.people.stream() .collect(Collectors.groupingBy(Person::getLastName)); Assert.assertEquals(3, lastNamesToPeople.get("Smith").size()); // This code will throw a NullPointerException Assert.assertEquals(0, lastNamesToPeople.get("Smith1").size());
Этот код можно еще больше упростить, используя groupBy
из Eclipse Collections. В отличие от groupingBy
на Collectors
, который возвращает Map
и возвращает null для отсутствующих ключей, groupBy
возвращает Multimap
. Multimap
вернет пустые коллекции для отсутствующих ключей.
Multimap<String, Person> lastNamesToPeople = this.people.groupBy(Person::getLastName); Assert.assertEquals(3, lastNamesToPeople.get("Smith").size()); // This assertion passes, as a Multimap will return an empty list Assert.assertEquals(0, lastNamesToPeople.get("Smith1").size());
GroupByEach
Используйте groupByEach
, если вы хотите сгруппировать элементы коллекции вместе с помощью функции, которая вычисляет несколько ключей для каждого элемента. Метод принимает в качестве параметра тип Function
и возвращает Multimap
. Function
должен возвращать подтип Iterable
.
Вот обязательный пример Java, который берет список Person и группирует их по PetType
, которыми они владеют, чтобы убедиться, что PetType
уникальны.
Map<PetType, Set<Person>> peopleByPetType = Maps.mutable.empty(); for (Person person : this.people) { MutableList<Pet> pets = person.getPets(); for (Pet pet : pets) { PetType petType = pet.getType(); Set<Person> peopleWithPetType = peopleByPetType.get(petType); if (peopleWithPetType == null) { peopleWithPetType = Sets.mutable.empty(); peopleByPetType.put(petType, peopleWithPetType); } peopleWithPetType.add(person); } } Assert.assertEquals(2, peopleByPetType.get(PetType.CAT).size()); Assert.assertEquals(2, peopleByPetType.get(PetType.DOG).size());
Я не знаю, как сделать это, используя Java 8 Streams с Collectors
сегодня, без создания моего собственного Collector
. Вот решение, использующее метод Eclipse Collections groupByEach
.
Multimap<PetType, Person> multimap = this.people.groupByEach( Person::getPetTypes, Multimaps.mutable.set.empty()); Assert.assertEquals(2, multimap.get(PetType.CAT).size()); Assert.assertEquals(2, multimap.get(PetType.DOG).size());
В приведенном выше примере мне нужно было передать пустой MutableSetMultimap
в качестве целевой коллекции для заполнения, потому что по умолчанию было бы возвращено MutableListMultimap
. Это потому, что this.people - это MutableList
. Если бы поле this.people
было SetIterable
или подклассом, работало бы следующее.
Multimap<PetType, Person> multimap = this.people.groupByEach(Person::getPetTypes); Assert.assertEquals(2, multimap.get(PetType.CAT).size()); Assert.assertEquals(2, multimap.get(PetType.DOG).size());
Не упускайте возможности сэкономить время
Такие высокоуровневые методы, как countBy
, groupBy
и groupByEach
, могут количественно и качественно улучшить ваш код. Вы можете сэкономить себе и своей команде много времени в течение всего жизненного цикла проекта, используя API более высокого уровня, такие как By
методы в Eclipse Collections.
Кстати, иногда мелочи жизни и API имеют большое значение.
Коллекции Eclipse открыты для взносов. Если вам нравится библиотека, вы можете сообщить нам об этом, отметив ее на GitHub.