Subversion Repositories mkgmap

Rev

Rev 3968 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2017.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

 package uk.me.parabola.imgfmt.app.mdr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.srt.SortKey;

/**
 * Helper class to perform sort on possibly large lists using sort keys.
 * The list are divided into chunks so that the peak memory usage is reduced.
 * @author Gerd Petermann
 *
 * @param <T>
 */

public abstract class LargeListSorter<T extends NamedRecord> {
        private final Sort sort;
       
        public LargeListSorter(Sort sort) {
                this.sort = sort;
        }

        /**
         * Sort list in place.
         * @param list list of records.
         */

        public void sort(List<T> list) {
                mergeSort(0, list, 0, list.size());
        }
       
        /**
         * A merge sort implementation which sorts large chunks using a cache for the keys
         * @param depth recursion depth
         * @param list list to sort
         * @param start position of first element in list
         * @param len number of elements in list
         */

        private void mergeSort(int depth, List<T> list, int start, int len) {
                // we split if the number is very high and recursion is not too deep
                if (len > 1_000_000 && depth < 3) {
                        mergeSort(depth+1,list, start, len / 2); // left
                        mergeSort(depth+1,list, start + len / 2, len - len / 2); // right
                        merge(list,start,len);
                } else {
                        // sort one chunk
//                      System.out.println("sorting list of roads. positions " + start + " to " + (start + len - 1));
                        Map<String, byte[]> cache = new HashMap<>();
                        List<SortKey<T>> keys = new ArrayList<>(len);

                        for (int i = start; i < start + len; i++) {
                                keys.add(makeKey(list.get(i), sort, cache));
                        }
                        cache = null;
                        Collections.sort(keys);
                       
                        for (int i = 0; i < keys.size(); i++){
                                SortKey<T> sk = keys.get(i);
                                T r = sk.getObject();
                                list.set(start+i, r);
                        }
                        return;
                }
        }
       
       
        private void merge(List<T> list, int start, int len) {
//              System.out.println("merging positions " + start + " to " + (start + len - 1));
                int pos1 = start;
                int pos2 = start + len / 2;
                int stop1 = start + len / 2;
                int stop2 = start + len;
                boolean fetch1 = true;
                boolean fetch2 = true;
                List<T> merged = new ArrayList<>();
                SortKey<T> sk1 = null;
                SortKey<T> sk2 = null;
                while (pos1 < stop1 &&  pos2 < stop2) {
                        if (fetch1 && pos1 < stop1) {
                                sk1 = makeKey(list.get(pos1), sort, null);
                                fetch1 = false;
                        }
                        if (fetch2 && pos2 < stop2) {
                                sk2 = makeKey(list.get(pos2), sort, null);
                                fetch2 = false;
                        }
                        int d = sk1.compareTo(sk2);
                        if (d <= 0) {
                                merged.add(sk1.getObject());
                                fetch1 = true;
                                pos1++;
                        } else {
                                merged.add(sk2.getObject());
                                fetch2 = true;
                                pos2++;
                        }
                }
                while (pos1 < stop1) {
                        merged.add(list.get(pos1++));
                }
                while (pos2 < stop2) {
                        merged.add(list.get(pos2++));
                }
                assert merged.size() == len;
                for (int i = 0; i < len; i++) {
                        list.set(start+i, merged.get(i));
                }
        }

        protected abstract SortKey<T>  makeKey(T record, Sort sort, Map<String, byte[]> cache);
}