Как преобразовать список <SomeType>в карту, сохранив все повторяющиеся ключи и значения - PullRequest
0 голосов
/ 21 января 2019

У меня был список, как показано ниже:

List<Address> address;

где

Address:
city
country
state

Я хочу преобразовать его во что-то вроде ниже

Map <String,String> convertedMap=list.stream().collect
(Collectors.toMap(Address:getCity+Address:getCountry ,Address:getState)); 

Я хочу сохранить все дублирующиеся ключи и значения в сгенерированной карте, как показано ниже.

(key=City1country1, value= state1) ,(key=City1country1, value=state2),(key=City1country1,value= state1) ;

Ответы [ 4 ]

0 голосов
/ 21 января 2019

использовать toMap со следующей сигнатурой метода

   toMap(Function k, Function v,BinaryOperator m)

BinaryOperator информирует JVM о том, что делать, если он обнаруживает дубликаты ключей.В вашем случае, если он встречает дубликаты, он предполагает их объединение.

, таким образом s1 + "," + s2

, следовательно, ваше решение становится

  Map<String, String> map = list.stream().
            collect(Collectors.toMap((address -> {
                String y = address.getCity() + address.getCountry();
                return y + s;
            }), Address::getState, (s1, s2) -> s1 + "," + s2));

выход

     country3City3  state3
     country1City1  state1,state2,state1

      Process finished with exit code 0
0 голосов
/ 21 января 2019

Этот тип вещей является общим требованием для множества задач кодирования, вот быстрый способ выполнить то, что вам нужно, если у вас есть ограничения по времени, я решил использовать набор, а не список, чтобы время поиска было O(1) вместо O (N):

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

class MapExample {
  public static void main(String[] args) {
    List<String> addressesList = new ArrayList<>();
    addressesList.add("Portland USA ME");
    addressesList.add("Portland USA OR");
    addressesList.add("Boston USA MA");
    System.out.println(cityToStatesMap(addressesList));
  }

  private static Map<String, Set<String>> cityToStatesMap(List<String> addressesList) {
    Map<String, Set<String>> result = new HashMap<>();
    if (addressesList == null || addressesList.size() == 0) {
      return result;
    }
    for (String address : addressesList) {
      String[] addressComponents = address.split(" ");
      String city = addressComponents[0];
      String country = addressComponents[1];
      String state = addressComponents[2];
      String key = city + country;
      Set<String> states = result.computeIfAbsent(key, k -> new HashSet<>());
      states.add(state);
      result.put(key, states);
    }
    return result;
  }
}

Вывод:

{PortlandUSA=[ME, OR], BostonUSA=[MA]}

Примечание: я думаю, вы должны использовать что-то вроде , для разделителя, а не для пробела, так что будет легче иметь дело с городами с несколькими словами, например, Нью-Йорк, Сан-Франциско и т. Д.

0 голосов
/ 21 января 2019

Вы не можете использовать Collectors.toMap, потому что для этого требуются уникальные ключи. Вместо этого вам нужен groupingBy, который возвращает собранные значения для каждого ключа:

class Location {
    public final String city;
    public final String country;

    Location(Address address) {
        this.city = address.getCity();
        this.country = address.getCountry();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Location) {
            Location other = (Location) obj;
            return Objects.equals(this.city, other.city) &&
                   Objects.equals(this.country, other.country);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(city, country);
    }
}

Map<Location, Set<String>> statesByLocation = addresses.stream().collect(
    Collectors.groupingBy(Location::new,
        Collectors.mapping(Address::getState, Collectors.toSet())));

Вы можете использовать различные хаки для объединения города и страны (например, список из двух строк), но вам действительно нужно просто создать ключевой класс, как показано выше. С кодом будет гораздо проще работать, особенно если у вас (или другого разработчика) должна быть причина вернуться к нему через шесть месяцев.

Что касается методов Collector:

  • Collectors.groupingBy(Location::new эквивалентно address -> new Location(address) и создает каждый ключ на карте.
  • Collectors.mapping(Address::getState, Collectors.toSet()) означает, что для каждого адреса, который соответствует определенному ключу, будет вызываться getState (), а результирующие строки будут объединяться в Set для формирования значения Map.

Лично я думаю, что я бы выбрал простой цикл for вместо трудно читаемого Stream-подхода:

Map<Location, Set<String>> statesByLocation = new HashMap<>();
for (Address address : addresses) {
    statesByLocation.computeIfAbsent(new Location(address),
        k -> new HashSet<String>()).add(address.getState());
}
0 голосов
/ 21 января 2019

Как упоминалось в комментарии, на картах не хранятся дублированные ключи, поэтому вы должны использовать Map<String, List<String>> вместо Map<String, String>.

Чтобы подвести итог, вам просто нужно использовать метод Collectors.toMap с параметром mergeFunction, где вы можете обрабатывать дублированные ключи. Эта операция будет вызываться каждый раз, когда появляются одинаковые клавиши. В этом случае мы просто объединяем два списка в один. Посмотрите на приведенный ниже код (скомпилированный с JDK 11), я считаю, что он делает именно то, что вам нужно, и печатает ожидаемый результат (конечно, используя List<String>).

import java.util.*;
import java.util.stream.Collectors;

public class ListToMapWithDuplicatedKeysSample {

    public static void main(String[] args) {
        List<Address> addresses = List.of(
                new Address("City1", "country1", "state1"),
                new Address("City1", "country1", "state2"),
                new Address("City1", "country1", "state1"),
                new Address("City3", "country3", "state3")
        );
        Map<String, List<String>> result = addresses.stream()
                .collect(
                        Collectors.toMap(
                                address -> address.getCity() + address.getCountry(),
                                address -> Collections.singletonList(address.getState()),
                                ListToMapWithDuplicatedKeysSample::mergeEntriesWithDuplicatedKeys
                        )
                );
        System.out.println(result);
    }

    private static List<String> mergeEntriesWithDuplicatedKeys(List<String> existingResults, List<String> newResults) {
        List<String> mergedResults = new ArrayList<>();
        mergedResults.addAll(existingResults);
        mergedResults.addAll(newResults);
        return mergedResults;
    }

    private static class Address {

        private final String city;
        private final String country;
        private final String state;

        public Address(String city, String country, String state) {
            this.city = city;
            this.country = country;
            this.state = state;
        }

        String getState() {
            return state;
        }

        String getCountry() {
            return country;
        }

        String getCity() {
            return city;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...