diff --git a/2022/README.md b/2022/README.md
index 28c46de9430e425ce27ea2c237c4980c24402eac..a081fdf5cbca4d05ffbbeff11d10abb919d28fd1 100644
--- a/2022/README.md
+++ b/2022/README.md
@@ -49,5 +49,21 @@ Where we parameterized the commonality on Day 1, today we'll simply extract the
 
 ## Day 3
 
+- [The problem](https://adventofcode.com/2022/day/3)
+- [The solution](src/main/java/edu/unl/cse/bohn/year2022/Day3.java)
+
+### Part 1
+
+The subproblems are
+- Determine an item's priority
+- For each knapsack, determine which items are in each compartment
+  - Assume an even non-zero number of items
+- For each knapsack, determine which item is common to both compartments
+  - Assume there is exactly one item that is common
+    - *Aha!* one of the knapsacks in the sample data has 'L' as a common item, twice
+- Sum the priorities of all the "common items"
+
+#Day 4
+
 (coming soon)
 
diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day3.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day3.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e21cef5ccda4ba9174058b92b54e5e751967478
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day3.java
@@ -0,0 +1,98 @@
+package edu.unl.cse.bohn.year2022;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.Iterator;
+import java.util.List;
+
+@SuppressWarnings("unused")
+public class Day3 extends Puzzle {
+    public Day3(boolean isProductionReady) {
+        super(isProductionReady);
+        //noinspection SpellCheckingInspection
+        sampleData = """
+                vJrwpWtwJgWrhcsFMMfFFhFp
+                jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
+                PmmdzqPrVvPwwTWBwg
+                wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
+                ttgJtRGJQctTZtZT
+                CrZsJsPPZsGzwwsLwLmpwMDw""";
+    }
+
+    @Override
+    public long computePart1(List<String> data) {
+        long prioritySum = 0;
+        for (String datum : data) {
+            String compartment1 = datum.substring(0, datum.length() / 2);
+            String compartment2 = datum.substring(datum.length() / 2);
+            if (compartment1.length() != compartment2.length()) {
+                throw new IllegalArgumentException("A knapsack has an odd number of items; cannot divide evenly " +
+                        "between compartments: " + compartment1 + " " + compartment2);
+            }
+            char commonItem = getCommonItem(compartment1, compartment2);
+            prioritySum += getPriority(commonItem);
+        }
+        return prioritySum;
+    }
+
+    @Override
+    public long computePart2(List<String> data) {
+        return 0;
+    }
+
+    private char getCommonItem(String firstCompartment, String secondCompartment) {
+        Character commonItem = null;
+        List<Character> firstCompartmentItems = firstCompartment.chars().sorted().mapToObj(c -> (char) c).toList();
+        List<Character> secondCompartmentItems = secondCompartment.chars().sorted().mapToObj(c -> (char) c).toList();
+        Iterator<Character> firstCompartmentIterator = firstCompartmentItems.iterator();
+        Iterator<Character> secondCompartmentIterator = secondCompartmentItems.iterator();
+        Character firstItem = null;
+        Character secondItem = null;
+        // with the items sorted, we will advance through the items until we reach the end one of the compartments
+        while (firstCompartmentIterator.hasNext() || secondCompartmentIterator.hasNext()) {
+            firstItem = (firstItem == null && firstCompartmentIterator.hasNext()) ?
+                    firstCompartmentIterator.next() : firstItem;
+            secondItem = (secondItem == null && secondCompartmentIterator.hasNext()) ?
+                    secondCompartmentIterator.next() : secondItem;
+            if (commonItem == null && (firstItem == null || secondItem == null)) {
+                throw new IllegalArgumentException("Reached the end of a compartment among "
+                        + firstCompartment + " and " + secondCompartment + " without finding a common item.");
+            }
+            else if (firstItem == secondItem) {
+                if (commonItem == null || commonItem == firstItem) {
+                    commonItem = firstItem;
+                    firstItem = null;
+                    secondItem = null;
+                } else {
+                    throw new IllegalStateException("Found a common item (" + firstItem + ") when one already exists " +
+                            "(" + commonItem + ") for compartments " + firstCompartment + " and " + secondCompartment);
+                }
+            } else if (firstItem != null && secondItem != null) {
+                if (firstItem < secondItem) {
+                    firstItem = null;
+                } else {
+                    secondItem = null;
+                }
+            } else {    // if we've gotten this far with a null item, then we just need to exhaust the remaining string
+                firstItem = null;
+                secondItem = null;
+            }
+        }
+        if (commonItem == null) {
+            throw new IllegalArgumentException("No common item found among "
+                    + firstCompartment + " and " + secondCompartment);
+//                    + firstCompartmentItems + " and " + secondCompartmentItems);
+        }
+        return commonItem;
+    }
+
+    private int getPriority(char c) {
+        if ('a' <= c && c <= 'z') {
+            return (c - 'a' + 1);
+        } else if ('A' <= c && c <= 'Z') {
+            return (c - 'A' + 27);
+        } else {
+            throw new IllegalArgumentException("The character '" + c + "' is not a valid item.");
+        }
+    }
+}