diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java
new file mode 100755
index 00000000..8db0e364
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/CountingHash.java
@@ -0,0 +1,126 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.utils;
+
+
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import com.kingsrook.qqq.backend.core.utils.collections.MutableMap;
+
+
+/*******************************************************************************
+ ** Hash that provides "counting" capability -- keys map to Integers that
+ ** are automatically/easily summed to
+ **
+ *******************************************************************************/
+public class CountingHash extends AbstractMap implements Serializable
+{
+ private Map map = null;
+
+
+
+ /*******************************************************************************
+ ** Default constructor
+ **
+ *******************************************************************************/
+ public CountingHash()
+ {
+ this.map = new HashMap<>();
+ }
+
+
+
+ /*******************************************************************************
+ ** Constructor where you can supply a source map (e.g., if you want a specific
+ ** Map type (like LinkedHashMap), or with pre-values.
+ **
+ ** Note - the input map will be wrapped in a MutableMap - so - it'll be mutable.
+ **
+ *******************************************************************************/
+ public CountingHash(Map sourceMap)
+ {
+ this.map = new MutableMap<>(sourceMap);
+ }
+
+
+
+ /*******************************************************************************
+ ** Increment the value for the specified key by 1.
+ **
+ *******************************************************************************/
+ public Integer add(K key)
+ {
+ Integer value = getOrCreateListForKey(key);
+ Integer sum = value + 1;
+ map.put(key, sum);
+ return (sum);
+ }
+
+
+
+ /*******************************************************************************
+ ** Increment the value for the specified key by the supplied addend
+ **
+ *******************************************************************************/
+ public Integer add(K key, Integer addend)
+ {
+ Integer value = getOrCreateListForKey(key);
+ Integer sum = value + addend;
+ map.put(key, sum);
+ return (sum);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ private Integer getOrCreateListForKey(K key)
+ {
+ Integer value;
+
+ if(!this.map.containsKey(key))
+ {
+ this.map.put(key, 0);
+ value = 0;
+ }
+ else
+ {
+ value = this.map.get(key);
+ }
+ return value;
+ }
+
+
+
+ /***************************************************************************
+ *
+ ***************************************************************************/
+ public Set> entrySet()
+ {
+ return this.map.entrySet();
+ }
+
+}
diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CountingHashTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CountingHashTest.java
new file mode 100644
index 00000000..7033b170
--- /dev/null
+++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/CountingHashTest.java
@@ -0,0 +1,76 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.utils;
+
+
+import java.util.Map;
+import com.kingsrook.qqq.backend.core.BaseTest;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+
+/*******************************************************************************
+ ** Unit test for CountingHash
+ *******************************************************************************/
+class CountingHashTest extends BaseTest
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void test()
+ {
+ CountingHash countingHash = new CountingHash<>();
+
+ assertNull(countingHash.get("a"));
+
+ countingHash.add("a");
+ assertEquals(1, countingHash.get("a"));
+
+ countingHash.add("a");
+ assertEquals(2, countingHash.get("a"));
+
+ countingHash.add("a", 2);
+ assertEquals(4, countingHash.get("a"));
+
+ countingHash.add("b", 5);
+ assertEquals(5, countingHash.get("b"));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testAlwaysMutable()
+ {
+ CountingHash alwaysMutable = new CountingHash<>(Map.of("A", 5));
+ alwaysMutable.add("A");
+ alwaysMutable.add("B");
+ assertEquals(6, alwaysMutable.get("A"));
+ assertEquals(1, alwaysMutable.get("B"));
+ }
+
+}
\ No newline at end of file