22

我有一个Properties对象,有时我需要添加其他对象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(),然后将它们全部放入一个新对象。有没有更好的办法?

4

13 回答 13

22

不,java.util.Propertiesextendsjava.util.Hashtable没有为键或值定义可预测的排序顺序。

您可以尝试将所有值转储到类似的java.util.TreeMap内容中,这将对您的键施加自然顺序。

于 2012-04-23T06:42:15.233 回答
21

您所要做的就是创建扩展属性的类。来源: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();
  }

}

这个对我有用。

于 2013-09-03T08:55:30.800 回答
13

覆盖在Java 8keys中运行良好,但从Java 9开始,方法的新实现不再调用方法,而是调用方法。storekeysentrySet

因此,您也必须覆盖entrySet以在存储时使用Java 8/9/10Properties进行排序。

这是一个内联覆盖的示例:

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()));
        }
    };
于 2018-09-01T10:37:34.267 回答
7

解决方案也适用于 java > 8

尽管问题没有明确提及store(OutputStream out, String comments)功能,但我发现这(和load(InputStream inStream)分别)是Properties.

如果我们对存储/加载属性不感兴趣,我们可以Properties用一些MapSortedMap实现来替换。

不同之Properties处在于/方法Map的存在 提供了一些特定的序列化规则。这些规则没有(也不会)在 java 版本之间发生变化。例如store()load()

  • 存储对像prop=value
  • Unicode 和特殊字符的转义规则。
  • 在顶部添加可选注释

不幸的是,上述 3 部分功能隐藏在私有方法中,并且不能在希望使用不同内部数据结构的扩展或替代实现中轻松重用(例如,用于保持属性排序)。

所以剩下的就是保持相同的内部结构并覆盖这两种store()方法

注意:为了获得排序的属性,前段时间我遵循了 danisupr4实现,但在java9 上它坏了。

TL;博士

下面的代码通过覆盖 only方法维护了完整的功能,并使用-的所有版本进行了测试。 我相信过滤这个公共方法的输出可以使实现对类中未来的代码更改不那么脆弱Propertiesstore(OutputStream out, String comments)java5java12
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()方法,但相同的概念适用于两者。

于 2019-05-02T17:17:38.417 回答
6

@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();
    }
}
于 2014-07-31T13:11:01.167 回答
6

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();
}
于 2015-11-30T21:10:04.733 回答
4

对键进行排序要简单得多:

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

Collections.sort(keys);
于 2014-05-21T20:14:02.087 回答
2

您可以覆盖 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;
}
于 2015-03-05T11:51:05.733 回答
2

我通过覆盖方法存储来做到这一点,如下所示:

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

}

以前的答案不太适合我。

于 2018-05-23T00:32:25.697 回答
1

在 Java 8Properties.keys()中用于存储 properties ,在 Java 9 中这发生了变化Properties.entrySet()并被使用:

爪哇 8

private void store0(BufferedWriter bw, String comments, boolean escUnicode)
    throws IOException
{
...
        for (Enumeration<?> e = keys(); e.hasMoreElements();) {

爪哇 9

private void store0(BufferedWriter bw, String comments, boolean escUnicode)
    throws IOException
{
...
        for (Map.Entry<Object, Object> e : entrySet()) {

我使用这个类来处理两个 java 版本:

public class SortedProperties extends Properties {

    /**
     * constructor
     *
     * @param unsortedProperties
     */
    public SortedProperties(Properties unsortedProperties) {
        putAll(unsortedProperties);
    }

    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public synchronized Enumeration keys() {
        Enumeration<Object> keysEnum = super.keys();
        Vector<String> keyList = new Vector<String>();
        while (keysEnum.hasMoreElements()) {
            keyList.add((String) keysEnum.nextElement());
        }
        Collections.sort(keyList);
        return keyList.elements();
    }

    @Override
    public Set<java.util.Map.Entry<Object, Object>> entrySet() {
        // use a TreeMap since in java 9 entrySet() instead of keys() is used in store()
        TreeMap<Object, Object> treeMap = new TreeMap<>();
        Set<Map.Entry<Object, Object>> entrySet = super.entrySet();
        for (Map.Entry<Object, Object> entry : entrySet) {
            treeMap.put(entry.getKey(), entry.getValue());
        }
        return Collections.synchronizedSet(treeMap.entrySet());
    }
}
于 2020-02-18T07:35:00.623 回答
0

我的解决方案受smillien62 的启发:

final Properties properties = new Properties()
{
  private boolean recurse;

  @Override
  public synchronized Set<Map.Entry<Object, Object>> entrySet()
  {
    if (this.recurse)
    {
      return super.entrySet();
    }
    else
    {
      this.recurse = true;
      final TreeMap<Object, Object> treeMap = new TreeMap<>(this);
      final Set<Map.Entry<Object, Object>> entries = treeMap.entrySet();
      this.recurse = false;
      return entries;
    }
  }
};
于 2020-01-01T12:31:10.897 回答
0

怎么样:

    System.getProperties().entrySet().stream()
            .sorted((e1, e2) -> e1.getKey().toString().compareToIgnoreCase(e2.getKey().toString()))
于 2021-03-03T16:01:18.403 回答
0

尝试使用 TreeMap(按自然顺序对键进行排序)和 StringBuilder(我们也可以使用 sysout,但很容易记录(slf4j)返回值)

public static String getPrettyPrintProps(Properties properties) {
Map<String, String> map = new TreeMap(properties);
StringBuilder sb = new StringBuilder();
map.forEach((k, v) -> sb.append("\n" + k + ":" + v ) );
return  sb.toString();
}
于 2020-04-16T06:06:49.307 回答