Найти Ρ‚Π΅ΠΌΡƒ
81 подписчик

πŸ–₯ Π“Ρ€ΡƒΠΏΠΏΠΈΡ€ΠΎΠ²ΠΊΠ° элСмСнтов ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ Π±Π΅Π· Stream API


Π•ΡΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π³Ρ€ΡƒΠΏΠΏΠΈΡ€ΡƒΠ΅Ρ‚ элСмСнты ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ ΠΈΠ· нСскольким полям.

public void test() {
List<Item> itemsList = ...//Π§Ρ‚Π΅Π½ΠΈΠ΅ ΠΈΠ· XML;

Map<List<Object>, List<Item>> groupBy =
itemsList.stream().collect(Collectors.groupingBy(
i -> Arrays.asList(i.id, i.city, i.order_status)));

for (Map.Entry<List<Object>, List<Item>> t : groupBy.entrySet()) {
System.out.println(t.getKey());
for (Item item : t.getValue()) {
System.out.println(" " + item);
}
}
}
Как ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠ΅ ΠΆΠ΅ Π±Π΅Π· Stream API?

Для этого ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΌΠ°ΠΏΡƒ с явным ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ Ρ‚ΠΈΠΏΠ° ΠΊΠ»ΡŽΡ‡Π° ΠΈ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ, Π² Ρ†ΠΈΠΊΠ»Π΅ ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΈΡˆΡŒ ΠΏΠΎ списку ΠΈ добавляСшь элСмСнты Π² ΠΌΠ°ΠΏΡƒ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ
Map::computeIfAbsent -- этот ΠΌΠ΅Ρ‚ΠΎΠ΄ ΠΏΡ€ΠΈ нСобходимости создаёт Π½ΠΎΠ²ΠΎΠ΅ ΠΈΠ»ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅-список, для ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ сразу ΠΆΠ΅ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ List:add.

ΠŸΡ€ΠΈ этом Π»ΡƒΡ‡ΡˆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ LinkedHashMap, Ρ‡Ρ‚ΠΎΠ±Ρ‹ порядок элСмСнтов Π² Π½Π΅ΠΉ Π±Ρ‹Π» ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ (HashMap порядка Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚).

public static Map<List<Object>, List<Item>> groupBy(List<Item> list) {
Map<List<Object>, List<Item>> map = new LinkedHashMap<>();
for (Item item : list) {
List<Object> key = Arrays.asList(item.getId(), item.getCity(), item.getOrderStatus());
map.computeIfAbsent(key, k -> new ArrayList<>()).add(item);
}
return map;
}

Π‘Π»Π΅Π΄ΡƒΠ΅Ρ‚ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ вмСсто списка ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π΅Π΅ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ΠΊΠΈΠΉ ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹ΠΉ класс, для ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ hashCode ΠΈ equals.

Π­Ρ‚ΠΎ достаточно просто ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ record, ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½ΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Ρ… с Java 16, ΠΈΠ»ΠΈ Lombok-Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ @Data / @AllArgsConstructor.

enum OrderStatus {NEW, PROCESSING, COMPLETED, CANCELED};

// ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Ρ‹ конструктор, Π³Π΅Ρ‚Ρ‚Π΅Ρ€Ρ‹ Π±Π΅Π· прСфикса `get`, hashCode / equals, toString
record MyKey(long id, String city, OrderStatus status) {
public MyKey(Item item) {
this(item.getId(), item.getCity(), item.getOrderStatus());
}
}

public static Map<MyKey, List<Item>> groupBy(List<Item> list) {
Map<MyKey, List<Item>> map = new LinkedHashMap<>();
list.forEach(item -> map
.computeIfAbsent(new MyKey(item), k -> new ArrayList<>())
.add(item)
);

// ΠΎΡ‚Π»Π°Π΄ΠΎΡ‡Π½Ρ‹ΠΉ Π²Ρ‹Π²ΠΎΠ΄
map.forEach((key, val) -> {
System.out.println(key);
val.forEach(item -> System.out.println("\t" + item));
});

return map;
}

ΠŸΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ созданиС Ρ‚Π°ΠΊΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ для любого ΠΊΠ»ΡŽΡ‡Π°, Ссли ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒ Π½Π΅ΠΊΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ-ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΎΡ€ Function<Item, Key>, Π² послСднСм ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Ρ‚Π°ΠΊΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ Π±Ρ‹Π» конструктор ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ° MyKey(Item item). НапримСр, для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠ»ΡŽΡ‡Π°-списка ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄:

// MyClass
public static List<Object> keyIdCityStatus(Item item) {
return Arrays.asList(item.getId(), item.getCity(), item.getOrderStatus());
}
ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ выглядит Ρ‚Π°ΠΊ:

public static<K> Map<K, List<Item>> groupBy(Function<Item, K> keyBuilder, List<Item> list) {
Map<K, List<Item>> map = new LinkedHashMap<>();

list.forEach(item -> map
.computeIfAbsent(keyBuilder.apply(item), k -> new ArrayList<>())
.add(item)
);

return map;
}
БоотвСтствСнно Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ для любого ΠΊΠ»ΡŽΡ‡Π°:

// ссылка Π½Π° Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ MyClass::keyIdCityStatus
Map<List<Object>, List<Item>> keyListMap = groupBy(MyClass::keyIdCityStatus, list);

// ссылка Π½Π° конструктор MyKey::new
Map<MyKey, List<Item>> keyRecordMap = groupBy(MyKey::new, list);

2 ΠΌΠΈΠ½ΡƒΡ‚Ρ‹