【问题标题】:Comparator<File> for "directories-first" orderComparator<File> 用于“目录优先”顺序
【发布时间】:2010-12-14 15:29:37
【问题描述】:

我很难过...假设我有这个目录树:

{someRoot}/
{someRoot}/bar/
{someRoot}/bar/file1.txt
{someRoot}/foo/
{someRoot}/foo/baz/
{someRoot}/foo/baz/file3.txt
{someRoot}/foo/abracadabra.txt
{someRoot}/foo/file2.txt
{someRoot}/aardvark.txt
{someRoot}/food.txt
{someRoot}/zebra.txt

您会注意到顺序。将此称为 order1。在每个阶段,目录都排在文件之前。 (注意: bar/file1.txt 位于 foo 之前,因此在全球范围内,目录并非都位于所有文件之前。)

如果我枚举这个目录树,然后递归枚举子目录,我会得到以下List&lt;File&gt;,顺序为order2

{someRoot}/
{someRoot}/aardvark.txt
{someRoot}/bar/
{someRoot}/foo/
{someRoot}/food.txt
{someRoot}/zebra.txt
{someRoot}/bar/file1.txt
{someRoot}/foo/abracadabra.txt
{someRoot}/foo/baz/
{someRoot}/foo/file2.txt
{someRoot}/foo/baz/file3.txt

如果我创建简单的Comparator&lt;File&gt;

Comparator<File> fc = new Comparator<File>(){
    @Override public int compare(File o1, File o2) {
        return o1.compareTo(o2);
    }
};

然后我排序,我从字典排序中得到了这个排序(order3):

{someRoot}
{someRoot}/aardvark.txt
{someRoot}/bar
{someRoot}/bar/file1.txt
{someRoot}/foo
{someRoot}/food.txt
{someRoot}/foo/abracadabra.txt
{someRoot}/foo/baz
{someRoot}/foo/baz/file3.txt
{someRoot}/foo/file2.txt
{someRoot}/zebra.txt

但我不想要这种排序(有问题:注意food.txt 位于目录foo 及其子项之间),我想要order1。我怎样才能写一个比较器来得到我呢?

【问题讨论】:

  • 将 food.txt 放在那个位置的规则是什么? barista.txt 会放在“/bar/file1.txt”和“/foo/”之间吗?
  • Order1 也称为“深度优先”。该短语可能有助于您的搜索。
  • 在 order1 中,barista.txt 将放在它们之后。需要首先比较大多数特定目录。
  • @Kelly:我从未听说过深度优先比较器,只是深度优先搜索。
  • @Jason:它用于描述必须遍历树的递归解析器的行为,有点像你正在做的事情。有“深度优先”和“广度优先”之分。它们描述了在对等节点之前枚举子节点之间做出的选择,反之亦然。

标签: java file comparator


【解决方案1】:

这在我的测试中有效。

new Comparator<File>() {
    @Override
    public int compare(File first, File second) {
        if (first.isDirectory() && second.isDirectory())
            return first.compareTo(second);

        if (first.isDirectory())
            return this.compareToFile(first, second);

        if (second.isDirectory())
            return -(this.compareToFile(second, first));

        return this.compareFiles(first, second);
    }

    private int compareFiles(File first, File second) {
        File firstParentFile = first.getParentFile();
        File secondParentFile = second.getParentFile();

        if (isSubDir(firstParentFile, secondParentFile))
            return -1;

        if (isSubDir(secondParentFile, firstParentFile))
            return 1;

        return first.compareTo(second);
    }

    private int compareToFile(File directory, File file) {
        File fileParent = file.getParentFile();
        if (directory.equals(fileParent))
            return -1;

        if (isSubDir(directory, fileParent))
            return -1;

        return directory.compareTo(file);
    }

    private boolean isSubDir(File directory, File subDir) {
        for (File parentDir = directory.getParentFile(); parentDir != null; parentDir = parentDir.getParentFile()) {
            if (subDir.equals(parentDir)) {
                return true;
            }
        }

        return false;
    }

【讨论】:

  • 只有一件事,isSubDir 中的 for 循环应该是: for (File parentDir = directory.getParentFile(); parentDir != null; parentDir = parentDir.getParentFile()) {
  • @dagge 我猜这可能会导致我编写 compareFiles 的方式出现问题(尽管 compareToFiles 可以使用任何一种方式)。固定的。谢谢。
【解决方案2】:

这个递归得到你喜欢的排序文件树:

public static void main(String[] args) {
    List<File> files = getFileTree(new File("."));

    for (File f : files)
        System.out.println(f);
}

private static List<File> getFileTree(File file) {

    List<File> files = new LinkedList<File>();
    files.add(file);

    if (file.isDirectory()) {
        File[] current = file.listFiles();
        Arrays.sort(current, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (o1.isDirectory())
                    return o2.isDirectory() ? o1.compareTo(o2) : -1;
                else if (o2.isDirectory())
                    return 1;

                return o1.compareTo(o2);
            }
        });

        for (File f : current)
            files.addAll(getFileTree(f));
    }

    return files;
}

【讨论】:

  • +1 -- 但我不确定我是否要使用这种方法; GUI 表有几种可能的排序顺序,我可能需要重新排序才能达到我想要的顺序。
  • @Jason S:啊!现在我得到了你想要的.. :)
  • 谢谢你节省了整个晚上的工作
【解决方案3】:
Comparator<File> fc = new Comparator<File>(){
        @Override public int compare(File o1, File o2) {
            boolean isFirstDirectory = o1.isDirectory();
            boolean isSecondDirectory = o2.isDirectory();

            if(!isFirstDirectory && !isSecondDirectory) {
                return o1.compareTo(o2);

            } else if (isFirstDirectory && !isSecondDirectory){
                return (int) 1;
            } else if(isSecondDirectory && !isFirstDirectory) {
                return -1;
            } else {
                return o1.compareTo(o2);
            }


        }
    };

【讨论】:

  • 不,这行不通。我不希望所有目录都位于所有文件之前。在给定的父目录中,我希望其子目录位于其文件之前。
【解决方案4】:

编辑 您必须比较第一级的基本目录和第二级的文件名

import java.io.File;
import java.io.IOException;
import java.util.Comparator;

public class FileComparator implements Comparator<File> {

    @Override
    public int compare(File o1, File o2) {

        int pathCompare = this.getPath(o1).compareTo(this.getPath(o2));
        if (pathCompare != 0) {
            return subPathCompare;
        } else {
            if (o1.isDirectory() && !o2.isDirectory()) {
                return -1;
            } else if (!o1.isDirectory() && o2.isDirectory()) {
                return 1;
            } else {
                return o1.compareTo(o2);
            }
        }
    }

    //maybe there is a better way to speparete path and file name, but it works
    private String getPath(File file) {
        if (file.isFile()) {
            return file.getParent().toLowerCase();
        } else {
            return file.getPath().toLowerCase();
        }
    }
}

如果路径和文件的分离是必要的,因为如果只有一个:

        if (o1.isDirectory() && !o2.isDirectory()) {
            return -1;
        } else if (!o1.isDirectory() && o2.isDirectory()) {
            return 1;
        } else {
            return o1.compareTo(o2);
        }

结果不是第一种情况,而是另一种情况,所有目录都是第一项,文件是最后一项:

  \dir1\
  \dir2\
  \dir3\
  \dir4\
  \dir1\file1\
  \dir1\file2\
  \dir2\file1\
  \dir2\file2\

所以我的解决方案是首先检查两个比较文件是否属于同一目录。

  • 如果它们不属于同一个目录,那么就按那里的目录进行比较。
  • 如果属于同一目录,则子目录“排序”“在”普通文件之前。

【讨论】:

  • 我已经知道该怎么做,但我不知道如何将它合并到一个工作的比较器中。
【解决方案5】:

我确实找到了一种方法来实现我想要的伪相反(目录 after 文件,没有检查文件和目录名称位于根级别的边缘情况)。

...不,将其转换为我想要的并不是一件容易的事,因为我使用的是目录的字典排序,它将不同级别的目录排序和同一级别的目录排序结合在一起。

static class FileDirectoryPriorityComparator implements Comparator<File>
{
    @Override public int compare(File f1, File f2) {
        if (f1.isDirectory())
        {
            if (f2.isDirectory())
            {
                return f1.compareTo(f2);                        
            }
            else
            {
                return compareDirFile(f1, f2);
            }
        }
        else
        {
            if (f2.isDirectory())
            {
                return -compareDirFile(f2, f1);
            }
            // f1 and f2 are both files
            else
            {
                return compareFiles(f1, f2);
            }
        }
    }

    private int compareDirFile(File dir, File file) {
        /* 
         * If dir compares differently to file's parent, use that ordering.
         * Otherwise, dir comes before file. 
         */              
        File fparent = file.getParentFile();

        if (fparent == null)
            return -1;

        int i = dir.compareTo(fparent);
        if (i != 0)
            return i;

        return -1;
    }

    private int compareFiles(File f1, File f2) {
        /* 
         * If f1's parent compares differently to f2's parent, use that ordering.
         * Otherwise use default ordering.
         */              
        File fp1 = f1.getParentFile();
        File fp2 = f2.getParentFile();

        if (fp1 == null)
        {
            if (fp2 != null)
            {
                return 1;
            }
        }
        else 
        {
            if (fp2 == null)
            {
                return -1;
            }
            else
            {
                int i = fp1.compareTo(fp2);
                if (i != 0)
                    return i;                   
            }
        }           

        return f1.compareTo(f2);
    }
}

【讨论】:

    【解决方案6】:

    当然为时已晚。无论如何,这里有一个 Comparator 实现,按照这个 SO 的要求进行排序。

    public class DirectoryBeforeFileComparator implements Comparator<File> {
        @Override
        public int compare(File o1, File o2) {
            if (o1.isDirectory() && !o2.isDirectory()) {
                // directory before non-directory.
                return -1;
            }
            if (!o1.isDirectory() && o2.isDirectory()) {
                // non-directory after directory
                return 1;
            }
            // compare two pathnames lexicographically
            return o1.compareTo(o2);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      • 2012-11-18
      相关资源
      最近更新 更多