3
votes

Comment imprimer facilement et proprement plusieurs objets à l'aide d'une méthode toString, qui sont également alignés?

J'ai une ArrayList de membres, chacun avec ses propres informations, y compris le nom, l'âge, la date de naissance, etc., qui est appelée avec une méthode toString lors de l'impression sur le terminal.

Voici le code:

|ID: 12| Name: Casper| Age: 49| Tlf.: 12345678| Active: true| Next payment: 2018-02-12|
|ID: 13| Name: Allan| Age: 69| Tlf.: 12345678| Active: true| Next payment: 2018-01-12|
|ID: 16| Name: Christina| Age: 100| Tlf.: 12345678| Active: false| Next payment: 2018-02-04|
|ID: 19| Name: RICK| Age: 49| Tlf.: 12345678| Active: false| Next payment: 2018-04-14|

Et voici le résultat:

@Override
    public String toString() {
        return "|ID: " + id + "| Name: " + name + "| Age: " + 
              calculateAge(age, LocalDate.now()) + "| Tlf.: " + tlfNo + "| Active: " + 
              activeMember + "| Next payment: " + nextPayment + "|";
    }

Comment puis-je aligner les différentes informations les unes sur les autres ?


13 commentaires

String.format


@Naman mais vous aurez besoin de connaître le maximum pour chacun


tfl est-il toujours 8 chiffres?


@Eugene désolé, je ne vous ai pas compris pour le mot max . vouliez-vous dire la longueur des caractères?


@Naman oui, mais il semble que si le champ tfl est toujours à 8 chiffres, vous n'auriez besoin que de connaître le nom maximum ...


@Naman, vous voulez probablement remplir la chaîne avec des espaces, vous devez donc maintenant la longueur maximale de chaque champ.


En termes de chaînes, il doit y avoir un maximum de caractères dans chaque valeur de types variés qui doivent être connus pour se remplir d'espaces, d'accord. Le simple fait de suggérer String.format serait toujours le moyen de commencer à chercher à améliorer la pièce actuelle. @StephanHogenboom


@Eugene Oui, en fait, «nom» est la seule information qui a une longueur variable.


ID aussi, bien sûr, puisque vous ne savez pas combien vous en aurez.


Sauf si vous souhaitez toujours réserver l'espace maximum pour chaque type de valeur, par ex. onze caractères pour chaque int , la méthode toString () n'est pas au bon endroit. La méthode toString () implémente une opération sans contexte. Formater tous les éléments d'une Liste avec un alignement correct est une opération entièrement différente.


@Eugene ah oui, bien sûr. Bien qu'il s'agisse simplement d'un projet scolaire, le nombre maximal de chiffres d'identification ne dépassera pas 2.


@Holger Alors pourrais-je créer une méthode uniquement pour le formatage de la chaîne (c'est-à-dire formattedString ()) dans la même classe que la méthode toString (), puis demander à la méthode toString d'appeler la méthode formattedString ()?


@ malthe.w oui, ce serait une approche raisonnable, en supposant que formattedString () a un objet paramètre contenant les tailles maximales, déterminées pour une liste particulière dans une étape précédente. Ce serait encore mieux si la classe fournissait également la méthode d'usine pour un tel enregistrement.


3 Réponses :


1
votes

vous pouvez créer une méthode pour cela, car toString ne fonctionnera tout simplement pas:

    Pojo one = new Pojo("Casper", 49, true, 1);
    Pojo two = new Pojo("Allan", 100, true, 10_000);

    System.out.println(printPojos(one, two));



ID: 1    | Name: Casper| Age: 49 | Active: true 
ID: 10000| Name: Allan | Age: 100| Active: true 

J'utilise Strings :: padEnd code > (de guava , mais c'est assez facile à écrire même sans bibliothèque externe).

Pojo dans mon cas ressemble à ceci:

static class Pojo {

    private final int id;
    private final String name;
    private final int age;
    private final boolean active;

    // constructor, getters

}

Et le résultat ressemble à:

static String printPojos(Pojo... pojos) {
    int maxName = Arrays.stream(pojos)
                        .map(Pojo::getName)
                        .mapToInt(String::length)
                        .max()
                        .orElse(0);

    int maxId = Arrays.stream(pojos)
                      .mapToInt(x -> x.getName().length())
                      .max()
                      .orElse(0);

    StringJoiner result = new StringJoiner("\n");
    for (Pojo pojo : pojos) {
        StringJoiner sj = new StringJoiner("|");
        sj.add("ID: " + Strings.padEnd(Integer.toString(pojo.getId()), maxId, ' '));
        sj.add(" Name: " + Strings.padEnd(pojo.getName(), maxName, ' '));
        sj.add(" Age: " + Strings.padEnd(Integer.toString(pojo.getAge()), 3, ' '));
        sj.add(" Active: " + Strings.padEnd(Boolean.toString(pojo.isActive()), 5, ' '));

        result.merge(sj);
    }

    return result.toString();
}


1 commentaires

J'aime votre solution, je ne connais pas très bien Guava. Je vais aller le chercher :)



0
votes

J'ai une implémentation simple pour votre problème, qui ne nécessite pas de bibliothèques externes. Si vous souhaitez utiliser des bibliothèques externes, il y a suffisamment de méthodes disponibles dans Apache-commons ou Guava par exemple. Cela pourrait probablement être fait plus intelligemment mais cela fera l'affaire:

class Member {

  String id;
  String name;
  int age;
  LocalDate date;
  int tlfNo;
  boolean activeMember;
  LocalDate nextPayment;

  public Member(String id, String name, int age, int tlfNo, boolean activeMember, LocalDate nextPayment) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.tlfNo = tlfNo;
    this.activeMember = activeMember;
    this.nextPayment = nextPayment;
  }

  @Override
  public String toString() {
    return String.format("|ID: %s| Name: %s| Age: %s| Tlf.: %s| Active: %s| Next payment: %s|", 
      doFormat(id, "id", 10), doFormat(name, "name", 10),             
      doFormat(String.valueOf(age), "age", 10), 
      doFormat(String.valueOf(tlfNo), "tlfNo", 10), 
      doFormat(String.valueOf(activeMember), "active", 10),             
      doFormat(nextPayment.toString(), "nextPayment", 20));
  }

  String doFormat(String value, String fieldName, int maxLenght) {
    if (value.length() > maxLenght) {
      throw new IllegalArgumentException(String.format("cannot format string longer than %s for %s, value = %s", maxLenght, fieldName, value));
    }
    return padLeftSpaces(value, maxLenght);
  }

  public String padtLefSpaces(String inputString, int length) {
    if (inputString.length() >= length) {
      return inputString;
    }
    StringBuilder sb = new StringBuilder();
    while (sb.length() < length - inputString.length()) {
      sb.append(' ');
    }
    sb.append(inputString);

    return sb.toString();
  }
}

L'idée est de remplir tout le champ avec des espaces et de ne remplacer que les espaces occupés. La méthode pad left est empruntée à bealdung


2 commentaires

C'est une solution vraiment étrange, étant donné que String.format est capable de faire le remplissage que vous faites manuellement dans padLeftSpaces .


@Holger D'accord, je ne savais pas que String.format était capable de remplir automatiquement. Je suppose que j'ai aussi appris quelque chose en essayant de résoudre cette question.



2
votes

Vous pouvez faire quelque chose comme:

 public String toString() {
        return String.format("|id: %10d |Name: %30s| Age: %02d | Tlf.: %d| Active: %s| Next Payment: %s |", id, getNameTrimmed() , age , tlfNo ,    activeMember, nextPayment );
 }

 private  String getNameTrimmed(){
    if(name.length()>30) {
        return name.substring(0,27)+"...";
    }
    return name;
}

De cette façon, l'ID aura 10 caractères (il cassera toujours le format si vous avez des ID plus longs) Le nom aura 30 donc vous devez ajouter une méthode pour vous donner un nom de 30 caractères.


2 commentaires

Cela semble prometteur, mais je veux les espaces après le nom, pas entre «Nom:» et l'entrée du nom. Quelle est la syntaxe pour cela?


ajoutez un signe - au format. Pour l'ID, il deviendra% -10d et pour le nom% -30s