Java 8中Collections.sort的问题

时间:2014-11-20 22:00:27

标签: java sorting java-8

 @Entity
 @NamedQueries({
   @NamedQuery(
     name = "FolderNode.findByName",
     query = "SELECT f FROM FolderNode f WHERE f.name = :name AND f.parentNode = :parentNode"),
   @NamedQuery(
     name = "FolderNode.findRootNodeByName",
     query = "SELECT f FROM FolderNode f WHERE f.name = :name AND f.parentNode is null")
 })
 public class FolderNode extends InstructorTreeNode {
   public FolderNode() {
     super();
   }

   public FolderNode(String name) {
     this();
     setName(name);
   }

   public FolderNode(int sortOrder, String name) {
     this(name);
     this.sortOrder = sortOrder;
   }

   public FolderNode(int sortOrder, String name, EmployeeState status) {
     this(sortOrder, name);
     this.status = status;
   }

   public static FolderNode addWaitingListNode(String name) {
     EntityManager em = getDao().getEntityManager();
     em.getTransaction().begin();
     FolderNode waitingListNode = getWaitingListFolder();
     FolderNode folderNode = new FolderNode(0, name);
     waitingListNode.addChild(folderNode);
     em.merge(waitingListNode);
     em.getTransaction().commit();
     em.close();
     return folderNode;
   }

   public static void addWaitingListStudent(String waitingList, Student s) {
     EntityManager em = FolderNode.getDao().getEntityManager();
     em.getTransaction().begin();
     FolderNode waitingListsNode = getWaitingListFolder();
     FolderNode waitingListNode = getDao().findFolderNodeByName(waitingListsNode, waitingList);
     waitingListNode.addChild(new EmployeeLeaf(s.getInmate()));
     em.merge(waitingListNode);
     em.getTransaction().commit();
     em.close();
   }

   public static FolderNode getAMClassFolder() {
     return getDao().findFolderNodeByName(getStudentsFolder(), "AM Class");
   }

   public static FolderNode getAttendanceFolder() {
     return getDao().findFolderNodeByName(getRootFolder(), "Employee Attendance");
   }

   public static FolderNode getFormerParaprosFolder() {
     return getDao().findFolderNodeByName(getParaprosFolder(), "Former");
   }

   public static FolderNode getFormerStudentsFolder() {
     return getDao().findFolderNodeByName(getStudentsFolder(), "Former");
   }

   public static FolderNode getPMClassFolder() {
     return getDao().findFolderNodeByName(getStudentsFolder(), "PM Class");
   }

   public static FolderNode getParaprosFolder() {
     return getDao().findFolderNodeByName(getRootFolder(), "Parapros");
   }

   public static FolderNode getPendingStudentsFolder() {
     return getDao().findFolderNodeByName(getRootFolder(), "Pending Students");
   }

   public static FolderNode getRootFolder() {
     return getDao().findFolderNodeByName(null, EducationPreferences.getInstructor().getInstructorName());
   }

   public static FolderNode getStudentsFolder() {
     return getDao().findFolderNodeByName(getRootFolder(), "Students");
   }

   public static FolderNode getWaitingListFolder(String name) {
     FolderNode waitingListsNode = getWaitingListFolder();
     return getDao().findFolderNodeByName(waitingListsNode, name);
   }

   public static FolderNode getWaitingListFolder() {
     return getDao().findFolderNodeByName(getRootFolder(), "Waiting List");
   }

   public static void setClassFolder(Student aStudent, EntityManager entityManager) {
     EntityManager em = entityManager;
     if (entityManager == null) {
       em = FolderNode.getDao().getEntityManager();
       em.getTransaction().begin();
     }

     EmployeeLeaf leaf = EmployeeLeaf.findActiveStudentLeaf(aStudent);
     FolderNode node = aStudent.getShift() == Shift.AM ? getAMClassFolder() : getPMClassFolder();
     leaf.setParentNode(node);
     em.merge(leaf);
     GlobalEntityMethods.updateHistory(leaf);
     if (entityManager == null) {
       em.getTransaction().commit();
       em.close();
     }
   }

   public static void transferWaitingListStudent(String currentFolder, String toFolder, Student student) {
     EntityManager em = FolderNode.getDao().getEntityManager();
     em.getTransaction().begin();
     FolderNode waitingListsNode = getWaitingListFolder();
     FolderNode currentWaitingListNode = getDao().findFolderNodeByName(waitingListsNode, currentFolder);
     EmployeeLeaf employeeLeaf = EmployeeLeaf.getDao().findWaitingListLeafByInmate(student.getInmate());
     currentWaitingListNode.removeChild(employeeLeaf);
     FolderNode toWaitingListNode = getDao().findFolderNodeByName(waitingListsNode, toFolder);
     toWaitingListNode.addChild(employeeLeaf);
     em.merge(currentWaitingListNode);
     em.merge(toWaitingListNode);
     em.getTransaction().commit();
     em.close();
   }

   public void addChild(InstructorTreeNode node) {
     childNodes.add(node);
     node.setParentNode(this);
   }

   public List<InstructorTreeNode> getChildNodes() {
     Collections.sort(childNodes);
     return childNodes;
   }

   @Override
   public Set<Inmate> getInmates() {
     Set<Inmate> inmateSet = new HashSet<> (50);
     for (InstructorTreeNode node: getChildNodes()) {
       inmateSet.addAll(node.getInmates());
     }
     return inmateSet;
   }

   public int getSortOrder() {
     return sortOrder;
   }

   public EmployeeState getStatus() {
     return status;
   }

   @Override
   public List<InstructorTreeNode> getTree() {
     List <InstructorTreeNode> result = new ArrayList<> (25);
     for (InstructorTreeNode childNode: getChildNodes()) {
       if (childNode instanceof FolderNode) {
         result.add(childNode);
       }
       result.addAll(childNode.getTree());
     }
     return result;
   }

   @Override
   public JPanel getView(EmployeeViewController controller) {
     if ("Employee Attendance".equals(getName())) {
       return new AttendanceView();
     } else if ("Waiting List".equals(getName())) {
       return new AllWaitingListsPanel(controller);
     } else if (getParentNode().getName().equals("Waiting List")) {
       return new WaitingListPanel(controller);
     } else if ("Pending Students".equals(getName())) {
       return new PendingStudentsPanel(controller);
     } else if ("Students".equals(getName())) {
       return new AllStudentsPanel(controller);
     } else if ("AM Class".equals(getName())) {
       return new AllStudentsPanel(controller, Shift.AM);
     } else if ("PM Class".equals(getName())) {
       return new AllStudentsPanel(controller, Shift.PM);
     } else if (getParentNode().getName().equals("Students") && "Former".equals(getName())) {
       return new FormerStudentsPanel(controller);
     } else if ("Parapros".equals(getName())) {
       return new AllParaprosPanel(controller);
     } else if (getParentNode().getName().equals("Parapros") && "Former".equals(getName())) {
       return new FormerParaprosPanel(controller);
     }
     throw new UnsupportedOperationException("unknown folder");
   }

   public void removeChild(InstructorTreeNode node) {
     childNodes.remove(node);
     node.setParentNode(null);
   }

   public void removeEmployeeLeaf(Inmate inmate) {
     for (InstructorTreeNode node: childNodes) {
       if (node instanceof EmployeeLeaf) {
         EmployeeLeaf employeeLeaf = (EmployeeLeaf) node;
         if (employeeLeaf.getInmate().equals(inmate)) {
           childNodes.remove(employeeLeaf);
           break;
         }
       }
     }
   }

   public void setChildNodes(List<InstructorTreeNode> childNodes) {
     this.childNodes = childNodes;
   }

   public void setSortOrder(int sortOrder) {
     this.sortOrder = sortOrder;
   }

   public void setStatus(EmployeeState status) {
     this.status = status;
   }

   @OneToMany(mappedBy = "parentNode", cascade = CascadeType.ALL, orphanRemoval = true)
   private List<InstructorTreeNode> childNodes;

   private int sortOrder;

   @Enumerated(EnumType.STRING)
   private EmployeeState status;
 }


 @Entity
 @Table(catalog = "education", name = "instructortreenode", uniqueConstraints = @UniqueConstraint(columnNames = {
   "PARENTNODE_ID", "NAME"
 }))
 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 public abstract class InstructorTreeNode implements Comparable<InstructorTreeNode> {
   public InstructorTreeNode() {
     super();
   }

   public static InstructorTreeNodeDAO getDao() {
     return dao;
   }

   @Override
   public int compareTo(InstructorTreeNode o) {
     if (o instanceof FolderNode && this instanceof FolderNode) {
       FolderNode thisFolder = (FolderNode) this;
       FolderNode otherFolder = (FolderNode) o;
       if (thisFolder.getSortOrder() != otherFolder.getSortOrder()) {
         return thisFolder.getSortOrder() - otherFolder.getSortOrder();
       } else {
         return thisFolder.getName().compareToIgnoreCase(otherFolder.getName());
       }
     } else if (o instanceof EmployeeLeaf && this instanceof EmployeeLeaf) {
       return getName().compareToIgnoreCase(((InstructorTreeNode) o).getName());
     }
     return (o instanceof FolderNode) ? -1 : +1;
   }

   public int getCount() {
     return getTree().size();
   }

   public abstract Set<Inmate> getInmates();

   public String getName() {
     return name;
   }

   public FolderNode getParentNode() {
     return parentNode;
   }

   public abstract List<InstructorTreeNode> getTree();

   public abstract JPanel getView(EmployeeViewController theController);

   public void setName(String name) {
     this.name = name;
   }

   public void setParentNode(FolderNode parentNode) {
     this.parentNode = parentNode;
   }

   @Override
   public String toString() {
     return name;
   }

   private static final InstructorTreeNodeDAO dao = new InstructorTreeNodeDAO();
   private String name;

   @ManyToOne
   private FolderNode parentNode;
 }

这是我的问题: Collections.sort行在Java 8u5和之前工作正常,但是 在Java 8u20中,他们似乎更改了Collections.sort的代码 并且它不再使用任何东西,除了自然顺序,即使你指定 比较者。

我是否应该使用其他方法对列表进行排序,或者是否存在错误 Collections.sort。

任何帮助都会非常感激,因为这让我发疯了。

我忘了说这段代码不使用指定的比较器,但是根据文档它应该使用CompareTo,如果你的类实现了Comparable,这就是我正在使用的。 我也试过指定一个比较器,但它也没有用。

4 个答案:

答案 0 :(得分:9)

如果您使用方法Collections#sort(List<T> list),则按照方法List#sort(Comparator comparator)推荐,比较器为null。来自java.util.Collections的源代码如下:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

如果要指定自己的比较器,则需要使用方法Collections#sort(List<T> list, Comparator<T> comparator),它将比较器传递给列表排序方法。来自java.util.Collections的源代码如下:

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

到目前为止一切顺利。现在,正如您正确指出的那样,如果您没有指定比较器,则使用该类的自然排序,即您定义的compareTo方法。

然而,Comparabledocumentation also states the following

  

强烈建议(尽管不要求)自然排序与equals一致。这是因为没有显式比较器的有序集(和有序映射)在与自然顺序与equals不一致的元素(或键)一起使用时表现得“奇怪”。特别是,这样的有序集(或有序映射)违反了集合(或映射)的一般契约,它是根据等于方法定义的。

由于课程InstructorTreeNode未覆盖Object#equals,即使compareTo返回false,您的==方法也可能返回0。我认为这导致文档称之为“奇怪”。

答案 1 :(得分:9)

由于Collections.sort现在委托给List.sort,实际的List实施会产生影响。像ArrayListVector这样的实现有机会以比默认实现更有效的方式实现List.sort,因为它们将内部数组直接传递给Arrays.sort,省略了复制步骤默认实现。

除非程序员使用子类化实现(而不是使用委托)的反模式来覆盖实现矛盾行为的方法,否则这将无缝地工作。来自EclipseLink / JPA are known to have problems with this的Lazily填充列表,因为它们试图拦截每个读取方法以在继续之前填充列表但是错过了新的sort方法。如果在调用sort时尚未填充列表,sort将看到空列表状态。

在你的代码中,没有任何指示列表来自哪里以及它具有哪个实际的实现类,但是因为我看到很多熟悉的注释,我猜,你使用这样的框架......

答案 2 :(得分:0)

你可能不喜欢这个答案,因为它并没有为你的情况提供快速解决方案,但从长远来看它会对你有所帮助。

这是一种你可以通过一些调试来弄清楚自己的bug。我不知道你正在使用什么IDE,但是使用Eclipse,你甚至可以进入JDK中的代码!

所以,我会做的是在你在childNodes上调用sort()的行设置一个断点。然后我会进入JDK代码并自己完成它。它将变得非常清楚发生了什么,以及它为什么不调用你的比较函数。

答案 3 :(得分:-1)

您可以尝试构建自定义比较器。这是一个应该如何看待的例子。这是为了比较BigDecimals。

class YourComparator implements Comparator<InstructorTreeNode> {
@Override
public int compare(final  InstructorTreeNode 01, final InstructorTreeNode o2) {
    return o2.getYourCompVal().compareTo(o1.getYourCompVal());
}

}

 public List<InstructorTreeNode> getChildNodes() {
 Collections.sort(childNodes, new YourComparator());
 return childNodes;}