简体   繁体   中英

How to implement toString() for collections?

I often need to print lists or maps, but I don't like the output of their default toString() methods. For example HashMap#toString() outputs something like this:

{key-1=value-1, key-2=value-2, key-3=value-3}

But I would like the output to be like this:

key-1 = value-1, key-2 = value-2, key-3 = value-3

I created a custom class that extends HashMap and wrote my own toString() method, which leads to my real problem:

public final class CustomMap<K, V> extends HashMap<K, V> {

    private static final long serialVersionUID = 1493227382148892732L;

    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();

        final Iterator<Map.Entry<K, V>> entryIterator = entrySet().iterator();

        if (entryIterator.hasNext()) {
            final Map.Entry<K, V> entry = entryIterator.next();

            builder.append(entry.getKey());
            builder.append(" = ");
            builder.append(entry.getValue());
        }

        while (entryIterator.hasNext()) {
            final Map.Entry<K, V> entry = entryIterator.next();

            builder.append(", ");
            builder.append(entry.getKey());
            builder.append(" = ");
            builder.append(entry.getValue());
        }

        return builder.toString();
    }

}

As you can see, I have repeating code, because the , part has to be omitted once. So how can I rewrite this as an fast and efficient method without duplicate code?

That can be easily solved with a boolean :

    final Iterator<Map.Entry<K, V>> entryIterator = entrySet().iterator();
    boolean first = true;
    while (entryIterator.hasNext()) {
        final Map.Entry<K, V> entry = entryIterator.next();
        if (!first)
            builder.append(", ");
        else
            first=false;
        builder.append(entry.getKey());
        builder.append(" = ");
        builder.append(entry.getValue());
    }

Another option is to use Java 8 StringJoiner :

    StringJoiner sj = new StringJoiner(",");
    while (entryIterator.hasNext()) {
        final Map.Entry<K, V> entry = entryIterator.next();
        sj.add(entry.getKey() + " = " + entry.getValue());
    }
    return sj.toString();

Don't extends HashMap rather use String.subString() method on HashMap.toString()

String s=yourMap.toString();//eg-"{key-1=value-1, key-2=value-2, key-3=value-3}"
System.out.println(s.substring(1,s.length()-1)); 

Output: -key-1=value-1, key-2=value-2, key-3=value-3

Or

Create method to remove last comma

public StringBuilder removeComma(StringBuilder sbf) {
                if (sbf.charAt(sbf.length() - 1) == ',') {
                    sbf.deleteCharAt(sbf.length() - 1);
                }
                return sbf;
            }


public String toString() {
        final StringBuilder builder = new StringBuilder();
        final Iterator<Map.Entry<K, V>> entryIterator = entrySet().iterator();
        while (entryIterator.hasNext()) {
            final Map.Entry<K, V> entry = entryIterator.next();
            builder.append(entry.getKey()).append(" = ").append(entry.getValue()).append(",");
        }
        return removeComma(builder).toString();
      }

Another option Using a variable-

 public String toString() {
                String comma="";//initaly empty
                final StringBuilder builder = new StringBuilder();
                final Iterator<Map.Entry<K, V>> entryIterator = entrySet().iterator();
                while (entryIterator.hasNext()) {
                    final Map.Entry<K, V> entry = entryIterator.next();
                    builder.append(comma).append(entry.getKey()).append(" = ").append(entry.getValue());
                    comma=",";//set value of comma
                }
                return removeComma(builder).toString();
              }

I just checked the source of AbstractCollection and there it is done like this:

public final class CustomMap<K, V> extends HashMap<K, V> {

    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 1493227382148892732L;

    @Override
    public String toString() {
        final Iterator<Map.Entry<K, V>> entryIterator = entrySet().iterator();

        if (!entryIterator.hasNext()) {
            return "";
        }

        final StringBuilder builder = new StringBuilder();

        for (;;) {
            final Map.Entry<K, V> entry = entryIterator.next();

            builder.append(entry.getKey());
            builder.append(" = ");
            builder.append(entry.getValue());

            if (!entryIterator.hasNext()) {
                return builder.toString();
            }

            builder.append(", ");
        }
    }

}

This looks very clever and there is only one check in the loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM