Сложная сортировка Java ArrayList

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

т.е. ACLK_SCRN_200MHZ_DATAB_S0_P1:8 должен стоять перед ACLK_SCRN_200MHZ_DATAB_S0_P10:8

начиная с 1 < 10, и так далее с остальными строками.

Вопрос: Как мне выполнить буквенно-цифровую сортировку, чтобы получить правильный порядок?

Должен ли я выполнять свою функцию? Как бы это выглядело?

List<String> trendList = new ArrayList<String>(80000);

Collections.sort(trendList);

ACLK_SCRN_200MHZ_DATAB_S0_P0:8
ACLK_SCRN_200MHZ_DATAB_S0_P10:8
ACLK_SCRN_200MHZ_DATAB_S0_P11:8
ACLK_SCRN_200MHZ_DATAB_S0_P12:8
ACLK_SCRN_200MHZ_DATAB_S0_P13:8
ACLK_SCRN_200MHZ_DATAB_S0_P14:8
ACLK_SCRN_200MHZ_DATAB_S0_P15:8
ACLK_SCRN_200MHZ_DATAB_S0_P1:8
ACLK_SCRN_200MHZ_DATAB_S0_P2:8
ACLK_SCRN_200MHZ_DATAB_S0_P3:8
ACLK_SCRN_200MHZ_DATAB_S0_P4:8
ACLK_SCRN_200MHZ_DATAB_S0_P5:8
ACLK_SCRN_200MHZ_DATAB_S0_P6:8
ACLK_SCRN_200MHZ_DATAB_S0_P7:8
ACLK_SCRN_200MHZ_DATAB_S0_P8:8
ACLK_SCRN_200MHZ_DATAB_S0_P9:8
ACLK_SCRN_200MHZ_DATAB_S1_P0:8
ACLK_SCRN_200MHZ_DATAB_S1_P10:8
ACLK_SCRN_200MHZ_DATAB_S1_P11:8
ACLK_SCRN_200MHZ_DATAB_S1_P12:8
ACLK_SCRN_200MHZ_DATAB_S1_P13:8
ACLK_SCRN_200MHZ_DATAB_S1_P14:8
ACLK_SCRN_200MHZ_DATAB_S1_P15:8
ACLK_SCRN_200MHZ_DATAB_S1_P1:8
ACLK_SCRN_200MHZ_DATAB_S1_P2:8
ACLK_SCRN_200MHZ_DATAB_S1_P3:8
ACLK_SCRN_200MHZ_DATAB_S1_P4:8
ACLK_SCRN_200MHZ_DATAB_S1_P5:8
MLC_C_SAMPLE
MLC_SAMPLE
SWR
TOUCHDOWN
TEST_REV

person Mark Kennedy    schedule 28.02.2013    source источник
comment
Вы можете написать регулярное выражение и использовать настраиваемый строковый компаратор для чисел. Однако было бы разумнее реализовать объект, который анализирует эти строки и реализовать собственный метод compareTo() или Comparator для объектов.   -  person Andrew Mao    schedule 28.02.2013


Ответы (5)


Вы получаете этот результат, потому что он сравнивает строки, используя только String.compareTo по умолчанию, который использует необработанные коды символов. ':' появляется после '0' - '9', чтобы потом сопоставлять.

Вам нужно будет предоставить свой Comparator и позвонить

Collections.sort(trendList, new CustomComparator());
person Alan Krueger    schedule 28.02.2013

Вы можете сделать это со своим собственным компаратором:

class TrendListComparator implements Comparator<String> {
     // retrieve the P and S
     final Pattern p = Pattern.compile(".*_S(\\d+)_P(\\d+).*");
     public int compare(String str1, String str2) {
         Matcher m1 = p.matcher(str1);
         Matcher m2 = p.matcher(str2);
         if (m1.matches() && m2.matches()) {
             Integer s1 = Integer.valueOf(m.group(1));
             Integer p1 = Integer.valueOf(m.group(2));
             Integer s2 = Integer.valueOf(m.group(1));
             Integer p2 = Integer.valueOf(m.group(2));
             // compare
             return s1.equals(s2) ? p1.compareTo(p2) : s1.compareTo(s2);
         } else {
             return str1.compareTo(str2); // standard sort if no P and S
         }
     }
}

Collections.sort(trendList, new TrendListComparator());

Здесь вы можете увидеть работающий пример: http://ideone.com/AleOEy

Вы даже можете сделать это с помощью только одного Matcher и повторно использовать его во время одной и той же сортировки:

class TrendListComparator implements Comparator<String> {
     // retrieve the P and S
     final Matcher m = Pattern.compile(".*_S(\\d+)_P(\\d+).*_S(\\d+)_P(\\d+).*").matcher("");
     public int compare(String str1, String str2) {
         if (m.reset(str1 + str2).matches()) {
             Integer s1 = Integer.valueOf(m.group(1));
             Integer p1 = Integer.valueOf(m.group(2));
             Integer s2 = Integer.valueOf(m.group(3));
             Integer p2 = Integer.valueOf(m.group(4));
             // compare
             return s1.equals(s2) ? p1.compareTo(p2) : s1.compareTo(s2);
         } else {
             return str1.compareTo(str2); // standard sort if no P and S
         }
     }
}

См .: http://ideone.com/EsfFPj

person Alex    schedule 28.02.2013
comment
Привет, мне очень нравится этот код, но как будет выполняться проверка ошибок? Как проверить, есть ли в строке целое число? Я все время ошибаюсь! - person Mark Kennedy; 01.03.2013
comment
@ user1022944 Здесь вы можете увидеть работающий пример: ideone.com/AleOEy - person Alex; 01.03.2013

Вы можете передать собственный компаратор в функцию Collections.sort():

Collections.sort(trendList, new Comparator<String>() {

    @Override
    public int compare(String s1, String s2) {
        int sValue1 = s1.split(...) // split the strings to retrieve their values
        int sValue2 = // ...
        int pValue1 = // ...
        int pValue2 = // ...
        if (sValue1 > sValue2) { return 1; }
        if (sValue1 < sValue2) { return -1; }
        if (pValue1 > pValue2) { return 1; }
        if (pValue 1 < pValue2) { return -1; }
        return 0;
    }

});
person sdasdadas    schedule 28.02.2013

Вы можете создать свой собственный класс, реализующий _1 _ интерфейс. В методе compare вам нужно будет проанализировать вашу строку и извлечь числа из соответствующих частей строки, которые необходимо отсортировать по числам. Метод должен возвращать число меньше 0, если s1 меньше s2, 0, если s1 равно s2, и больше 0, если s1 больше s2.

public class MyTrendListComparator implements Comparator<String>
{
    public int compare(String s1, String s2)
    {
        // Parse and compare here.
    }
}

Затем передайте экземпляр вашего компаратора в Collections.sort:

Collections.sort(trendList, new MyTrendListComparator());
person rgettman    schedule 28.02.2013

Вот код:

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CustomSorter {
   public static void main( String[] args ) {
      String[] data = {
         "ACLK_SCRN_200MHZ_DATAB_S0_P0:8",
         "ACLK_SCRN_200MHZ_DATAB_S0_P10:8",
         ...
         "ACLK_SCRN_200MHZ_DATAB_S1_P4:8",
         "ACLK_SCRN_200MHZ_DATAB_S1_P5:8",
      };
      List<String> trendList = Arrays.asList( data );
      Collections.sort( trendList, new Comparator< String >(){
         @Override public int compare( String o1, String o2 ){
            String[] p1 = o1.split( "_S\\d+_P" );
            String[] p2 = o2.split( "_S\\d+_P" );
            if( p1.length != 2 ) {
               throw new IllegalStateException("Unexpected item: '"+o1+"'");
            }
            if( p2.length != 2 ) {
               throw new IllegalStateException("Unexpected item: '"+o1+"'");
            }
            final int i1;
            try{
               i1 = Integer.parseInt(p1[1].substring(0,p1[1].indexOf(':')));
            } catch( NumberFormatException x ) {
               throw new IllegalStateException( "Unexpected item: '"+o1+"'");
            }
            final int i2;
            try{
               i2 = Integer.parseInt(p2[1].substring(0,p2[1].indexOf(':')));
            } catch( NumberFormatException x ) {
               throw new IllegalStateException("Unexpected item: '"+o2+"'");
            }
            int cmp = p1[0].compareTo( p2[0] );
            if( cmp == 0 ) {
               cmp = i1 - i2;
            }
            return cmp;
         }});
      for( String entry : trendList ) {
         System.out.println( entry );
      }
   }
}

и выходы:

ACLK_SCRN_200MHZ_DATAB_S0_P0:8
ACLK_SCRN_200MHZ_DATAB_S0_P1:8
ACLK_SCRN_200MHZ_DATAB_S0_P2:8
ACLK_SCRN_200MHZ_DATAB_S0_P3:8
ACLK_SCRN_200MHZ_DATAB_S0_P4:8
ACLK_SCRN_200MHZ_DATAB_S0_P5:8
ACLK_SCRN_200MHZ_DATAB_S0_P6:8
ACLK_SCRN_200MHZ_DATAB_S0_P7:8
ACLK_SCRN_200MHZ_DATAB_S0_P8:8
ACLK_SCRN_200MHZ_DATAB_S0_P9:8
ACLK_SCRN_200MHZ_DATAB_S0_P10:8
ACLK_SCRN_200MHZ_DATAB_S0_P11:8
ACLK_SCRN_200MHZ_DATAB_S0_P12:8
ACLK_SCRN_200MHZ_DATAB_S0_P13:8
ACLK_SCRN_200MHZ_DATAB_S0_P14:8
ACLK_SCRN_200MHZ_DATAB_S0_P15:8
ACLK_SCRN_200MHZ_DATAB_S1_P0:8
ACLK_SCRN_200MHZ_DATAB_S1_P1:8
ACLK_SCRN_200MHZ_DATAB_S1_P2:8
ACLK_SCRN_200MHZ_DATAB_S1_P3:8
ACLK_SCRN_200MHZ_DATAB_S1_P4:8
ACLK_SCRN_200MHZ_DATAB_S1_P5:8
ACLK_SCRN_200MHZ_DATAB_S1_P10:8
ACLK_SCRN_200MHZ_DATAB_S1_P11:8
ACLK_SCRN_200MHZ_DATAB_S1_P12:8
ACLK_SCRN_200MHZ_DATAB_S1_P13:8
ACLK_SCRN_200MHZ_DATAB_S1_P14:8
ACLK_SCRN_200MHZ_DATAB_S1_P15:8
person Aubin    schedule 28.02.2013
comment
Очень красиво, но что такое STR_LENGTH? - person Mark Kennedy; 01.03.2013
comment
ACLK_SCRN_200MHZ_DATAB_S12 появится перед ACLK_SCRN_200MHZ_DATAB_S2, если вы не проверите также номер S\\d+. - person Alex; 01.03.2013
comment
Хорошо, ответ изменен, как вы предлагаете. - person Aubin; 01.03.2013
comment
Это очень хорошо. ОДНА последняя проблема. Что делать, если есть строка, которая не содержит _ или:, но я все равно хочу, чтобы она была правильно отсортирована? Кажется, что substring (0, p1 [1] .indexOf (':')) нарушит код, поскольку indexOf вернет -1. Что же тогда переназначить i1 и i2? - person Mark Kennedy; 01.03.2013
comment
Добавлена ​​обработка ошибок. Пожалуйста, опубликуйте исчерпывающую спецификацию входных строк. - person Aubin; 01.03.2013
comment
Он должен сортировать все строковые регистры (НЕ только те, которые содержат: или _). Кроме того, нецелесообразно просто выводить ошибки, а фактически сортировать все строки. Я опубликовал несколько примеров кейсов, которые я отсортировал вручную, но хотел бы, чтобы это сделала программа. БЛАГОДАРНОСТЬ!!! - person Mark Kennedy; 01.03.2013
comment
Я сделал что-то вроде if (p1 [1] .indexOf (':') == -1 || p2 [1] .indexOf (':') == -1) return 1; По крайней мере, тогда это не ошибка, но и сортировка не выполняется правильно! - person Mark Kennedy; 01.03.2013
comment
Кроме того, я не предполагаю, что две строки, скажем, ACLK_SCRN_200MHZ_DATAB_S0_P8: 8 и ACLK_SCRN_200MHZ_DATAB_S1_P0: 8, уже отсортированы ... то есть S0 ‹S1. Этот код выполняет сортировку ТОЛЬКО по числам между двоеточиями. Я ищу функцию, которая сортирует часть двоеточия, а также часть строки - person Mark Kennedy; 01.03.2013
comment
Неважно, здесь я нашел решение - stackoverflow.com/questions/104599/ - person Mark Kennedy; 01.03.2013