Фильтрация и преобразование коллекций в Гуаве
1. обзор
В этом руководстве мы покажем, как использоватьfilter and transform collections with Guava.
Мы будем фильтровать с помощью предикатов, преобразовывать с помощью функций, которые предоставляет библиотека, и, наконец, мы увидим, как объединить фильтрацию и преобразование.
Дальнейшее чтение:
Новый поток, компаратор и коллектор в Гуаве 21
Краткое и практическое руководство по инструментам в пакете common.collect в Guava 21.
Руководство по Guava Multimap
Краткое руководство по Guava Multimap по сравнению со стандартным java.util.Map
Руководство по Guava RangeSet
Узнайте, как использовать Google Guava RangeSet и его реализации на практических примерах.
2. Отфильтровать коллекцию
Начнем с простого примераfiltering a collection. Мы будем использовать готовый предикат, предоставляемый библиотекой и созданный с помощью служебного классаPredicates:
@Test
public void whenFilterWithIterables_thenFiltered() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Iterable result
= Iterables.filter(names, Predicates.containsPattern("a"));
assertThat(result, containsInAnyOrder("Jane", "Adam"));
}
Как видите, мы фильтруемList имен, чтобы получить только имена, содержащие символ «a» - и для этого мы используемIterables.filter().
В качестве альтернативы мы также можем эффективно использовать APICollections2.filter():
@Test
public void whenFilterWithCollections2_thenFiltered() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result
= Collections2.filter(names, Predicates.containsPattern("a"));
assertEquals(2, result.size());
assertThat(result, containsInAnyOrder("Jane", "Adam"));
result.add("anna");
assertEquals(5, names.size());
}
Здесь следует отметить несколько моментов - во-первых, выводCollections.filter() равенa live view of the original collection - изменения в одном будут отражены в другом.
Также важно понимать, что теперьthe result is constrained by the predicate - если мы добавим элемент, который не удовлетворяет этому параметруPredicate, будет выданIllegalArgumentException:
@Test(expected = IllegalArgumentException.class)
public void givenFilteredCollection_whenAddingInvalidElement_thenException() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result
= Collections2.filter(names, Predicates.containsPattern("a"));
result.add("elvis");
}
3. Написать собственный фильтрPredicate
Далее - давайте напишем собственныйPredicate вместо того, чтобы использовать тот, который предоставлен библиотекой. В следующем примере мы определим предикат, который получает только имена, начинающиеся с «A» или «J»:
@Test
public void whenFilterCollectionWithCustomPredicate_thenFiltered() {
Predicate predicate = new Predicate() {
@Override
public boolean apply(String input) {
return input.startsWith("A") || input.startsWith("J");
}
};
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result = Collections2.filter(names, predicate);
assertEquals(3, result.size());
assertThat(result, containsInAnyOrder("John", "Jane", "Adam"));
}
4. Объединить несколько предикатов
Мы можем объединить несколько предикатов, используяPredicates.or() иPredicates.and(). В следующем примере мы фильтруемList имен, чтобы получить имена, начинающиеся с «J» или не содержащие «a»:
@Test
public void whenFilterUsingMultiplePredicates_thenFiltered() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result = Collections2.filter(names,
Predicates.or(Predicates.containsPattern("J"),
Predicates.not(Predicates.containsPattern("a"))));
assertEquals(3, result.size());
assertThat(result, containsInAnyOrder("John", "Jane", "Tom"));
}
5. Удаление нулевых значений при фильтрации коллекции
Мы можем очистить значенияnull из коллекции, отфильтровав их с помощьюPredicates.notNull(), как в следующем примере:
@Test
public void whenRemoveNullFromCollection_thenRemoved() {
List names =
Lists.newArrayList("John", null, "Jane", null, "Adam", "Tom");
Collection result =
Collections2.filter(names, Predicates.notNull());
assertEquals(4, result.size());
assertThat(result, containsInAnyOrder("John", "Jane", "Adam", "Tom"));
}
6. Проверить, все ли элементы в коллекции соответствуют условию
Затем давайте проверим, все ли элементы в коллекции соответствуют определенному условию. Мы будем использоватьIterables.all(), чтобы проверить, все ли имена содержат «n» или «m», затем мы проверим, все ли элементы содержат «a»:
@Test
public void whenCheckingIfAllElementsMatchACondition_thenCorrect() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
boolean result = Iterables.all(names, Predicates.containsPattern("n|m"));
assertTrue(result);
result = Iterables.all(names, Predicates.containsPattern("a"));
assertFalse(result);
}
7. Преобразовать коллекцию
А теперь давайте посмотрим, какtransform a collection using a Guava Function. В следующем примере мы преобразуемList имен вList изIntegers (длина имени) с помощьюIterables.transform():
@Test
public void whenTransformWithIterables_thenTransformed() {
Function function = new Function() {
@Override
public Integer apply(String input) {
return input.length();
}
};
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Iterable result = Iterables.transform(names, function);
assertThat(result, contains(4, 4, 4, 3));
}
Мы также можем использовать APICollections2.transform(), как в следующем примере:
@Test
public void whenTransformWithCollections2_thenTransformed() {
Function func = new Function(){
@Override
public Integer apply(String input) {
return input.length();
}
};
List names =
Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result = Collections2.transform(names, func);
assertEquals(4, result.size());
assertThat(result, contains(4, 4, 4, 3));
result.remove(3);
assertEquals(3, names.size());
}
Обратите внимание, что выводCollections.transform() равенa live view of the original Collection - изменения одного влияют на другое.
И - как и раньше - если мы попытаемся добавить элемент к выходуCollection, будет выброшенUnsupportedOperationException.
8. СоздатьFunction изPredicate
Мы также можем создатьFunction изPredicate, используяFunctions.fromPredicate(). Это, конечно, будет функция, которая преобразует входные данные вBoolean в соответствии с условием предиката.
В следующем примере мы преобразуемList имен в список логических значений, где каждый элемент представляет, содержит ли имя «m»:
@Test
public void whenCreatingAFunctionFromAPredicate_thenCorrect() {
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result =
Collections2.transform(names,
Functions.forPredicate(Predicates.containsPattern("m")));
assertEquals(4, result.size());
assertThat(result, contains(false, false, true, true));
}
9. Состав двух функций
Далее - давайте посмотрим, как преобразовать коллекцию с помощью составногоFunction.
Functions.compose() возвращает композицию двух функций, поскольку он применяет второйFunction к выходу первогоFunction.
В следующем примере - первыйFunction преобразует имя в его длину, затем второйFunction преобразует длину в значениеboolean, которое представляет, является ли длина имени четной:
@Test
public void whenTransformingUsingComposedFunction_thenTransformed() {
Function f1 = new Function(){
@Override
public Integer apply(String input) {
return input.length();
}
};
Function f2 = new Function(){
@Override
public Boolean apply(Integer input) {
return input % 2 == 0;
}
};
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result =
Collections2.transform(names, Functions.compose(f2, f1));
assertEquals(4, result.size());
assertThat(result, contains(true, true, true, false));
}
10. Объедините фильтрацию и преобразование
А теперь - давайте посмотрим еще один классный API, который есть в Guava - тот, который фактически позволит нам объединить фильтрацию и преобразование вместе -FluentIterable.
В следующем примере мы фильтруемList имен, а затем преобразуем их с помощьюFluentIterable:
@Test
public void whenFilteringAndTransformingCollection_thenCorrect() {
Predicate predicate = new Predicate() {
@Override
public boolean apply(String input) {
return input.startsWith("A") || input.startsWith("T");
}
};
Function func = new Function(){
@Override
public Integer apply(String input) {
return input.length();
}
};
List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
Collection result = FluentIterable.from(names)
.filter(predicate)
.transform(func)
.toList();
assertEquals(2, result.size());
assertThat(result, containsInAnyOrder(4, 3));
}
Стоит отметить, что в некоторых случаях императивная версия более читабельна и должна быть предпочтительнее функционального подхода.
11. Заключение
Наконец, мы узнали, как фильтровать и преобразовывать коллекции, используя Guava. Мы использовали APICollections2.filter() иIterables.filter() для фильтрации, а такжеCollections2.transform() иIterables.transform() для преобразования коллекций.
Наконец, мы кратко рассмотрели очень интересный APIFluentIterable fluent, сочетающий в себе фильтрацию и преобразование.
Реализация всех этих примеров и фрагментов кодаcan be found in the GitHub project - это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.