2017-01-11 17 views
2

У меня есть класс, напримерКак ранжировать коллекцию объектов

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) 

Кроме того, я хочу сделать это с помощью пользовательских Collector (или нечто подобное) с Java 8, а не использовать простые петли.

+0

Вы можете просто сделать это, используя 'Comparator'. Подробнее о 'Comparator & Comparable' в Java: https://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html – user2004685

+0

@ user2004685 с помощью' Comparator' Я могу, например, отсортировать свою коллекцию но как я могу установить ранги на свои элементы? –

+0

Ваш пример для меня не имеет смысла. Ваш единственный конструктор параметров устанавливает ранг в '0', так как объекты' Person' волшебным образом получают ранг, когда вы их сортируете? – azurefrog

ответ

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(), но с помощью метода аккумулятора, который:

  1. получает звание предыдущего элемента
  2. увеличивает его, если ток Ранг элемента должен быть отличным от предшествующий элемент ранг
  3. создает элемент в новом ранге

Вот как это выглядит, и он должен работать для упорядоченных потоков:

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)); 
    } 

     ... 
    } 

Для полноты, это полный код класса. Я скопировал остальное с 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; 
    } 
} 
2

Вы можете сделать это следующим образом:

  1. во-первых, отсортировать список персонализированных Comparator;
  2. сделать новый список с прибылью без дубликатов (distinct() метод);
  3. установить соответствующие разряды, используя 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)); 
+0

Ваш алгоритм очень неэффективен. Последняя строка имеет квадратичную сложность, подумайте об этом. И если вы держите в объектах списка с тем же результатом .equals(), это даст вам даже неправильный рейтинг. – JiangHongTiao

 Смежные вопросы

  • Нет связанных вопросов^_^