У меня есть сценарий, в котором у меня есть список, как показано ниже:
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-значным строковым кодом!
Где та часть, где вы работаете над первыми 10 цифрами?
@PoliticRevolutionnaire я имею в виду код с наименьшим номером при сравнении
@RajRaichand, чтобы сравнить любые два числа, просто, вы можете просто использовать метод .compareTo между строками или вы можете прочитать их как целые числа и сравнить таким образом. Вы хотите взять все числа, которые имеют общие первые 10 цифр, а затем добавить только наименьшее из них в другой список?
@PoliticRevolutionnaire Позвольте мне прояснить это .... сначала я хочу найти повторяющиеся строки, используя только первые 10 цифр .... после получения дубликатов я хочу сравнить полное строковое значение 13 цифр и найти наименьший код строки на основе сравнения и добавить этот строковый код в другой список




Хотя я ненавижу делать твою домашнюю работу за тебя, это было весело. :/
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?
Но [1070045028000, 1070045052000, 1070045086000, 1070045089000] не является правильным ответом, так как ни 1070045086000, ни 1070045089000 не имеют дубликатов.
Потому что это числа. Представьте, что вы сравниваете 012345678901 и 0123456789001. При сравнении строк вторая будет меньше (0<1 по индексу 11), а это не так.
@Holger Было ли требование добавлять только дубликаты? Вопрос не ясен
@ f1sh Имеет смысл, спасибо за объяснение. Также я предполагаю, что вопрос хотел тех, у которых не было дубликатов, как и у вас. Это не очень ясно.
Хорошо, это не так ясно, но в противном случае это была бы довольно тривиальная задача. Между прочим, поскольку мы заставляем строки содержать ровно десять цифр, сравнения строк достаточно даже с начальными нулями; ваш контрпример включает строки разной длины, чего не может быть в результате substring(0, 10).
@Holger да, это может случиться. мы используем только первые 10 символов для проверки «уникальности», но число за ним (которое мы сравниваем только при обнаружении 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);
}
}
Вы можете использовать .substring(0,10), чтобы получить первые десять цифр для сравнения. Не могли бы вы уточнить, что вы подразумеваете под самыми низкими строками? Являются ли они строками ниже определенного порогового значения или ниже порогового значения в пределах значений дубликатов?