使用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);
}
我不确定如何更新文件,任何人都可以帮忙吗?
感谢您的帮助!
答案 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)之间的标准合并技术是:
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中。