WHAT YOU WILL MAKE
Create mining statistics briefing MiningReport
The mine car brings a batch of records: screening, conversion, sorting, statistics, each through a section of pipeline. Organize the data with clear behavioral rules, and then calculate the answers by hand so that there is evidence for each number in the briefing. Prerequisites: List, Map, record, method reference and loop. Today, let the mining data ride on the mine cart and pass through screening, processing, sorting and statistics stations in sequence.
Before you start: It is recommended to complete Lessons 24–29 first
- LambdaPassing behavior as value
- PredicateOne parameter, returns true or false
- BiFunctionTwo parameters, return result
01 / 16
Before starting: predict first, then let the program answer
This chapter uses JDK 21 and does not require the preview function. The story is borrowed from the block world, and the example runs on the ordinary Java console; if the values and rules are course models, they will be clearly stated.
Each common example is a complete independent file, saved according to the file name marked on the box. Place each example in a different folder to avoid duplicate names of auxiliary classes. When the complete project is divided into sections, they are connected sequentially in the same file.
Terminal command: replace the Chinese placeholder with the actual file name and class name
javac -encoding UTF-8 ExampleFile.java
java ExampleClass- Prediction: Write the expected output first, or draw out the changes in references and data.
- Verification: Check line by line after running, and then only change one condition.
- Explanation: Explain whether the results come from Java rules, course contracts, or input data.
02 / 16
Lambda is a short piece of behavior
Lambda is not a new method, but a shorter transitive behavior. ore -> ore.length() >= 2 means receiving ore and then answering whether the condition is true.
- LambdaPassing behavior as value
- PredicateOne parameter, returns true or false
- BiFunctionTwo parameters, return result
L30Example01.java · Complete independent example
public class L30Example01 {
public static void main(String[] args) {
java.util.function.Predicate<String> longName = name -> name.length() >= 2;
java.util.function.BiFunction<Integer, Integer, Integer> sum = (x, y) -> x + y;
System.out.println(longName.test("diamond"));
System.out.println(sum.apply(3, 4));
}
}Output
true
703 / 16
Stream pipeline is divided into three sections
Stream describes the data processing method: source provides elements, intermediate operation building steps, and terminates the operation request result. It does not save a copy of the data that can be read over and over again like a List does.
- SourceList.stream provides elements
- Intermediate operationsfilter、map、sorted
- Terminate operationtoList、sum、count
L30Example02.java · Complete independent example
import java.util.*;
public class L30Example02 {
public static void main(String[] args) {
List<String> ores = List.of("Coal", "diamond", "Iron ore");
var pipeline = ores.stream().filter(name -> name.length() >= 2);
System.out.println(pipeline.toList());
}
}Output
[Coal, diamond, Iron ore]| Phase | Example | Effect |
|---|---|---|
| Source | ores.stream | Provide elements |
| Intermediate operations | filter map sorted | Description processing |
| Terminate operation | toList sum count | Produce results |
04 / 16
filter only retains matching elements
filter expresses retention conditions: elements that meet the conditions enter the result. Filtering does not delete the original list members, nor should the source be secretly modified when judging conditions.
- filterSelect elements by true or false
- New resultsCatch with termination operation
- Original Listwill not be deleted as a result
L30Example03.java · Complete independent example
import java.util.*;
record MinedBlock(String ore, int amount, int value) {
MinedBlock {
if (ore == null || ore.isBlank() || amount < 0 || amount > 1_000_000
|| value < 0 || value > 1_000_000)
throw new IllegalArgumentException("Check mineral name, quantity and unit price");
ore = ore.strip();
}
}
public class L30Example03 {
public static void main(String[] args) {
List<MinedBlock> blocks = List.of(new MinedBlock("Coal", 2, 1),
new MinedBlock("diamond", 3, 10));
var rare = blocks.stream().filter(b -> b.value() >= 5).toList();
System.out.println(rare.size());
System.out.println(blocks.size());
}
}Output
1
205 / 16
map changes elements into another form
map like workbench recipe. Each input element is converted once, the number usually remains the same, but the type can change.
- mapConvert elements into new forms
- Return typeCan differ from source
- HashMapIt is a key-value container, and the concept is different
L30Example04.java · Complete independent example
import java.util.*;
public class L30Example04 {
public static void main(String[] args) {
List<String> ores = List.of("diamond", "iron");
System.out.println(ores.stream().map(s -> s.toUpperCase(Locale.ROOT)).toList());
System.out.println(ores.stream().map(String::length).toList());
}
}Output
[DIAMOND, IRON]
[7, 4]06 / 16
sorted control reporting order
Natural ordering is suitable for strings and numbers. Complex records should be clearly sorted by which field, and a second rule can be added when the scores are the same.
- Sorting fieldWhether to specify unit price or subtotal
- Second ruleSame points according to mineral name
- Original sequencesorted does not modify the source
L30Example05.java · Complete independent example
import java.util.*;
record MinedBlock(String ore, int amount, int value) {
MinedBlock {
if (ore == null || ore.isBlank() || amount < 0 || amount > 1_000_000
|| value < 0 || value > 1_000_000)
throw new IllegalArgumentException("Check mineral name, quantity and unit price");
ore = ore.strip();
}
}
public class L30Example05 {
public static void main(String[] args) {
List<MinedBlock> blocks = List.of(new MinedBlock("B", 1, 5),
new MinedBlock("A", 1, 5), new MinedBlock("C", 1, 10));
var names = blocks.stream()
.sorted(Comparator.comparingInt(MinedBlock::value).reversed()
.thenComparing(MinedBlock::ore))
.map(MinedBlock::ore).toList();
System.out.println(names);
System.out.println(blocks.get(0).ore());
}
}Output
[C, A, B]
B07 / 16
Numerical flow is responsible for summation and averaging
The object stream must first be mapped into a numerical stream before it can be directly summed. You can use mapToInt for quantities, and mapToLong for potentially large values; determine the value range first every time.
- long multiplicationConvert first and then calculate
- sumThe empty value stream is 0
- averageThere may be no result, use orElse
L30Example06.java · Complete independent example
import java.util.*;
record MinedBlock(String ore, int amount, int value) {
MinedBlock {
if (ore == null || ore.isBlank() || amount < 0 || amount > 1_000_000
|| value < 0 || value > 1_000_000)
throw new IllegalArgumentException("Check mineral name, quantity and unit price");
ore = ore.strip();
}
}
public class L30Example06 {
public static void main(String[] args) {
List<MinedBlock> blocks = List.of(new MinedBlock("diamond", 3, 10),
new MinedBlock("Iron", 7, 3));
long total = blocks.stream().mapToLong(b -> (long) b.amount() * b.value()).sum();
System.out.println(total);
System.out.println(blocks.stream().mapToInt(MinedBlock::amount).average().orElse(0));
System.out.println(List.<MinedBlock>of().stream()
.mapToLong(MinedBlock::amount).sum());
}
}Output
51
5.0
008 / 16
distinct limit and skip
Sort first and then limit to get the top three; first limit and then sort will only sort the first three of the original list. Pipeline order is algorithmic order.
- distinctDeduplication according to equality rules
- sorted and limitChange the answer in sequence
- skipSkip the specified amount
L30Example07.java · Complete independent example
import java.util.*;
public class L30Example07 {
public static void main(String[] args) {
List<Integer> values = List.of(3, 1, 3, 2);
System.out.println(values.stream().distinct().sorted().limit(2).toList());
System.out.println(values.stream().limit(2).sorted().toList());
System.out.println(values.stream().distinct().sorted().skip(1).toList());
}
}Output
[1, 2]
[1, 3]
[2, 3]09 / 16
toList catches the final result
Java 21's Stream.toList returns an unmodifiable list. It can be read, but adding or removing it will fail.
- toListReturn to unmodifiable list
- Variable copynew ArrayList<>(result)
- Element statusContainer read-only does not mean depth is immutable
L30Example08.java · Complete independent example
import java.util.*;
public class L30Example08 {
public static void main(String[] args) {
List<String> result = List.of("Coal", "diamond").stream().toList();
List<String> editable = new ArrayList<>(result);
editable.add("Iron");
System.out.println(result);
System.out.println(editable);
}
}Output
[Coal, diamond]
[Coal, diamond, Iron]BadToList.java · Intentional error example
import java.util.*;
public class BadToList {
public static void main(String[] args) {
var result = List.of("Coal").stream().toList();
result.add("Iron");
}
}10 / 16
Stream can only be consumed once
Stream is like a one-time minecart route. After reaching the end point, you cannot go back to the starting point and run again.
- Disposable StreamCan no longer be consumed after termination
- Retain sourceList can repeatedly create new streams
- Report againRestart from stream()
BadStreamReuse.java · Intentional error example
import java.util.*;
public class BadStreamReuse {
public static void main(String[] args) {
var stream = List.of("Coal", "Iron").stream();
stream.count();
stream.toList();
}
}L30Example09.java · Complete independent example
import java.util.*;
public class L30Example09 {
public static void main(String[] args) {
List<String> ores = List.of("Coal", "Iron");
System.out.println(ores.stream().count());
System.out.println(ores.stream().toList());
}
}Output
2
[Coal, Iron]11 / 16
Avoid modifying external collections
If the external List is secretly modified in forEach, the data flow direction is divided into two. Returning results is easier to read and test than hiding side effects.
- Avoid hidden side effectsReturn result expression collection
- Do not modify the sourceThe traversal contract requires no interference
- forEachSuitable for actions that must be performed
L30Example10.java · Complete independent example
import java.util.*;
public class L30Example10 {
public static void main(String[] args) {
List<String> ores = List.of("Coal", "diamond", "Iron ore");
List<String> rare = ores.stream().filter(s -> s.length() >= 2).toList();
System.out.println(rare);
}
}Output
[Coal, diamond, Iron ore]12 / 16
Complex Lambda is extracted as a method
After Lambda exceeds one or two lines, extract it into a named method. The main process is clearer and the rules can be tested individually.
- Naming methodExplain complex selection rules
- Method referencePut named actions into pipes
- Independent testingCan be verified without the entire report
L30Example11.java · Complete independent example
import java.util.*;
record MinedBlock(String ore, int amount, int value) {
MinedBlock {
if (ore == null || ore.isBlank() || amount < 0 || amount > 1_000_000
|| value < 0 || value > 1_000_000)
throw new IllegalArgumentException("Check mineral name, quantity and unit price");
ore = ore.strip();
}
}
public class L30Example11 {
static boolean isRare(MinedBlock b) {
return b != null && b.value() >= 5;
}
public static void main(String[] args) {
List<MinedBlock> blocks = List.of(new MinedBlock("Coal", 2, 1),
new MinedBlock("diamond", 3, 10));
System.out.println(blocks.stream().filter(L30Example11::isRare)
.map(MinedBlock::ore).toList());
}
}Output
[diamond]13 / 16
Challenge Blueprint Mining Statistical Briefing
The challenge program is called MiningReport. Each record saves the mineral name, quantity, and single block value, and the Stream generates the total blocks, rare list, total value, and highest value mineral.
- Total quantity14+7+4+3+20=48
- Total value14+21+20+30+0=85
- Highest subtotalDiamond record is 30
Acceptance conditions
- The total number of blocks is 48.
- Rare minerals are arranged from high to low in terms of single piece value.
- The total value is calculated by multiplying the quantity by the unit price.
- The highest value mineral is selected based on the total value of the entire record.
- When the list is empty, the highest value is displayed as None.
| Minerals | Quantity | Unit price | Subtotal |
|---|---|---|---|
| Coal | 14 | 1 | 14 |
| Iron ore | 7 | 3 | 21 |
| Gold ore | 4 | 5 | 20 |
| diamond | 3 | 10 | 30 |
| Cobblestone | 20 | 0 | 0 |
14 / 16
Mining Statistics Program
Create a new MiningReport.java, compile and run using Java 21.
- Clean up the source firstReject null lists and filter empty records
- Screen rareThe unit price is at least 5
- Final stable sortingUnit price in descending order, then by name
MiningReport.java · Paragraph 1/2, sequentially connected to the same file
import java.util.*;
record MinedBlock(String ore, int amount, int value) {
MinedBlock {
if (ore == null || ore.isBlank() || amount < 0 || amount > 1_000_000
|| value < 0 || value > 1_000_000)
throw new IllegalArgumentException("Check mineral name, quantity and unit price");
ore = ore.strip();
}
}
public class MiningReport {
static boolean isRare(MinedBlock block) {
return block != null && block.value() >= 5;
}
static long totalValue(MinedBlock block) {
return (long) block.amount() * block.value();
}
static List<MinedBlock> clean(List<MinedBlock> blocks) {
if (blocks == null || blocks.size() > 1_000_000)
throw new IllegalArgumentException("The record table cannot be null And at most 100 Ten thousand");
return blocks.stream().filter(Objects::nonNull).toList();
}
static long totalBlocks(List<MinedBlock> blocks) {
return clean(blocks).stream().mapToLong(MinedBlock::amount).sum();
}
static long valueOfAll(List<MinedBlock> blocks) {
return clean(blocks).stream().mapToLong(MiningReport::totalValue).sum();
}
static List<String> rareLabels(List<MinedBlock> blocks) {MiningReport.java · Paragraph 2/2, sequentially connected to the same file
return clean(blocks).stream().filter(MiningReport::isRare)
.sorted(Comparator.comparingInt(MinedBlock::value).reversed()
.thenComparing(MinedBlock::ore))
.map(block -> block.ore() + "x" + block.amount()).toList();
}
static String bestOre(List<MinedBlock> blocks) {
return clean(blocks).stream()
.sorted(Comparator.comparingLong(MiningReport::totalValue).reversed()
.thenComparing(MinedBlock::ore))
.map(MinedBlock::ore).findFirst().orElse("None");
}
public static void main(String[] args) {
List<MinedBlock> blocks = List.of(
new MinedBlock("Coal", 14, 1), new MinedBlock("Iron ore", 7, 3),
new MinedBlock("Gold ore", 4, 5), new MinedBlock("diamond", 3, 10),
new MinedBlock("Cobblestone", 20, 0));
System.out.println("Total blocks=" + totalBlocks(blocks));
System.out.println("Rare=" + rareLabels(blocks));
System.out.println("Total value=" + valueOfAll(blocks));
System.out.println("Highest value=" + bestOre(blocks));
}
}Output
Total blocks=48
Rare=[diamondx3, Gold orex4]
Total value=85
Highest value=diamond15 / 16
Expansion tasks and unit review
Lessons 26 to 30 completed key value statistics, collection deduplication, random simulation, exception recovery, file archiving and data pipeline.
- Pipeline explanationExplain the input and output of each step
- Empty resultThe total number is 0, the highest is none
- Boundary rulesQuantity, unit price and total number of records
Third gear operation
- Wooden Pickaxe: Filter a list of integers for even numbers and sum them.
- Iron Pickaxe: Sort items by name length, if the length is the same, then sort items in alphabetical order.
- Diamond Pickaxe: Generate top five, average points and underachievement lists from player records.
| Self-evaluation | Can | Still need to practice |
|---|---|---|
| I can read Stream pipe | □ | □ |
| I can choose filter and map | □ | □ |
| I will handle empty results | □ | □ |
16 / 16
Own acceptance record
Beautiful output is the starting point, and being able to explain boundaries is considered mastery. Select one case each from normal, borderline and error, write down your expectations before running, and leave evidence after running.
| Category | The operation you selected | Check key points |
|---|---|---|
| Normal | A common input or a successful action | Is the result consistent line by line? |
| Border | Range endpoints and adjacent values | Is there one more step or one less thing? |
| Error | Illegal quantity, missing object or bad data | Whether it is explicitly rejected and whether the status is maintained |
- Bug records: triggering conditions, actual phenomena, causes, and minimum repairs.
- Illustrated record: describe the starting point, end point and meaning of each arrow.
- Adaptation record: only change one rule, and modify expectations and tests at the same time.
TAKE IT WITH YOU
Take the lesson offline
The complete lesson, code, diagrams and exercises, in your preferred language.