合并排序的文件Java

时间:2017-12-25 13:40:08

标签: java

使用Java实现外部合并排序。

因此,给定一个文件,我将其拆分为较小的文件,然后对较小的部分进行排序,最后合并已排序的(较小的)文件。

所以,最后一步就是我遇到了麻烦。

我有一个文件列表,我想在每一步,取每个文件的第一行的最小值,然后删除该行。

所以,它应该是这样的:

public static void mergeSortedFiles(List<File> sorted, File output) throws IOException {
    BufferedWriter wf = new BufferedWriter(new FileWriter(output));
    String curLine = "";
    while(!sorted.isEmpty()) {
        curLine = findMinLine(sorted);
        wf.write(curLine);
    }
}

public static String findMinLine(List<File> sorted) throws IOException {
    List<BufferedReader> brs = new ArrayList<>();
    for(int i =0; i<sorted.size() ; i++) {
        brs.add(new BufferedReader(new FileReader(sorted.get(i))));
    }
    List<String> lines = new ArrayList<>();
    for(BufferedReader br : brs) {
        lines.add(br.readLine());
    }
    Collections.sort(lines);
    return lines.get(0);
}

我不确定如何更新文件,任何人都可以帮忙吗?

感谢您的帮助!

3 个答案:

答案 0 :(得分:2)

您可以在每个文件周围创建一个Comparable包装器,然后将包装器放在堆中(例如PriorityQueue)。

public class ComparableFile<T extends Comparable<T>> implements Comparable<ComparableFile<T>> {
    private final Deserializer<T> deserializer;
    private final Iterator<String> lines;
    private T buffered;

    public ComparableFile(File file, Deserializer<T> deserializer) {
        this.deserializer = deserializer;
        try {
            this.lines = Files.newBufferedReader(file.toPath()).lines().iterator();
        } catch (IOException e) {
            // deal with it differently if you want, I'm just providing a working example
            // and wanted to use the constructor in a lambda function
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public int compareTo(ComparableFile<T> that) {
        T mine = peek();
        T theirs = that.peek();

        if (mine == null) return theirs == null ? 0 : -1;
        if (theirs == null) return 1;
        return mine.compareTo(theirs);
    }

    public T pop() {
        T tmp = peek();

        if (tmp != null) {
            buffered = null;
            return tmp;
        }

        throw new NoSuchElementException();
    }

    public boolean isEmpty() {
        return peek() == null;
    }

    private T peek() {
        if (buffered != null) return buffered;
        if (!lines.hasNext()) return null;
        return buffered = deserializer.deserialize(lines.next());
    }
}

然后,您可以这样合并它们:

public class MergeFiles<T extends Comparable<T>> {
    private final PriorityQueue<ComparableFile<T>> files;

    public MergeFiles(List<File> files, Deserializer<T> deserializer) {
        this.files = new PriorityQueue<>(files.stream()
                .map(file -> new ComparableFile<>(file, deserializer))
                .filter(comparableFile -> !comparableFile.isEmpty())
                .collect(toList()));
    }

    public Iterator<T> getSortedElements() {
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return !files.isEmpty();
            }

            @Override
            public T next() {
                if (!hasNext()) throw new NoSuchElementException();
                ComparableFile<T> head = files.poll();
                T next = head.pop();
                if (!head.isEmpty()) files.add(head);
                return next;
            }
        };
    }
}

这里有一些代码可以证明它有效:

public static void main(String[] args) throws IOException {
    List<File> files = Arrays.asList(
            newTempFile(Arrays.asList("hello", "world")),
            newTempFile(Arrays.asList("english", "java", "programming")),
            newTempFile(Arrays.asList("american", "scala", "stackoverflow"))
    );

    Iterator<String> sortedElements = new MergeFiles<>(files, line -> line).getSortedElements();
    while (sortedElements.hasNext()) {
        System.out.println(sortedElements.next());
    }
}

private static File newTempFile(List<String> words) throws IOException {
    File tempFile = File.createTempFile("sorted-", ".txt");
    Files.write(tempFile.toPath(), words);
    tempFile.deleteOnExit();
    return tempFile;
}

输出:

american
english
hello
java
programming
scala
stackoverflow
world

答案 1 :(得分:0)

那么你想要做的是在文本文件中交换两行?你可以使用RandomAccessFile来做到这一点,但是每次你交换两行时你都要等待下一次IO爆发,这将是非常慢的。 所以我强烈建议您使用以下代码在堆上进行合并排序:

List<String> lines1 = Files.readAllLines(youFile1);
List<String> lines2 = Files.readAllLines(youFile2);
//use merge sort on theese lines
List<String> merged;
FileWriter writer = new FileWriter(yourOutputFile); 
for(String str: merged) {
    writer.write(str + System.lineSeparator());
}
writer.close();

答案 2 :(得分:0)

固定数量的文件(比如2)之间的标准合并技术是:

  • 有一个变量用于每个文件的当前记录的排序键的值(对于java,使该变量可比较)。
  • 通过读取每个文件的第一条记录(并填写相应的变量)来启动该过程
  • 通过基本上
  • 的代码块循环(直到两者都为文件结尾)

if(key_1.compareTo(key_2)== 0){处理两个文件;然后读两个文件} 否则if(key_1.compareTo(key_2)== -1){process file 1;然后读取文件1} else {process file 2;然后读取文件2}

请注意,此代码基本上只是确定具有最低密钥的文件,并处理该代码。

如果您的文件数量是可变的,那么您的关键变量数量也是可变的,并且“确定具有最低当前关键字的文件”无法按上述方式完成。相反,拥有与文件一样多的current_key_value对象,并将它们全部存储在TreeSet中。现在,TreeSet的第一个元素将是所有文件的当前最低键值,如果您确保在键变量和文件号之间保持链接,则只需处理该文件(并删除刚刚处理的键值)从TreeSet中读取已处理文件中的新记录,并将其键值添加到TreeSet中。