在Java中与任何比较器一起使用PriorityQueue

在Java中与任何比较器一起使用PriorityQueue,java,Java,常见问题:如何使用自定义类的不同比较器对PriorityQueue中的对象进行排序 我尝试在适当的优先级队列对中使用此比较器,并在下一个代码中使用具有类似排序结果的对象列表: class User{ private Integer id; private String name; public User(Integer i, String n){ this.id=i; this.name=n; } public Integer getId() {return

常见问题:如何使用自定义类的不同比较器对PriorityQueue中的对象进行排序

我尝试在适当的优先级队列对中使用此比较器,并在下一个代码中使用具有类似排序结果的对象列表:

class User{
  private Integer id;
  private String name;
  public User(Integer i, String n){
    this.id=i;
    this.name=n;
  }
  public Integer getId() {return id;}
  public String getName() {return name;}
  @Override
  public boolean equals(Object obj) {
    if (this == obj)return true;
    if (obj == null)return false;
    if (getClass() != obj.getClass())return false;
    User other = (User) obj;
    if(id == null){
      if (other.id != null)return false;
    }else if(!id.equals(other.id))return false;
    return true;
  }
  @Override
  public String toString() {return "[id:" + id + ", name:" + name + "]";}
}

public class MyPriorityQueue {

  public static Comparator<User> cmpId = Comparator.comparingInt(x -> x.getId());
  public static Comparator<User> cmpNameLength = Comparator.comparingInt(x -> x.getName().length());

  public static void main(String[] args) {
    List<User> users = new ArrayList<User>(10);
    users.add(new User(1,"11111"));
    users.add(new User(3,"333"));
    users.add(new User(5,"5"));
    users.add(new User(4,"44"));
    users.add(new User(2,"2222"));

    Queue<User> ids = new PriorityQueue<User>(10, cmpId); //use first comparator
    users.forEach(x-> ids.offer(x));

    Queue<User> names = new PriorityQueue<User>(10, cmpNameLength); //use second comparator
    names.addAll(users);

    System.out.println("Variant_1.1:"); 
    ids.forEach(System.out::println);

    System.out.println("Variant_2.1:"); 
    names.forEach(System.out::println);

    System.out.println("Variant_1.2:"); 
    users.sort(cmpId);  //use first comparator
    users.forEach(System.out::println);

    System.out.println("Variant_2.2:"); 
    users.sort(cmpNameLength);  //use second comparator
    users.forEach(System.out::println);
  }
}
我希望:

  • 变量1.1和2.1的结果
  • 变量1.2和2.2的结果
将是相同的,但他们是不同的


我的问题:我在排序priorityqueue/comparator时犯了什么错误?在我的示例中,如何获得priorityqueue的排序结果以及相应的列表?

在您的代码示例中,您使用它来迭代队列

ids.forEach(System.out::println);

names.forEach(System.out::println);
forEach
最终委托到。但是,需要注意的是,中的子类override有不同的javadoc,其中有一个关于排序的特殊注释

返回此队列中元素的迭代器。迭代器不会以任何特定顺序返回元素

换句话说,不能保证在
PriorityQueue
上迭代将使用
比较器。相反,如果您通过反复调用来更改代码以耗尽队列,那么我希望您会看到根据自定义
比较器排序的结果

深入研究OpenJDK源代码,我们可以看到其中的内部数据结构是一个。这是由一个数组支持的,当调用方添加和删除队列元素时,代码在内部保持堆不变

/**
 * Priority queue represented as a balanced binary heap: the two
 * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
 * priority queue is ordered by comparator, or by the elements'
 * natural ordering, if comparator is null: For each node n in the
 * heap and each descendant d of n, n <= d.  The element with the
 * lowest value is in queue[0], assuming the queue is nonempty.
 */
transient Object[] queue; // non-private to simplify nested class access

您没有做错什么,只是
PriorityQueue
的迭代器是:

不保证以任何特定顺序遍历优先级队列的元素()

forEach的
forEach
方法在内部使用迭代器,因此也存在同样的问题

这是因为底层的数据结构是这样的,它可以在您定义项时“排序”。如果实现者希望按排序的顺序返回项目,他们必须首先收集项目,然后在返回之前对其进行排序。这会导致性能下降,因此(我猜想)决定无序返回,因为
PriorityQueue
主要是一个队列,而不是一个排序的集合,用户可以自己对项目进行排序(这是最有效的)

要获得有序的元素,请执行以下操作:

  while(pq.peek() != null){
      System.out.println(pq.poll());
  }

感谢您澄清“forEach”。在更正代码“while(!ids.isEmpty())System.out.println(ids.poll());”而不是“ids.forEach(System.out::println);”之后,获得了预期的结果。
            return (E) queue[lastRet = cursor++];
  while(pq.peek() != null){
      System.out.println(pq.poll());
  }