Как найти повторяющиеся значения на основе первых 10 цифр?

У меня есть сценарий, в котором у меня есть список, как показано ниже:

List<String> a1 = new ArrayList<String>();  
a1.add("1070045028000");
a1.add("1070045028001");
a1.add("1070045052000");
a1.add("1070045086000");
a1.add("1070045052001");
a1.add("1070045089000");

Ниже я попытался найти повторяющиеся элементы, но он будет проверять всю строку вместо частичной строки (первые 10 цифр).

for (String s:al){
         if (!unique.add(s)){  
             System.out.println(s);
         }
     }

Есть ли способ идентифицировать все дубликаты на основе первых 10 цифр числа, а затем найти самые низкие строки, сравнив их с дубликатами и добавив в другой список?

Примечание. Также всегда будет только 2 дубликата с каждым 10-значным строковым кодом!

Вы можете использовать .substring(0,10), чтобы получить первые десять цифр для сравнения. Не могли бы вы уточнить, что вы подразумеваете под самыми низкими строками? Являются ли они строками ниже определенного порогового значения или ниже порогового значения в пределах значений дубликатов?

Politic Revolutionnaire 24.07.2019 20:02

Где та часть, где вы работаете над первыми 10 цифрами?

f1sh 24.07.2019 20:03

@PoliticRevolutionnaire я имею в виду код с наименьшим номером при сравнении

Raj Raichand 24.07.2019 20:04

@RajRaichand, чтобы сравнить любые два числа, просто, вы можете просто использовать метод .compareTo между строками или вы можете прочитать их как целые числа и сравнить таким образом. Вы хотите взять все числа, которые имеют общие первые 10 цифр, а затем добавить только наименьшее из них в другой список?

Politic Revolutionnaire 24.07.2019 20:08

@PoliticRevolutionnaire Позвольте мне прояснить это .... сначала я хочу найти повторяющиеся строки, используя только первые 10 цифр .... после получения дубликатов я хочу сравнить полное строковое значение 13 цифр и найти наименьший код строки на основе сравнения и добавить этот строковый код в другой список

Raj Raichand 24.07.2019 20:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
5
204
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Хотя я ненавижу делать твою домашнюю работу за тебя, это было весело. :/

public static void main(String[] args) {
    List<String> al=new ArrayList<>();
    al.add("1070045028000");
    al.add("1070045028001");
    al.add("1070045052000");
    al.add("1070045086000");
    al.add("1070045052001");
    al.add("1070045089000");

    List<String> ret=new ArrayList<>();
    for(String a:al) {
        boolean handled = false;
        for(int i=0;i<ret.size();i++){
            String ri = ret.get(i);
            if (ri.substring(0, 10).equals(a.substring(0,10))) {
                Long iri = Long.parseLong(ri);
                Long ia = Long.parseLong(a);
                if (ia < iri){
                    //a is smaller, so replace it in the list
                    ret.set(i, a);
                }
                //it was a duplicate, we are done with it
                handled = true;
                break;
            }
        }
        if (!handled) {
            //wasn't a duplicate, just add it
            ret.add(a);
        }
    }
    System.out.println(ret);
}

отпечатки

[1070045028000, 1070045052000, 1070045086000, 1070045089000]

Почему бы не использовать String.compareTo(String) вместо парсинга в Long?

Nexevis 24.07.2019 20:40

Но [1070045028000, 1070045052000, 1070045086000, 1070045089000] не является правильным ответом, так как ни 1070045086000, ни 1070045089000 не имеют дубликатов.

Holger 24.07.2019 20:43

Потому что это числа. Представьте, что вы сравниваете 012345678901 и 0123456789001. При сравнении строк вторая будет меньше (0<1 по индексу 11), а это не так.

f1sh 24.07.2019 20:43

@Holger Было ли требование добавлять только дубликаты? Вопрос не ясен

f1sh 24.07.2019 20:44

@ f1sh Имеет смысл, спасибо за объяснение. Также я предполагаю, что вопрос хотел тех, у которых не было дубликатов, как и у вас. Это не очень ясно.

Nexevis 24.07.2019 20:49

Хорошо, это не так ясно, но в противном случае это была бы довольно тривиальная задача. Между прочим, поскольку мы заставляем строки содержать ровно десять цифр, сравнения строк достаточно даже с начальными нулями; ваш контрпример включает строки разной длины, чего не может быть в результате substring(0, 10).

Holger 24.07.2019 20:50

@Holger да, это может случиться. мы используем только первые 10 символов для проверки «уникальности», но число за ним (которое мы сравниваем только при обнаружении 10-символьного дубликата) может иметь другую длину.

f1sh 24.07.2019 21:10

Вы можете группировать по (String s) -> s.substring(0, 10)

Map<String, List<String>> map = list.stream()
    .collect(Collectors.groupingBy(s -> s.substring(0, 10)));

map.values() даст вам Collection<List<String>>, где каждый List<String> — это список дубликатов.

{
1070045028=[1070045028000, 1070045028001], 
1070045089=[1070045089000], 
1070045086=[1070045086000], 
1070045052=[1070045052000, 1070045052001]
}

Если это одноэлементный список, дубликаты не найдены, и вы можете отфильтровать эти записи.

{
1070045028=[1070045028000, 1070045028001], 
1070045052=[1070045052000, 1070045052001]
}

Тогда проблема сводится к сокращению списка значений до одного значения.

[1070045028000, 1070045028001] -> 1070045028000

Мы знаем, что первые 10 символов одинаковые, при сравнении их можно не учитывать.

[1070045028000, 1070045028001] -> [000, 001]

Это все еще необработанные значения String, мы можем преобразовать их в числа.

[000, 001] -> [0, 1]

Натуральный Comparator<Integer> даст 0 как минимум.

0
0 -> 000 -> 1070045028000

Повторите это для всех списков в map.values(), и все готово.

Код будет

List<String> result = map
    .values()
    .stream()
    .filter(list -> list.size() > 1)
    .map(l -> l.stream().min(Comparator.comparingInt(s -> Integer.valueOf(s.substring(10)))).get())
    .collect(Collectors.toList());
Ответ принят как подходящий

Прямое решение цикла будет

List<String> a1 = Arrays.asList("1070045028000", "1070045028001",
    "1070045052000", "1070045086000", "1070045052001", "1070045089000");

Set<String> unique = new HashSet<>();
Map<String,String> map = new HashMap<>();

for(String s: a1) {
    String firstTen = s.substring(0, 10);
    if (!unique.add(firstTen)) map.put(firstTen, s);
}
for(String s1: a1) {
    String firstTen = s1.substring(0, 10);
    map.computeIfPresent(firstTen, (k, s2) -> s1.compareTo(s2) < 0? s1: s2);
}
List<String> minDup = new ArrayList<>(map.values());

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

Как вариант, мы можем добавить все элементы на карту, собрав их в списки, а затем выбрать минимум из тех, у которых размер больше единицы:

List<String> minDup = new ArrayList<>();
Map<String,List<String>> map = new HashMap<>();

for(String s: a1) {
    map.computeIfAbsent(s.substring(0, 10), x -> new ArrayList<>()).add(s);
}
for(List<String> list: map.values()) {
    if (list.size() > 1) minDup.add(Collections.min(list));
}

Эта логика напрямую выражается с помощью Stream API:

List<String> minDup = a1.stream()
    .collect(Collectors.groupingBy(s -> s.substring(0, 10)))
    .values().stream()
    .filter(list -> list.size() > 1)
    .map(Collections::min)
    .collect(Collectors.toList());

Поскольку вы сказали, что для каждого ключа будет только 2 дубликата, накладные расходы на сбор List перед выбором минимума незначительны.


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

List<String> minDup = a1.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(s -> s.substring(0, 10), Function.identity(),
            BinaryOperator.minBy(Comparator.<String>naturalOrder())),
        m -> new ArrayList<>(m.values())));

что эквивалентно

Map<String,String> map = new HashMap<>();
for(String s: a1) {
    map.merge(s.substring(0, 10), s, BinaryOperator.minBy(Comparator.naturalOrder()));
}
List<String> minDup = new ArrayList<>(map.values());

Общим для этих решений является то, что вам не нужно сначала идентифицировать дубликаты, поскольку, когда вы хотите также сохранить уникальные значения, задача сводится к выбору минимума при обнаружении минимума.

Вот еще один способ сделать это – создать Набор и сохранить только 10-значный префикс:

Set<String> set = new HashSet<>();
for (String number : a1) {
    String prefix = number.substring(0, 10);
    if (set.contains(prefix)) {
        System.out.println("found duplicate prefix [" + prefix + "], skipping " + number);
    } else {
        set.add(prefix);
    }
}

Другие вопросы по теме