Как отсортировать коллекцию строк, которая содержит числа? - PullRequest
3 голосов
/ 19 октября 2011

У меня есть строковый вектор, который содержит такие данные:

5: 34, 5:38, 17:21, 22:11, ...

Если я попытаюсь объединить это, используя Collections.sort (...); , оно будет выглядеть так:

17: 21, 22:11, 5:34,5: 38

На самом деле я хочу, чтобы это выглядело так:

5: 34, 5:38, 17:21, 22: 11

Итак, я хочу отсортировать элементы по числу перед двоеточием ":", затем, если некоторые элементы имеют одинаковое число до ":", то отсортировать их по номеру после ":".

Какой самый простой способ сделать это?

Ответы [ 9 ]

7 голосов
/ 19 октября 2011

Правильный способ сделать это - не хранить нестроковые значения в виде строк.

Данные в вашей коллекции имеют некоторую структуру и правила и не могут быть произвольной строкой.,Поэтому не следует использовать тип данных String.

Давайте определим тип с именем TwoNumbers (потому что я не знаю, что должен представлять тип, даже если бы мог догадаться):

class TwoNumbers implements Comparable<TwoNumbers> {
    private final int num1;
    private final int num2;

    public TwoNumbers(int num1, int num2) {
        if (num1 <= 0 || num2 <= 0) {
            throw new IllegalArgumentException("Numbers must be positive!");
        }
        this.num1 = num1;
        this.num2 = num2;
    }

    public static TwoNumbers parse(String s) {
        String[] parts = s.split(":");
        if (parts.length != 2) {
            throw new IllegalArgumentException("String format must be '<num>:<num>'");
        }
        try {
            return new TwoNumbers(Integer.parseInt(parts[0]), Integer.parseInt(parts[0]));
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("parts must be numeric!", e);
        }
    }

    public int getNum1() {
        return num1;
    }

    public int getNum2() {
        return num2;
    }

    @Override
    public int compareTo(TwoNumbers o) {
        if (o == null) {
            return 1;
        }
        int diff = Integer.compare(o.num1, this.num1);
        if (diff == 0) {
            diff = Integer.compare(o.num2, this.num2);
        }
        return diff;
    }
}

Метод compareTo существует как реализация интерфейса Comparable : он определяет порядок упорядочения объектов этого типа.

Я использовал final поля (и не предоставляют установщиков), потому что класс реализует неизменяемые объекты .

Таким образом, вы можете напрямую сортировать данные без дополнительных Comparatorи не нужно распространять весь этот код "разбить и анализировать" по всей вашей программе.Вместо этого у вас есть single класс, который отвечает за обработку этого конкретного формата, и все остальные части кода могут просто использовать это.

4 голосов
/ 19 октября 2011

Это ужасно неэффективно, но оно должно делать свою работу.

Collections.sort(data, new Comparator<String>(){
    public int compare(String a, String b){
        String[] as = a.split(":");
        String[] bs = b.split(":");
        int result = Integer.valueOf(as[0]).compareTo(Integer.valueOf(bs[0]));
        if(result==0)
            result = Integer.valueOf(as[1]).compareTo(Integer.valueOf(bs[1]));
        return result;
    }
})

(Подсказка: если бы это был мой код, я бы оптимизировал его для использования подстрок вместо String.split (), ноМне лень)

2 голосов
/ 19 октября 2011

Вы можете создать пользовательский Comparator, чтобы разделить String и разобрать его на два целых числа, или , создать класс на заказ для представления каждого String и сохранить его в Collectionвместо.Я предпочитаю последний подход, так как вам придется только один раз разделить / проанализировать строку;например,

public class Data implements Comparable<Data> {
  private final int prefix;
  private final int suffix;

  public Data(String str) {
    String[] arr = str.split(":");

    if (arr.length != 2) {
      throw new IllegalArgumentException();
    }

    this.prefix = Integer.parseInt(arr[0]);
    this.suffix = Integer.parseInt(arr[1]);
  }

  public int compareTo(Data data) {
    // Should really avoid subtraction in case of overflow but done to keep code brief.
    int ret = this.prefix - data.prefix;

    if (ret == 0) {
      ret = this.suffix - data.suffix;
    }

    return ret;
  }

  // TODO: Implement equals and hashCode (equals to be consistent with compareTo).

  public String toString() { return String.format("%d:%d", prefix, suffix); }
}

Тогда это просто случай сохранения некоторых Data объектов в вашем Collection;например,

List<Data> l = new ArrayList<Data>();
l.add(new Data("13:56"));
l.add(new Data("100:16"));
l.add(new Data("9:1"));
Collections.sort(l);

Еще одна вещь - вы упоминаете, что используете Vector.Вам следует избегать использования Vector / Hashtable, поскольку они были заменены на List / Map, которые были представлены как часть Collections Framework в JDK 1.2.

0 голосов
/ 24 ноября 2013

Только что нашел этот (довольно старый) пост, и ответы не совсем решили мою проблему. Мне нужно было более общее решение, так как значения были введены пользователем, и что-то вроде «abc 1 a 12» и «abc 1 a 1» должно быть отсортировано по порядку числа (ов). Поэтому я написал следующий компаратор:

new Comparator<String>() {

        @Override
        public int compare(String o1, String o2) {
            String[] s1=splitNumeric(o1);
            String[] s2=splitNumeric(o2);
            for (int x=0;x<s1.length&&x<s2.length;x++){
                if (!s1[x].equals(s2[x])){
                    if (s1[x].charAt(0)=='N' && s2[x].charAt(0)=='N'){
                        long l1=Long.parseLong(s1[x].substring(1));
                        long l2=Long.parseLong(s2[x].substring(1));
                        return (int)Math.signum(l1-l2);
                    }
                    break;
                }
            }
            return o1.compareTo(o2);
        }
    }

Хотя функция splitNumeric определяется следующим образом:

   private String[] splitNumeric(String s){
        final String numbers="0123456789";
        LinkedList<String> out=new LinkedList<String>();
        int state=-1;
        for (int x=0;x<s.length();x++){
            if (numbers.contains(s.charAt(x)+"")){
                if (state==1)
                    out.set(out.size()-1,out.getLast()+s.charAt(x));
                else{
                    state=1;
                    out.add("N"+s.charAt(x));
                }
            }
            else{
                if (state==0)
                    out.set(out.size()-1,out.getLast()+s.charAt(x));
                else{
                    state=0;
                    out.add("S"+s.charAt(x)+"");
                }
            }
        }
        return out.toArray(new String[0]);
    }

Код отсортирует строки

"X 124 B"
"X 1 Y"
"X 111 Z" 
"X 12 Y"
"12:15"
"12:13"
"12:1"
"1:1"
"2:2"

следующим образом:

"1:1"
"2:2"
"12:1"
"12:13"
"12:15"
"X 1 Y"
"X 12 Y"
"X 111 Z" 
"X 124 B"

Наслаждайтесь:)

0 голосов
/ 19 октября 2011

Я думаю, что это довольно просто:

public class NumericalStringSort {

    public static void main(String[] args) {
        List<String> input = Arrays.asList(new String[] {"17:21", "22:11", "5:34", "5:38"});
        Collections.sort(input, new NumericalStringComparator());
        System.out.println(input);
    }

    public static class NumericalStringComparator implements Comparator<String> {
        public int compare(String object1, String object2) {
            return pad(object1).compareTo(pad(object2));
        }

        private String pad(String input) {
            return input.indexOf(":") == 1 ? "0" + input : input;
        }
    }
}
0 голосов
/ 19 октября 2011

Как правило, объекты в Java (включая Коллекции) сравниваются с их методами по умолчанию hashCode () и equals (). Для встроенных объектов и типов данных (таких как String, Integet и т. Д.) HashCode () вычисляется внутренне, и, следовательно, они используются в соответствии с требованиями JLS (Спецификация языка Java).

Поскольку мы не всегда можем зависеть от стандартных / встроенных объектов и нам нужно иметь дело с нашими собственными объектами (такими как Employee, Customer и т. Д.), Нам необходимо переопределить hashCode () и equals () метод, чтобы мы могли предоставить истину / ложь в соответствии с «ЛУЧШИМ» равенством объектов наших пользовательских классов.

Similary, sort () включает в себя действие сравнения, которому действительно нужен Comparator (который является классом, реализующим интерфейс Comparator с переопределенным методом сравнения). Вам также следует переопределить метод сравнения, который берет два сравниваемых объекта и возвращает результат (0 для равных, 1 для 1-го объекта больше второго, 2 для обратного варианта 1).

Теперь ваши данные должны обрабатываться по-другому, что значительно отличается от обычного сравнения. Вам нужно разделить данные на две части (используя метод разделения, который вы можете сделать), а затем вы можете выполнить отдельное сравнение по двум параметрам (первая часть перед двоеточием, вторая часть после двоеточия).

Наконец, вы должны предоставить экземпляр этого пользовательского компаратора для метода sort, который в конечном итоге будет выполнять пользовательскую сортировку для ваших пользовательских данных:)

0 голосов
/ 19 октября 2011

Реализуйте свой собственный Comparator и передайте его в качестве второго аргумента методу Colelctions.sort.

0 голосов
/ 19 октября 2011

Реализуйте свой собственный класс Comparator, который сравнивает два значения и вызывает Collections.sort(List list, Comparator c).

0 голосов
/ 19 октября 2011

Создайте java.util.Comparator и предоставьте его методу sort.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...