自定义 TreeSet 打印 Map.Entry 值的方式

Customize the way TreeSet prints Map.Entry values

我有一个 TreeSet 包含 Map.Entry<String, Double> 值,当我尝试使用 Iterator 迭代这个结构并打印它的键值对时,标准输出是看起来像的东西:

Tolkien=40.25 JKRowling=35.5 OBowden=14.0

但是我想为我的输出使用 自定义格式 并将 = 符号替换为 -> ,例如:

Tolkien -> 40.25, JKRowling -> 35.5, OBowden -> 14.0

这是我现在的代码:

Iterator iterator;.
iterator = lib.getSortedBooks().iterator(); 
while (iterator.hasNext()) {
    System.out.printf(iterator.next() + " ");
}

正确设置输出格式的最佳方法是什么?

System.out 中的打印方法在内部使用 toString(),因此当您调用 toString() 时,您得到的结果与您在输出中看到的相同。然后用箭头替换那个等号。

System.out.printf(iterator.next().toString().replace("=", " -> ") + " ");

一个更优雅的方法(实际上是正确的方法,因为 Map.Entry 没有承诺 toString() 格式):

Map.Entry<K, V> entry = iterator.next();
System.out.printf(entry.getKey() + " -> " + entry.getValue() + " ");

您只需将 = 字符替换为 -> 字符即可解决您的问题,例如:

Iterator<Entry<String, Double>> iterator = lib.getSortedBooks().iterator(); 
while (iterator.hasNext()) {
    Entry book = iterator.next();
    String bookAsText = book.toString();
    String adjustedText = bookAsText.replace("=", " -> ");

    System.out.print(adjustedText + ", ");
}

请注意,您的输出现在最后有一个尾随 ,。您可以使用 String#substring 方法删除它,或者如果您正在处理最后一个元素(使用 iterator.hasNext() 向前看一个元素)则不附加它,或者只使用 StringJoiner 我'一会儿给你看。


但是,如果您总是想要显式输出,那么您应该不依赖 toString 方法打印它确实如此。这可能会在未来改变,谁知道呢。

您应该明确地构建输出。 StringJoiner class 对此很有帮助,它允许您通过添加分隔符(如 ,)来连接字符串。因此使用这样的代码:

Set<Entry<String, Double>> books = lib.getSortedBooks();
String entryDelimiter = ", "
String valueDelimiter = " -> ";

StringJoiner sj = new StringJoiner(entryDelimiter);
for (Entry<String, Double> book : books) {
    sj.add(book.getKey());
    sj.add(valueDelimiter);
    sj.add(book.getValue());
}
String output = sj.toString();

System.out.println(output);

或更紧凑的Java 8解决方案使用Streams:

String output = lib.getSortedBooks().stream()
    .map(book -> book.getKey() + " -> " + book.getValue())
    .collect(Collectors.joining(", "));

System.out.println(output);

该方法使用集合作为流的源。然后它首先将每个元素转移到 book.getKey() + "->" + book.getValue() 中,这就是 Stream#map 所做的。之后它将所有这些元素与 , 连接起来并最终收集它们。

请注意,如果元素不再排序对您来说没问题,它也可以并行化。因此只需在两者之间调用 Stream#parallel 。它可以将给定的流“转换”为并行流。


根据项目的大小,您应该考虑将书籍的表示形式从 Map.Entry 更改为自己的 class Book,例如:

public class Book() {
    private final String mAuthor;
    private final double mPrice;

    public Book(final String author, final double price) {
        this.mAuthor = author;
        this.mPrice = price;
    }

    public String getAuthor() {
        return this.mAuthor;
    }

    public double getPrice() {
        return this.mPrice;
    }

    @Override
    public String toString() {
        return getAuthor + " -> " + getPrice();
    }
}

此方法对于将来的更改更加灵活,并且还允许您添加您认为最合适的 toString 表示。

因为那么,假设 lib.getSortedBooks() returns 一组 Book 而不是 Map.Entry,你可以简单地做:

String output = lib.getSortedBooks().stream()
    .map(Book::toString)
    .collect(Collectors.joining(", "));