
In a presentation about teaching new Java features, I casually mentioned that my textbooks for beginning students include a list of common loop patterns (counting matches, maximum, and so on). After all, students can't realistically be expected to invent them on the spot each time they are needed. Attendees asked me about that list. Here it is.
Beginning students often struggle with writing useful programs. So much of the instruction covers the syntax. Think of loops. Students are taught about while loops, for loops, and do/while loops. When faced with an actual problem to solve, they then ponder which of those loop types they should employ. That's not wrong, but it doesn't get them very far. Which should not be surprising. Imagine trying to speak a foreign language after being taught about nouns, verbs, adjectives, subjects and objects.
That's not how foreign language teaching works these days. Students get plenty of patterns. How to introduce yourself. How to ask for directions. How to express a desire. Sure, the grammar elements are taught as well. But always in the context of getting the job done. That way, when asked “How are you”, you don't have to reinvent the response from first principles.
When I teach loops, of course I teach the syntax. Briefly. And then I move on to the most common loop types. I don't assume that students can discover them on their own. I tediously dissect them. Twice. In the chapter on loops. And again in the chapter on arrays. That way, when a project calls for the maximum value, students hopefully have the algorithm at their fingertips instead of responding with: “By golly, I think I need a for loop.” That's what I casually mentioned in my presentation.
The exact list depends on the course/book/language, but here is a baker's dozen of examples in Java.
In a beginning course, I don't actually spend any effort on the “With API” column. In Java, C++, and Python, the API is rather higgedly-piggedly and not really helpful to students. They should learn the basic algorithms. Just like you learn how to introduce yourself, how to ask for directions, and how to express a desire, when you learn a foreign language.
What's with those toes, by the way? I have such an image next to the “element separators” algorithm in my textbooks. With a caption “To print five elements, you need four separators.”. Once you've seen it, it's very hard to un-see.
In the table below, strings is an ArrayList<String>, in is a Scanner, and values is a partially filled int[]. To try out the code in JShell, start with these definitions:
var strings = new ArrayList<String>(List.of("Mary ate a guava in Java surrounded by lava".split(" ")));
var in = new Scanner("2 3 5 7 11 13 17 19 23"); // Repeat each time the contents is consumed
| Pattern | Pseudocode | Example | With API |
|---|---|---|---|
| Sum | sum = 0 for each element e sum = sum + measure of e |
int sum = 0;
for (String e : strings) {
sum = sum + e.length();
}
|
int sum = strings.stream() .mapToInt(s -> s.length()) .sum(); |
| Average | sum = 0 for each element e sum = sum + measure of e if number of elements > 0 average = sum / number of elements else average = 0 |
int sum = 0;
int count = 0;
while (in.hasNextInt()) {
int e = in.nextInt();
sum = sum + e;
count++;
}
double average = 0;
if (count > 0) {
average = sum * 1.0 / count;
}
|
double average = in.tokens() .mapToInt(t -> Integer.parseInt(t)) .average() .orElse(0) |
| Counting matches | count = 0
for each element e
if e matches
count++
|
int count = 0;
for(String e : strings) {
if (e.contains("ava")) {
count++;
}
}
|
int count = (int) strings.stream()
.filter(e -> e.contains("ava"))
.count();
|
| Finding the first match (and optionally its position) | found = false
position = 0
while (not found and there are more elements) {
e = next element
if e matches
found = true
match = e
else
position++
}
|
boolean found = false;
int position = 0;
String match = null;
while (!found && position < strings.size()) {
String e = strings.get(position);
if (e.contains("ava")) {
found = true;
match = e;
} else {
position++;
}
}
|
String match = strings.stream()
.filter(s -> s.contains("ava"))
.findFirst()
.orElse(null);
int position = IntStream.range(0, strings.size())
.filter(i -> strings.get(i).contains("ava"))
.findFirst()
.orElse(-1);
To find the position of an element, use int position = strings.indexOf("Java");
|
| Finding all matches | result = empty list
for each element e
if e matches
add e to result
|
var result = new ArrayList<String>();
for (String e : strings) {
if (e.contains("ava")) {
result.add(e);
}
}
|
List<String> matches = strings.stream()
.filter(e -> e.contains("ava"))
.toList();
|
| Maximum | largest = first element
for all remaining elements e
if measure of e > measure of largest
largest = e
|
String largest = strings.get(0);
for (int i = 1; i < strings.size(); i++) {
String e = strings.get(i);
if (e.length() > largest.length()) {
largest = e;
}
}
|
String largest = strings.stream() .max(Comparator.comparing(String::length)) .get() |
| Position of the Maximum | largestPosition = 0
for i = 1 to number of elements - 1
if measure of element at i > measure of
element at largestPosition
largestPosition = i
|
int largestPosition = 0;
for (int i = 1; i < strings.size(); i++) {
String e = strings.get(i);
if (strings.get(i).length() > strings.get(
largestPosition).length()) {
largestPosition = i;
}
}
|
int largestPosition = IntStream.range(0, strings.size())
.boxed()
.max((i, j) -> Integer.compare(strings.get(i).length(),
strings.get(j).length()))
.get();
|
| Comparing adjacent values | current = first element for all remaining elements e previous = current current = e Compare previous and current |
String current = in.next();
while (in.hasNext()) {
String previous = current;
current = in.next();
if (current.equals(previous)) {
System.out.println("Repeated: " + current);
}
}
|
N/A |
| Element separators | first = true
for each element e
if first
first = false
else
Append separator
Append e
|
String result = "";
boolean first = true;
for (String e : strings) {
if (first) { first = false; }
else { result += " | "; }
result += e;
}
|
String result = String.join(" | ", strings)
|
| Filling | for i from 0 to number of elements - 1 element at i = Compute value from i |
int[] values = new int[100];
for (int i = 0; i < values.length; i++) {
values[i] = i * i;
}
currentSize = 100;
|
int[] values = IntStream.range(0, n) .map(i -> i * i).toArray() |
| Removing the element at a given position | for i from position + 1 to number of elements - 1 element at i - 1 = element at i Decrement number of elements |
int position = 10;
for (int i = position + 1; i < currentSize; i++) {
values[i - 1] = values[i];
}
currentSize--;
|
Partially filled array:System.arraycopy(values, position + 1, values, position, currentSize - position - 1); currentSize--;List: strings.remove(i); |
| Inserting an element at a given position | if there is room for another element
Increment number of elements
for i from number of elements - 1 to position + 1
element at i = element at i - 1
element at position = new element
|
if (currentSize < values.length) {
currentSize++;
for (int i = currentSize - 1; i > position; i--) {
values[i] = values[i - 1];
}
values[position] = newElement;
}
|
Partially filled array:if (currentSize < values.length) {
currentSize++;
System.arraycopy(values, position, values, position + 1,
currentSize - position - 1);
values[position] = newElement;
}
List:strings.add(i, newElement) |
| Reading input into an array or list | for each input e
if there is room in the target
append e to the target
|
int currentSize = 0;
while (in.hasNextInt() && currentSize < values.length) {
values[currentSize] = in.nextInt();
currentSize++;
}
|
int[] values = in.tokens() .mapToInt(Integer::parseInt) .toArray(); |
Comments powered by Talkyard.