Как да броим дублиращи се елементи в Arraylist

1. Общ преглед

В този кратък урок ще разгледаме няколко различни начина за преброяване на дублираните елементи в ArrayList .

2. Цикъл с Map.put ()

Очакваният ни резултат ще бъде обект Map , който съдържа всички елементи от списъка за въвеждане като ключове и броя на всеки елемент като стойност.

Най-лесното решение за постигане на това би било да се премине през списъка с данни и за всеки елемент:

  • ако resultMap съдържа елемента, ние увеличаваме брояч с 1
  • в противен случай поставяме нов запис на картата (елемент, 1) на картата
public  Map countByClassicalLoop(List inputList) { Map resultMap = new HashMap(); for (T element : inputList) { if (resultMap.containsKey(element)) { resultMap.put(element, resultMap.get(element) + 1L); } else { resultMap.put(element, 1L); } } return resultMap; }

Това изпълнение има най-добра съвместимост, тъй като работи за всички съвременни версии на Java.

Ако не се нуждаем от съвместимостта преди Java 8, можем да опростим нашия метод допълнително:

public  Map countByForEachLoopWithGetOrDefault(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.put(e, resultMap.getOrDefault(e, 0L) + 1L)); return resultMap; }

След това нека създадем входен списък, за да тестваме метода:

private List INPUT_LIST = Lists.list( "expect1", "expect2", "expect2", "expect3", "expect3", "expect3", "expect4", "expect4", "expect4", "expect4"); 

А сега нека го проверим:

private void verifyResult(Map resultMap) { assertThat(resultMap) .isNotEmpty().hasSize(4) .containsExactly( entry("expect1", 1L), entry("expect2", 2L), entry("expect3", 3L), entry("expect4", 4L)); } 

Ще използваме повторно тази тестова система за останалите подходи.

3. Цикъл с Map.compute ()

В Java 8, удобният метод за изчисляване () е въведен в интерфейса на картата . Можем да използваме и този метод:

public  Map countByForEachLoopWithMapCompute(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.compute(e, (k, v) -> v == null ? 1L : v + 1L)); return resultMap; }

Забележете (k, v) -> v == null? 1L: v + 1L е функцията за преназначаване, която реализира интерфейса BiFunction . За даден ключ той или връща текущата си стойност, увеличена с единица (ако ключът вече присъства в картата), или връща стойността по подразбиране на един.

За да направим кода по-четим, бихме могли да извлечем функцията за пренасочване към нейната променлива или дори да я вземем като входен параметър за countByForEachLoopWithMapCompute.

4. Цикъл с Map.merge ()

Когато използваме Map.compute () , трябва да обработваме нулевите стойности изрично - например, ако съпоставяне за даден ключ не съществува. Ето защо внедрихме нулева проверка в нашата функция за пренасочване. Това обаче не изглежда красиво.

Нека изчистим нашия код допълнително с помощта на метода Map.merge () :

public  Map countByForEachLoopWithMapMerge(List inputList) { Map resultMap = new HashMap(); inputList.forEach(e -> resultMap.merge(e, 1L, Long::sum)); return resultMap; }

Сега кодът изглежда чист и кратък.

Нека да обясним как работи merge () . Ако съпоставянето за даден ключ не съществува или стойността му е нула , той свързва ключа с предоставената стойност. В противен случай той изчислява нова стойност, използвайки функцията за пренасочване и съответно актуализира картографирането.

Забележете, че този път използвахме Long :: sum като реализация на BiFunction интерфейс.

5. Stream API Collectors.toMap ()

Тъй като вече говорихме за Java 8, не можем да забравим мощния Stream API. Благодарение на Stream API можем да разрешим проблема по много компактен начин.

В toMap () колектор ни помага да конвертирате списъка за въвеждане в Карта :

public  Map countByStreamToMap(List inputList) { return inputList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum)); }

В toMap () е удобен колектор, който може да ни помогне да се превърне в потока на различни Карта реализации.

6. Stream API Collectors.groupingBy () и Collectors.counting ()

С изключение на toMap () , нашият проблем може да бъде решен от два други колектора, grouBB () и counting () :

public  Map countByStreamGroupBy(List inputList) { return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting())); }

Правилното използване на Java 8 Collectors прави нашия код компактен и лесен за четене.

7. Заключение

В тази бърза статия илюстрирахме различни начини за изчисляване на броя на дублиращите се елементи в списък.

Ако искате да разгледате самия ArrayList, можете да разгледате справочната статия.

Както винаги, пълният изходен код е достъпен в GitHub.