Удаление дублированных значений из Map
Нижеприведенный код, должен удалить из изначального списка МАП mapOfSoNameName повторяющиеся по именам элементы т.е по value. Я писал код и застрял... Каким путем пошел:
- С помощью foreach беру элемент основого мапа mapOfSoNameName
- Создаю копию изначального мапа (все это позже в функцию оберну) в mapOfSoNameNameClone тут же удаляю из него элемент который взят из оснвоного мапа
- Далее прохожусь в каждом цикле foreach с помощью цикла while каждый элемент mapOfSoNameNameClone и сравниваю с каждым элементом mapOfSoNameName
И вот засада кроется в том что при условии if(!value.equals(entry.getValue())) копирует все значения, а по моей идее не должно и если я меняю условие if(value.equals(entry.getValue())) то копирует только те которые СОВПАДАЮТ. А в моей логике сортированый мап должен быть БЕЗ совпадающих....
public static void main(String[] args)
{
Map<String,String> mapOfSoNameName = new HashMap<>();
Map<String,String> mapOfSoNameNameSorted = new HashMap<>();
Map<String,String> mapOfSoNameNameTemp = new HashMap<>();
mapOfSoNameName.put("Петров","Иван");
mapOfSoNameName.put("Иванов","Иван");
mapOfSoNameName.put("Анисимов","Николай");
mapOfSoNameName.put("Баширов","Азамат");
mapOfSoNameName.put("Викулов","Сергей");
mapOfSoNameName.put("Мешников","Степан");
mapOfSoNameName.put("Мясников","Борис");
mapOfSoNameName.put("Каспаров","Гари");
mapOfSoNameName.put("Златин","Гари");
mapOfSoNameName.put("Южаков","Ярослав");
int i = 1;// для проверки
for(Map.Entry<String,String> pairString : mapOfSoNameName.entrySet())
{
String key = pairString.getKey();
String value = pairString.getValue();
Map<String,String> mapOfSoNameNameClone = new HashMap<>();
mapOfSoNameNameClone.putAll(mapOfSoNameName);
mapOfSoNameNameClone.remove(key, value);
Iterator <Map.Entry<String,String>> itr = mapOfSoNameNameClone.entrySet().iterator();
while(itr.hasNext())
{
Map.Entry<String,String> entry = itr.next();
if(!value.equals(entry.getValue()))
{
mapOfSoNameNameSorted.put(entry.getKey(), entry.getValue());
}
}
System.out.println(i + " Изначальный список - " + mapOfSoNameName); //Для проверки
System.out.println(i + " Клон Изначального - " + mapOfSoNameNameClone); //Для проверки
System.out.println(i + " Отсортированный - " + mapOfSoNameNameSorted); // Для проверки
i++; // для проверки
mapOfSoNameNameClone.clear();
}
}
Ответы (1 шт):
Сейчас по коду получается, что включаются все люди, для которых есть хоть один другой человек с несовпадающим именем. Т.е. если в списке есть люди с разными именами, то попадут все, а если у всех имя одинаковое, то никто.
Чтобы исправить код нужно для каждого человека проверять, что нет тезок, и только потом добавлять. Простое исправление может выглядеть так:
//есть ли тезка
boolean hasNamesake = false;
Iterator<Map.Entry<String, String>> itr = mapOfSoNameNameClone.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> entry = itr.next();
if (value.equals(entry.getValue())) {
//нашли тезку
hasNamesake = true;
break;
}
}
if (!hasNamesake) {
//нету тезки, добавляем
mapOfSoNameNameSorted.put(key, value);
}
Но проще обойтись вообще без цикла с помощью метода containsValue:
Map<String, String> mapOfSoNameNameClone = new HashMap<>();
mapOfSoNameNameClone.putAll(mapOfSoNameName);
mapOfSoNameNameClone.remove(key, value);
if (!mapOfSoNameNameClone.containsValue(value)) {
mapOfSoNameNameSorted.put(key, value);
}
Еще лучше будет не клонировать мап на каждом шагу, а проверять дубликаты с помощью множеств.
//имена
Set<String> names = new HashSet<>();
//дубликаты
Set<String> duplicatedNames = new HashSet<>();
for (String name : mapOfSoNameName.values()) {
if (names.contains(name)) {
duplicatedNames.add(name);
}
names.add(name);
}
for (Map.Entry<String, String> pairString : mapOfSoNameName.entrySet()) {
String key = pairString.getKey();
String value = pairString.getValue();
if (!duplicatedNames.contains(value)) {
mapOfSoNameNameSorted.put(key, value);
}
System.out.println(i + " Изначальный список - " + mapOfSoNameName); //Для проверки
System.out.println(i + " Отсортированный - " + mapOfSoNameNameSorted); // Для проверки
i++; // для проверки
}
Можно написать короче, хоть и не так эффективно, через Stream API:
Collection<String> names = mapOfSoNameName.values();
Set<String> duplicatedNames = names.stream().filter(n -> Collections.frequency(names, n) > 1).collect(Collectors.toSet());
Map<String, String> mapOfSoNameNameSorted = mapOfSoNameName
.entrySet()
.stream()
.filter(a -> !duplicatedNames.contains(a.getValue()))
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
System.out.println(i + " Отсортированный - " + mapOfSoNameNameSorted);
Или еще короче:
Map<String, String> mapOfSoNameNameSorted = mapOfSoNameName.entrySet()
.stream()
.filter(a -> Collections.frequency(mapOfSoNameName.values(), a.getValue()) == 1)
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
P.S. Если это учебная задача, то все ОК. Но если практическая, то так хранить имена-фамилии очень странно, т.к. Map не поддерживает однофамильцев.
P.P.S. HashMap не обеспечивает что элементы будут идти по порядку. Если нужно сортировать фамилии по алфавиту, используйте TreeMap, если нужно хранить в порядке вставки, то LinkedHashMap