Компаратор <File>для заказа по каталогам - PullRequest
2 голосов
/ 14 декабря 2010

Я в тупике ... Допустим, у меня есть это дерево каталогов:

{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<File> с порядком 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<File>:

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 .Как я могу написать Компаратор, чтобы получить это?

Ответы [ 6 ]

4 голосов
/ 14 декабря 2010

Это работает в моих тестах.

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;
    }
2 голосов
/ 14 декабря 2010

Это рекурсивно возвращает отсортированное дерево файлов, как вам нравится:

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;
}
0 голосов
/ 25 апреля 2017

Конечно, слишком поздно. В любом случае, здесь есть реализация 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);
    }
}
0 голосов
/ 14 декабря 2010

Я нашел способ реализовать псевдо-противоположность того, что хотел (каталоги после файлов, не проверял крайние случаи, когда файлы и имена каталогов находятся на корневом уровне).

... И нет, это не тривиально, чтобы преобразовать это в то, что я хочу, потому что я использую лексикографическое упорядочение каталогов, которое объединяет упорядочение каталогов на разных уровнях и упорядочение каталогов натот же уровень.

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);
    }
}
0 голосов
/ 14 декабря 2010
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);
            }


        }
    };
0 голосов
/ 14 декабря 2010

Редактировать Вы должны сравнить базовый каталог на первом уровне и имя файла на втором уровне

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

результатом будет не случай 1, а другой случай, когда все каталоги будут первыми элементами, а файлы - последними:

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

Так что мойРешение - сначала проверить, принадлежат ли два сравниваемых файла одному и тому же каталогу.

  • Если они не принадлежат одному и тому же каталогу, значит, они сравниваются по этому каталогу.
  • Если принадлежат одному и тому же каталогу, то подкаталоги «сортируются» перед «обычными файлами».
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...