如何对对象集合进行排序
How to rank collection of objects
我有class,例如
class Person{
Integer rank;
Double profit;
Person(Integer rank, Double profit){
this.rank = rank;
this.profit = profit;
}
Person(Double profit){
this(0, profit);
}
}
我要对按排名评价的盈利者排序的列表进行排名。
这样
rank(Arrays.asList(
new Person(30),
new Person(20),
new Person(20),
new Person(10))
)
将生成列表
new Person(1, 30),
new Person(2, 20),
new Person(2, 20),
new Person(3, 10)
我还想使用 Java 8 中的自定义 Collector
(或类似的东西)来完成它,而不是使用简单的循环。
你可以这样做:
- 首先对列表进行自定义排序
Comparator
;
- 创建新的利润列表(
distinct()
方法);
- 使用
forEach()
设置适当的等级。
希望对您有所帮助。
List<Person> l = Arrays.asList(new Person(30.0), new Person(20.0), new Person(20.0), new Person(10.0));
Collections.sort(l,(Person o1, Person o2)->o1.profit.compareTo(o2.profit));
List<Double> p = l.stream().map(a -> a.profit).distinct().collect(Collectors.toList());
l.forEach(a -> a.setRank(p.indexOf(a.profit) + 1));
让我们使用一个自定义收集器,我们将所有来自外部的东西传递给它。收集器将从调用者那里获得排名和构造函数。我们想这样称呼它:
public List<Person> rank(List<Person> people) {
return people
.stream()
.sorted(Comparator.<Person>comparingDouble(x -> x.profit).reversed())
.collect(new IntegerRankingCollector<>(
Comparator.comparingDouble(p -> p.profit), // how to differentiate rankings
p -> p.rank, // where to get rank for an element which was already ranked
(p, rank) -> new Person(rank, p.profit) // how to create an element from another element values and a rank
));
}
此收集器可以像 Collectors.toList()
一样实现,但使用累加器方法:
- 获取前一个元素的排名
- 如果当前元素等级应该不同于
前一个元素排名
- 创建具有新等级的元素
这是它的外观,它应该适用于有序流:
public class IntegerRankingCollector<T> implements Collector<T, List<T>, List<T>> {
...
public IntegerRankingCollector(Comparator<? super T> comparator, Function<T, Integer> ranker, BiFunction<T, Integer, T> creator) {
this.comparator = comparator;
this.ranker = ranker;
this.creator = creator;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return (list, current) -> {
ArrayList<T> right = new ArrayList<>();
right.add(creator.apply(current, 1));
combiner().apply(list, right);
};
}
@Override
public BinaryOperator<List<T>> combiner() {
return (left, right) -> {
int rankAdjustment = getRankAdjustment(left, right);
for (T t : right)
left.add(creator.apply(t, rankAdjustment + ranker.apply(t)));
return left;
};
}
private int getRankAdjustment(List<T> left, List<T> right) {
Optional<T> lastElementOnTheLeft = optGet(left, left.size() - 1);
Optional<T> firstElementOnTheRight = optGet(right, 0);
if (!lastElementOnTheLeft.isPresent() || !firstElementOnTheRight.isPresent())
return 0;
else if (comparator.compare(firstElementOnTheRight.get(), lastElementOnTheLeft.get()) == 0)
return ranker.apply(lastElementOnTheLeft.get()) - 1;
else
return ranker.apply(lastElementOnTheLeft.get());
}
private Optional<T> optGet(List<T> list, int index) {
if (list == null || list.isEmpty())
return Optional.empty();
else
return Optional.of(list.get(index));
}
...
}
为了完整起见,这是 class 的完整代码。我从 Collectors.toList
:
复制了其余部分
public class IntegerRankingCollector<T> implements Collector<T, List<T>, List<T>> {
private static final Set<Characteristics> CHARACTERISTICSS = Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
private Comparator<? super T> comparator;
private BiFunction<T, Integer, T> creator;
private Function<T, Integer> ranker;
public IntegerRankingCollector(Comparator<? super T> comparator, Function<T, Integer> ranker, BiFunction<T, Integer, T> creator) {
this.comparator = comparator;
this.ranker = ranker;
this.creator = creator;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return (list, current) -> {
ArrayList<T> right = new ArrayList<>();
right.add(creator.apply(current, 1));
combiner().apply(list, right);
};
}
@Override
public BinaryOperator<List<T>> combiner() {
return (left, right) -> {
int rankAdjustment = getRankAdjustment(left, right);
for (T t : right)
left.add(creator.apply(t, rankAdjustment + ranker.apply(t)));
return left;
};
}
private int getRankAdjustment(List<T> left, List<T> right) {
Optional<T> lastElementOnTheLeft = optGet(left, left.size() - 1);
Optional<T> firstElementOnTheRight = optGet(right, 0);
if (!lastElementOnTheLeft.isPresent() || !firstElementOnTheRight.isPresent())
return 0;
else if (comparator.compare(firstElementOnTheRight.get(), lastElementOnTheLeft.get()) == 0)
return ranker.apply(lastElementOnTheLeft.get()) - 1;
else
return ranker.apply(lastElementOnTheLeft.get());
}
private Optional<T> optGet(List<T> list, int index) {
if (list == null || list.isEmpty())
return Optional.empty();
else
return Optional.of(list.get(index));
}
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
@Override
public Function<List<T>, List<T>> finisher() {
return l -> l;
}
@Override
public Set<Characteristics> characteristics() {
return CHARACTERISTICSS;
}
}
我有class,例如
class Person{
Integer rank;
Double profit;
Person(Integer rank, Double profit){
this.rank = rank;
this.profit = profit;
}
Person(Double profit){
this(0, profit);
}
}
我要对按排名评价的盈利者排序的列表进行排名。 这样
rank(Arrays.asList(
new Person(30),
new Person(20),
new Person(20),
new Person(10))
)
将生成列表
new Person(1, 30),
new Person(2, 20),
new Person(2, 20),
new Person(3, 10)
我还想使用 Java 8 中的自定义 Collector
(或类似的东西)来完成它,而不是使用简单的循环。
你可以这样做:
- 首先对列表进行自定义排序
Comparator
; - 创建新的利润列表(
distinct()
方法); - 使用
forEach()
设置适当的等级。
希望对您有所帮助。
List<Person> l = Arrays.asList(new Person(30.0), new Person(20.0), new Person(20.0), new Person(10.0));
Collections.sort(l,(Person o1, Person o2)->o1.profit.compareTo(o2.profit));
List<Double> p = l.stream().map(a -> a.profit).distinct().collect(Collectors.toList());
l.forEach(a -> a.setRank(p.indexOf(a.profit) + 1));
让我们使用一个自定义收集器,我们将所有来自外部的东西传递给它。收集器将从调用者那里获得排名和构造函数。我们想这样称呼它:
public List<Person> rank(List<Person> people) {
return people
.stream()
.sorted(Comparator.<Person>comparingDouble(x -> x.profit).reversed())
.collect(new IntegerRankingCollector<>(
Comparator.comparingDouble(p -> p.profit), // how to differentiate rankings
p -> p.rank, // where to get rank for an element which was already ranked
(p, rank) -> new Person(rank, p.profit) // how to create an element from another element values and a rank
));
}
此收集器可以像 Collectors.toList()
一样实现,但使用累加器方法:
- 获取前一个元素的排名
- 如果当前元素等级应该不同于 前一个元素排名
- 创建具有新等级的元素
这是它的外观,它应该适用于有序流:
public class IntegerRankingCollector<T> implements Collector<T, List<T>, List<T>> {
...
public IntegerRankingCollector(Comparator<? super T> comparator, Function<T, Integer> ranker, BiFunction<T, Integer, T> creator) {
this.comparator = comparator;
this.ranker = ranker;
this.creator = creator;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return (list, current) -> {
ArrayList<T> right = new ArrayList<>();
right.add(creator.apply(current, 1));
combiner().apply(list, right);
};
}
@Override
public BinaryOperator<List<T>> combiner() {
return (left, right) -> {
int rankAdjustment = getRankAdjustment(left, right);
for (T t : right)
left.add(creator.apply(t, rankAdjustment + ranker.apply(t)));
return left;
};
}
private int getRankAdjustment(List<T> left, List<T> right) {
Optional<T> lastElementOnTheLeft = optGet(left, left.size() - 1);
Optional<T> firstElementOnTheRight = optGet(right, 0);
if (!lastElementOnTheLeft.isPresent() || !firstElementOnTheRight.isPresent())
return 0;
else if (comparator.compare(firstElementOnTheRight.get(), lastElementOnTheLeft.get()) == 0)
return ranker.apply(lastElementOnTheLeft.get()) - 1;
else
return ranker.apply(lastElementOnTheLeft.get());
}
private Optional<T> optGet(List<T> list, int index) {
if (list == null || list.isEmpty())
return Optional.empty();
else
return Optional.of(list.get(index));
}
...
}
为了完整起见,这是 class 的完整代码。我从 Collectors.toList
:
public class IntegerRankingCollector<T> implements Collector<T, List<T>, List<T>> {
private static final Set<Characteristics> CHARACTERISTICSS = Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
private Comparator<? super T> comparator;
private BiFunction<T, Integer, T> creator;
private Function<T, Integer> ranker;
public IntegerRankingCollector(Comparator<? super T> comparator, Function<T, Integer> ranker, BiFunction<T, Integer, T> creator) {
this.comparator = comparator;
this.ranker = ranker;
this.creator = creator;
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return (list, current) -> {
ArrayList<T> right = new ArrayList<>();
right.add(creator.apply(current, 1));
combiner().apply(list, right);
};
}
@Override
public BinaryOperator<List<T>> combiner() {
return (left, right) -> {
int rankAdjustment = getRankAdjustment(left, right);
for (T t : right)
left.add(creator.apply(t, rankAdjustment + ranker.apply(t)));
return left;
};
}
private int getRankAdjustment(List<T> left, List<T> right) {
Optional<T> lastElementOnTheLeft = optGet(left, left.size() - 1);
Optional<T> firstElementOnTheRight = optGet(right, 0);
if (!lastElementOnTheLeft.isPresent() || !firstElementOnTheRight.isPresent())
return 0;
else if (comparator.compare(firstElementOnTheRight.get(), lastElementOnTheLeft.get()) == 0)
return ranker.apply(lastElementOnTheLeft.get()) - 1;
else
return ranker.apply(lastElementOnTheLeft.get());
}
private Optional<T> optGet(List<T> list, int index) {
if (list == null || list.isEmpty())
return Optional.empty();
else
return Optional.of(list.get(index));
}
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
@Override
public Function<List<T>, List<T>> finisher() {
return l -> l;
}
@Override
public Set<Characteristics> characteristics() {
return CHARACTERISTICSS;
}
}