Reputation: 17
I have been trying to write a method called printableStringCollector
, which basically returns a collector that forms a special printable String as described below -
Printable String spec
It is a multi-line String containing a formatted table of all results and total values and marks. The format of your table should meet the following requirements:
Example:
Student........|Phalaxing |Shieldwalling |Tercioing |Wedging |Total |Mark |
Eco Betty......|0 ........|83............|89........|59......|57.75 |F ...|
Lodbrok Johnny |61 .......|92............|67........|0.......|55.00 |F....|
Paige Umberto..|75....... |94............|0.........|52......|55.25 |F....|
Average........|45.33.....|89.67.........|52.00.....|37.00...|56.00 |F....|
Can anyone help me implement this method?
public Collector<CourseResult, ?, String> printableStringCollector() {
}
CourseResult
class:
public class CourseResult {
private final Person person;
private final Map<String, Integer> taskResults;
public CourseResult(final Person person, final Map<String, Integer> taskResults) {
this.person = person;
this.taskResults = taskResults;
}
public Person getPerson() {
return person;
}
public Map<String, Integer> getTaskResults() {
return taskResults;
}
}
Person
class:
public class Person {
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Person person = (Person) o;
return age == person.age &&
Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, age);
}
@Override
public String toString() {
return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
.add("firstName='" + firstName + "'")
.add("lastName='" + lastName + "'")
.add("age=" + age)
.toString();
}
}
Upvotes: 0
Views: 346
Reputation: 9090
Most of the time, when I need to create a custom collector, I create a new class for it and give it methods accumulate
, combine
and finish
, to match the operators in a collector. That class can do whatever is needed. For instance, where the finish
is optional:
Collector<T, ?, R> collector = Collector.of(
MyCollector::new,
MyCollector::accumulate,
MyCollector::combine,
MyCollector::finish);
In your case you will have to stream twice though, as the collector needs to have seen the length of each student name before it can output anything. The collector would store its CourseResult
instances, as well as the maximum length(s). It would then stream again, and use String.format
to do all the hard work in adding padding (e.g. "%" + maxNameLength + "s | %10s | ..."
).
The finish
method would look like this (simplified):
public String finish() {
String header = "...\n"; // use same padding
String pattern = "...";
return results.stream()
.map(result -> String.format(pattern, result.getPerson().getName(), ...)
.collect(Collectors.joining("\n", header, ""));
}
I wouldn't use the exact same pattern for the header, because the rows contain numbers where the header does not.
Upvotes: 1