Помогите, пожалуйста, с сортировкой в Java. У меня есть простой пример (выглядит так). Мне нужно отсортировать список по разнице между двумя BigDecimals.
Мой класс данных (я вырезал геттеры)
public class Quotation {
private final long id;
private final BigDecimal askPrice;
private final BigDecimal bidPrice;
public Quotation(long id, BigDecimal bidPrice, BigDecimal askPrice) {
this.id = id;
this.askPrice = askPrice;
this.bidPrice = bidPrice;
}
}
Здесь мы храним наши записи.
storeQuotation(new Quotation(1000,new BigDecimal("99"), new BigDecimal("104")));
storeQuotation(new Quotation(1001,new BigDecimal("69"), new BigDecimal("72")));
storeQuotation(new Quotation(1002,new BigDecimal("65"), new BigDecimal("69")));
storeQuotation(new Quotation(1003,new BigDecimal("70"), new BigDecimal("71")));
storeQuotation(new Quotation(1004,new BigDecimal("71"), new BigDecimal("73")));
storeQuotation(new Quotation(1005,new BigDecimal("90"), new BigDecimal("95")));
storeQuotation(new Quotation(1006,new BigDecimal("92"), new BigDecimal("93")));
storeQuotation(new Quotation(1007,new BigDecimal("94"), new BigDecimal("98")));
storeQuotation(new Quotation(1008,new BigDecimal("90"), new BigDecimal("92")));
storeQuotation(new Quotation(1009,new BigDecimal("92"), new BigDecimal("95")));
out - Not Sorted
id - 1000. Bid - 99. Ask - 104. Spread = 5
id - 1001. Bid - 69. Ask - 72. Spread = 3
id - 1002. Bid - 65. Ask - 69. Spread = 4
id - 1003. Bid - 70. Ask - 71. Spread = 1
id - 1004. Bid - 71. Ask - 73. Spread = 2
id - 1005. Bid - 90. Ask - 95. Spread = 5
id - 1006. Bid - 92. Ask - 93. Spread = 1
id - 1007. Bid - 94. Ask - 98. Spread = 4
id - 1008. Bid - 90. Ask - 92. Spread = 2
id - 1009. Bid - 92. Ask - 95. Spread = 3
И мне просто нужно отсортировать этот список по разнице между bidPrice и askPrice. Я попробовал этот метод...
public static List<Quotation> getSpreadsList(boolean decreasing) {
List<Quotation> sortedBySpread = QuotationsStoreImpl.quotationList;
Collections.sort(sortedBySpread, (a, b) ->
(a.getBidPrice().intValue() - b.getAskPrice().intValue()));
// sortedBySpread.sort((a, b) ->
// (a.getBidPrice().intValue() - b.getAskPrice().intValue()));
if (decreasing) {
Collections.reverse(sortedBySpread);
}
return sortedBySpread;
}
}
Но безуспешно...
out - Sorted
id - 1002. Bid - 65. Ask - 69. Spread = 4
id - 1003. Bid - 70. Ask - 71. Spread = 1
id - 1004. Bid - 71. Ask - 73. Spread = 2
id - 1001. Bid - 69. Ask - 72. Spread = 3
id - 1008. Bid - 90. Ask - 92. Spread = 2
id - 1009. Bid - 92. Ask - 95. Spread = 3
id - 1006. Bid - 92. Ask - 93. Spread = 1
id - 1007. Bid - 94. Ask - 98. Spread = 4
id - 1005. Bid - 90. Ask - 95. Spread = 5
id - 1000. Bid - 99. Ask - 104. Spread = 5
Список смешанный, но не отсортированный по моим критериям! Разворот не отсортирован!
Как правильно отсортировать этот список по разбросу?
У меня нет большого опыта в java. И все мои попытки ни к чему не привели.
Эээ, что происходит, когда дельта между этими двумя полями не является допустимым целым числом? Какой смысл использовать BigDecimal, когда вы полагаетесь на такой подверженный ошибкам способ их вычитания? (Например, у BigDecimal есть метод subtract
... если вас беспокоят накладные расходы, почему бы не вычислить это значение при построении объектов цитаты)?
Кстати, сократите код как запись: public record Quotation ( long id , BigDecimal askPrice , BigDecimal bidPrice ) {}
Collections.sort(sortedBySpread, (a, b) -> (a.getBidPrice().intValue() - b.getAskPrice().intValue()));
выполняет некоторую математику, которая мало что дает, поскольку она вычитает аск из ставки двух разных котировок.
Вместо этого вы должны рассчитать распространение a
, а затем вычесть распространение b
:
Collections.sort(sortedBySpread, (a, b) -> (a.getBidPrice().intValue() - a.getAskPrice().intValue()) - (b.getBidPrice().intValue() - b.getAskPrice().intValue()));
Как правило, это можно расширить до следующего, чтобы было более понятно, что происходит:
Collections.sort(sortedBySpread, (Quotation a, Quotation b) -> {
int spreadA = a.getBidPrice().intValue() - a.getAskPrice().intValue();
int spreadB = b.getBidPrice().intValue() - b.getAskPrice().intValue();
return spreadA - spreadB;
});
Но, начиная с первого фрагмента, IntelliJ предлагает более чистое решение.
Collections.sort(sortedBySpread, Comparator.comparingInt(a -> (a.getBidPrice().intValue() - a.getAskPrice().intValue())));
И, исходя из этого, может иметь смысл поставить getSpread
на Quotation
:
public int getSpread() {
return bidPrice.intValue() - askPrice.intValue();
}
что тогда позволит
Collections.sort(sortedBySpread, Comparator.comparingInt(Quotation::getSpread));
И наконец
sortedBySpread.sort(Comparator.comparingInt(Quotation::getSpread));
без необходимости Collections.sort
.
Большое спасибо ! Ваш ответ решил проблему! Я только сейчас понял концепцию a и b.
Для демонстрации я помещаю ваши значения в список. Я также добавил следующее в ваш класс.
public BigDecimal getSpread() {
return askPrice.subtract(bidPrice);
}
public String toString() {
return "id - %d bid - %6.2f ask - %6.2f spread - %6.2f".formatted(id, bidPrice, askPrice,getSpread());
}
Данные
List<Quotation> quotes = new ArrayList<>(List.of(
(new Quotation(1000,new BigDecimal("99"), new BigDecimal("104"))),
(new Quotation(1001,new BigDecimal("69"), new BigDecimal("72"))),
(new Quotation(1002,new BigDecimal("65"), new BigDecimal("69"))),
(new Quotation(1003,new BigDecimal("70"), new BigDecimal("71"))),
(new Quotation(1004,new BigDecimal("71"), new BigDecimal("73"))),
(new Quotation(1005,new BigDecimal("90"), new BigDecimal("95"))),
(new Quotation(1006,new BigDecimal("92"), new BigDecimal("93"))),
(new Quotation(1007,new BigDecimal("94"), new BigDecimal("98"))),
(new Quotation(1008,new BigDecimal("90"), new BigDecimal("92"))),
(new Quotation(1009,new BigDecimal("92"), new BigDecimal("95")))));
Сортировочная часть проста. Просто используйте компаратор, ссылаясь на спред.
quotes.sort(Comparator.comparing(Quotation::getSpread));
И распечатать
for(Quotation q : quotes) {
System.out.println(q);
}
Отпечатки
id - 1003 bid - 70.00 ask - 71.00 spread - 1.00
id - 1006 bid - 92.00 ask - 93.00 spread - 1.00
id - 1004 bid - 71.00 ask - 73.00 spread - 2.00
id - 1008 bid - 90.00 ask - 92.00 spread - 2.00
id - 1001 bid - 69.00 ask - 72.00 spread - 3.00
id - 1009 bid - 92.00 ask - 95.00 spread - 3.00
id - 1002 bid - 65.00 ask - 69.00 spread - 4.00
id - 1007 bid - 94.00 ask - 98.00 spread - 4.00
id - 1000 bid - 99.00 ask - 104.00 spread - 5.00
id - 1005 bid - 90.00 ask - 95.00 spread - 5.00
Кстати, если вы хотите изменить порядок сортировки на обратный, вы можете добавить reversed()
в конец comparator
. Например. quotes.sort(Comparator.comparing(Quotation::getSpread).reversed());
Я бы добавил метод
getSpread()
(илиcalcSoread()
) кQuotation
и отсортировал по нему. (вродеsort(sortedBySpread, Comparator.comparingInt(Quotation::getSpread)
)