First, one sees you are still a beginner, hence first some pointer to improving code. And after that your question to general improvement, where you were correct to suspect there should be something better.
inorderList
returns its parameter, so can simply return void
.
- It is better to program against interfaces, as then the implementation is free,
you can later change the implementation class. You can assign
Collections.emptyList(), Collections.singletonList(), Collections.asList
.
- You can use the diamond operator
<>
saving repetitive typing.
StringBuilder
is a utility class to prevent inefficient String concatenation with +
/+=
(100 +=
s would create 100 new strings, dropping 99 old strings).
- Always use
@Override
as that detects typos public String toSting()
.
- I show below the usage of a more compact for-each loop.
So:
private void inorderList(List<T> values) {
if (left != null) {
left.inorderList(values);
}
values.add(data);
if (right != null) {
right.inorderList(values);
}
}
@Override
public String toString() {
List<T> values = new ArrayList<>();
inorderList(values);
StringBuilder sb = new StringBuilder();
for (T value: values) {
if (sb.length() != 0) {
sb.append(", ");
}
sb.values.append(value);
}
return sb.toString();
}
A a comma separated string from multiple items, there exists a utility functions.
But let just answer your original question. Instead of collecting first a possibly huge ordered list, you can visit every node with a piece of code. There are several forms, but the latest is the Stream<T>
.
A usage example would be:
values.stream().forEach(v -> { // T v
if (sb.length() != 0) {
sb.append(", ");
}
sb.values.append(v);
});
But Stream
is a very expressive and unavoidable class.
The implementation of the stream is more sophisticated.
public Stream<T> stream() {
Stream s = Stream.of(data);
if (left != null) {
s = Stream.concat(left.stream(), s);
}
if (right != null) {
s = Stream.concat(s, right.stream());
}
return s;
}
Realize that Stream does not start to iterate until a "final" operation is done such as forEach
, collect
, count
, sum
.
Now you could do:
@Override
public String toString() {
stream().forEach(v -> {
if (sb.length() != 0) {
sb.append(", ");
}
sb.values.append(v);
});
return sb.toString();
}
With variations, like skipping empty strings.
stream()
.filter(Objects::notNull) // T v. Means: v -> Objects.notNull(v).
.map(v -> v.toString()) // T v. Or: Object::toString.
.filter(v -> !v.isBlank()) // String v.
.forEach(v -> {
if (sb.length() != 0) {
sb.append(", ");
}
sb.values.append(v);
});
return sb.toString();
}
This is a bit much, and it is not necessary to start programming high javanese.