Как сортировать свойства в java?

У меня есть переменная Properties, и иногда мне нужно добавить к ней другие свойства.

Properties myBasicProps = this.getClass.getResourceAsStream(MY_PROPS_PATH);
...
Properties otherProps = new Properties();
otherProps.load(new StringReader(tempPropsString)); //tempPropsString contains my temporary properties
myBasicProps.putAll(otherProps);

Я хочу отсортировать myBasicProps после этого. Я не хочу получать все ключи и значения, сортировать их с помощью Collections.sort(), а затем переносить все на новый объект. Есть ли лучший способ?

Ответы

Ответ 1

Нет, java.util.Properties extends java.util.Hashtable, который не определяет предсказуемый порядок сортировки для ключей или значений.

Вы можете попробовать сбросить все значения во что-то вроде java.util.TreeMap, что наложит естественный порядок на ваши ключи.

Ответ 2

все, что вам нужно сделать, это создать класс, который расширяет свойства.  источник: java2s.com

import java.io.FileOutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

public class Main{
  public static void main(String[] args) throws Exception {
    SortedProperties sp = new SortedProperties();
    sp.put("B", "value B");
    sp.put("C", "value C");
    sp.put("A", "value A");
    sp.put("D", "value D");
    FileOutputStream fos = new FileOutputStream("sp.props");
    sp.store(fos, "sorted props");
  }

}
class SortedProperties extends Properties {
  public Enumeration keys() {
     Enumeration keysEnum = super.keys();
     Vector<String> keyList = new Vector<String>();
     while(keysEnum.hasMoreElements()){
       keyList.add((String)keysEnum.nextElement());
     }
     Collections.sort(keyList);
     return keyList.elements();
  }

}

он работает для меня.

Ответ 3

Переопределение keys хорошо работает с Java 8, но с Java 9 новая реализация store методов больше не вызывает keys метода, а метод entrySet.

Таким образом, вы должны переопределить entrySet чтобы ваши Properties отсортированы с Java 8/9/10 при сохранении.

Вот пример с встроенным переопределением:

Properties properties = new Properties() {

    private static final long serialVersionUID = 1L;

    @Override
    public Set<Object> keySet() {
        return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()));
    }

    @Override
    public Set<Map.Entry<Object, Object>> entrySet() {

        Set<Map.Entry<Object, Object>> set1 = super.entrySet();
        Set<Map.Entry<Object, Object>> set2 = new LinkedHashSet<Map.Entry<Object, Object>>(set1.size());

        Iterator<Map.Entry<Object, Object>> iterator = set1.stream().sorted(new Comparator<Map.Entry<Object, Object>>() {

            @Override
            public int compare(java.util.Map.Entry<Object, Object> o1, java.util.Map.Entry<Object, Object> o2) {
                return o1.getKey().toString().compareTo(o2.getKey().toString());
            }
        }).iterator();

        while (iterator.hasNext())
            set2.add(iterator.next());

        return set2;
    }

    @Override
    public synchronized Enumeration<Object> keys() {
        return Collections.enumeration(new TreeSet<Object>(super.keySet()));
        }
    };

Ответ 4

@danisupr4 имеет лучшее решение.

Я бы немного улучшил его, чтобы вы не получали никаких предупреждений в своей среде IDE:

public static class MyProperties extends Properties {
    private static final long serialVersionUID = 1L;

    public Enumeration<Object> keys() {
        Enumeration<Object> keysEnum = super.keys();
        Vector<Object> keyList = new Vector<Object>();

        while (keysEnum.hasMoreElements()) {
            keyList.add(keysEnum.nextElement());
        }

        Collections.sort(keyList, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                return o1.toString().compareTo(o2.toString());
            }
        });

        return keyList.elements();
    }
}

Ответ 5

TreeMap должен быть самым простым способом:

Properties myProps = this.getClass.getResourceAsStream(MY_PROPS_PATH);

try {
    myProps.load(new FileInputStream(extraPropertiesFilename));
        //you can load more properties from external file

    Map<String, String> sortedMap = new TreeMap(myProps);

    //output sorted properties (key=value)
    for (String key : sortedMap.keySet()) {
        System.out.println(key + "=" + sortedMap.get(key));
    }

} catch (Exception e) {
    e.printStackTrace();
}

Ответ 6

гораздо проще просто отсортировать ключи:

List<String> keys = new ArrayList<String>()
for(String key : properties.stringPropertyNames()) {
  keys.add(key)
}

Collections.sort(keys);

Ответ 7

Решение работает также для Java> 8

Несмотря на то, что в этом вопросе явно не упоминается о функциональности store(OutputStream out, String comments), я считаю, что это (и load(InputStream inStream) соответственно) является наиболее интересной частью Properties.

Если бы мы не были заинтересованы в сохранении/загрузке свойств, мы могли бы просто заменить Properties на некоторую реализацию Map или SortedMap.

Отличие в Properties по сравнению с Map заключается в существовании методов store()/load(), которые предлагают некоторые специальные правила сериализации. Эти правила не изменились (и не будут) в разных версиях Java. например

  • Хранение пар, таких как prop=value
  • Экранирование правил для юникода и специальных символов.
  • Добавление необязательного комментария сверху

К сожалению, вышеупомянутые 3 части функциональности скрыты внутри частных методов, и не может быть легко повторно использован внутри расширенных или альтернативных реализаций, которые хотят использовать различные внутренние структуры данных (например, для сохранения отсортированных свойств). Поэтому остается сохранить те же внутренние структуры и переопределить два метода store().

Примечание: для получения отсортированных свойств, некоторое время назад я следовал реализации danisupr4, но на java9 это сломалось.

TL; DR

Приведенный ниже код поддерживает полную функциональность Properties, переопределяя только метод store(OutputStream out, String comments), и тестируется со всеми версиями из java5 - java12.
Я считаю, что фильтрация выходных данных этого открытого метода делает реализацию менее хрупкой для будущих изменений кода в классе java.util.Properties.


    class SortedStoreProperties extends Properties {

        @Override
        public void store(OutputStream out, String comments) throws IOException {
            Properties sortedProps = new Properties() {
                @Override
                public Set<Map.Entry<Object, Object>> entrySet() {
                    /*
                     * Using comparator to avoid the following exception on jdk >=9: 
                     * java.lang.ClassCastException: java.base/java.util.concurrent.ConcurrentHashMap$MapEntry cannot be cast to java.base/java.lang.Comparable
                     */
                    Set<Map.Entry<Object, Object>> sortedSet = new TreeSet<Map.Entry<Object, Object>>(new Comparator<Map.Entry<Object, Object>>() {
                        @Override
                        public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2) {
                            return o1.getKey().toString().compareTo(o2.getKey().toString());
                        }
                    }
                    );
                    sortedSet.addAll(super.entrySet());
                    return sortedSet;
                }

                @Override
                public Set<Object> keySet() {
                    return new TreeSet<Object>(super.keySet());
                }

                @Override
                public synchronized Enumeration<Object> keys() {
                    return Collections.enumeration(new TreeSet<Object>(super.keySet()));
                }

            };
            sortedProps.putAll(this);
            sortedProps.store(out, comments);
        }
    }

Примечание. В зависимости от того, кто вызывает store(), может быть даже лучше не переопределять существующий метод, но создать новый: storeSorted().
Кроме того, для простоты я переопределяю только один из методов store(), но та же концепция относится к обоим.

Ответ 8

Я сделал, переопределив метод хранения, например:

@Override
public void store(Writer writer, String comments) throws IOException {

    this.keySet().stream().map(k -> (String)k).sorted().forEach(k -> {
        try {
            writer.append(String.format("%s=%s\n", k, get(k)));
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

}

Предыдущие ответы мне не подходят.

Ответ 9

вы можете переопределить метод keys() для сохранения отсортированных свойств в txt файле:

//this method downloaded from edu.umd.cs.findbugs.config package
@SuppressWarnings("unchecked")
@Override
public synchronized Enumeration<Object> keys() {
    Set<?> set = keySet();
    return (Enumeration<Object>) sortKeys((Set<String>) set);
}
static public Enumeration<?> sortKeys(Set<String> keySet) {
    List<String> sortedList = new ArrayList<String>();
    sortedList.addAll(keySet);
    Collections.sort(sortedList);
    return Collections.enumeration(sortedList);
}

и переопределить метод stringPropertyNames() для сохранения отсортированных свойств в файле xml:

@Override
public Set<String> stringPropertyNames() {
Set<String> tmpSet = new TreeSet<String>();
for (Object key : keySet())
{
    tmpSet.add(key.toString());
}
    return tmpSet;
}