From f0d201d5842447814e24a846441ecdb6172616fc Mon Sep 17 00:00:00 2001 From: ysneve Date: Mon, 27 Jul 2026 13:10:13 +0500 Subject: [PATCH 1/2] Add RedisMap implementation of Map interface --- pom.xml | 4 +- src/main/java/org/redis/Main.java | 3 +- src/main/java/org/redis/model/RedisMap.java | 146 ++++++++++++++++++++ 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/redis/model/RedisMap.java diff --git a/pom.xml b/pom.xml index b4c0aac..f822aac 100644 --- a/pom.xml +++ b/pom.xml @@ -9,8 +9,8 @@ 1.0-SNAPSHOT - 17 - 17 + 21 + 21 UTF-8 diff --git a/src/main/java/org/redis/Main.java b/src/main/java/org/redis/Main.java index 68b3db6..6042eef 100644 --- a/src/main/java/org/redis/Main.java +++ b/src/main/java/org/redis/Main.java @@ -1,7 +1,8 @@ package org.redis; +import org.redis.model.RedisMap; + public class Main { public static void main(String[] args) { - System.out.println("Hello world!"); } } \ No newline at end of file diff --git a/src/main/java/org/redis/model/RedisMap.java b/src/main/java/org/redis/model/RedisMap.java new file mode 100644 index 0000000..41233e9 --- /dev/null +++ b/src/main/java/org/redis/model/RedisMap.java @@ -0,0 +1,146 @@ +package org.redis.model; + +import com.google.gson.Gson; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import redis.clients.jedis.Jedis; + +public class RedisMap implements Map { + + final Gson gson = new Gson(); + final Jedis jedis; + final String redisKey; + private final Class valueClass; + private final Class keyClass; + + public RedisMap(Jedis jedis, String redisKey, Class keyClass, Class valueClass) { + this.jedis = jedis; + this.redisKey = redisKey; + this.keyClass = keyClass; + if (!jedis.isConnected()) { + jedis.connect(); + } + this.valueClass = valueClass; + } + + public RedisMap(String address, int port, String password, String redisKey, + Class keyClass, Class valueClass) { + this.redisKey = redisKey; + this.valueClass = valueClass; + this.keyClass = keyClass; + jedis = new Jedis(address, port); + jedis.auth(password); + jedis.connect(); + } + + public RedisMap(String address, int port, String redisKey, Class keyClass, + Class valueClass) { + this.redisKey = redisKey; + this.valueClass = valueClass; + this.keyClass = keyClass; + jedis = new Jedis(address, port); + jedis.connect(); + } + + @Override + public int size() { + return Math.toIntExact(jedis.hlen(redisKey)); + } + + @Override + public boolean isEmpty() { + return jedis.hlen(redisKey) == 0; + } + + @Override + public boolean containsKey(Object key) { + return jedis.hexists(redisKey, serialize(key)); + } + + private String serialize(Object obj) { + if (obj instanceof String) { + return obj.toString(); + } + + return gson.toJson(obj); + } + + private R deserialize(String v, Class clazz) { + if (clazz.equals(String.class)){ + return (R) v; + } + + return gson.fromJson(v, clazz); + } + + @Override + public boolean containsValue(Object value) { + if (value == null) { + return false; + } + + return jedis.hgetAll(redisKey).containsValue(serialize(value)); + } + + @Override + public V get(Object key) { + String jsonValue = jedis.hget(redisKey, serialize(key)); + return deserialize(jsonValue, valueClass); + } + + @Override + public V put(K key, V value) { + V previousVal = get(key); + jedis.hset(redisKey, serialize(key), serialize(value)); + return previousVal; + } + + @Override + public V remove(Object key) { + V value = get(key); + jedis.hdel(redisKey, serialize(key)); + return value; + } + + @Override + public void putAll(Map m) { + Map map = m.entrySet().stream() + .collect(Collectors.toMap(entry -> serialize(entry.getKey()), + entry -> serialize(entry.getValue()))); + jedis.hset(redisKey, map); + } + + @Override + public void clear() { + jedis.del(redisKey); + } + + @Override + public Set keySet() { + return jedis.hgetAll(redisKey) + .keySet().stream() + .map(elem -> deserialize(elem, keyClass)) + .collect(Collectors.toSet()); + } + + @Override + public Collection values() { + return jedis.hgetAll(redisKey) + .values().stream() + .map(value -> deserialize(value, valueClass)) + .toList(); + } + + @Override + public Set> entrySet() { + return jedis.hgetAll(redisKey) + .entrySet().stream() + .collect( + Collectors.toMap( + entry -> deserialize(entry.getKey(), keyClass), + entry -> deserialize(entry.getValue(), valueClass))) + .entrySet(); + } +} From 22ad237593bfa6b9fec5ed2165fc9372b55f259b Mon Sep 17 00:00:00 2001 From: ysneve Date: Mon, 27 Jul 2026 16:10:39 +0500 Subject: [PATCH 2/2] Add test for RedisMap class --- pom.xml | 12 + .../java/org/redis/model/RedisMapTest.java | 283 ++++++++++++++++++ src/test/java/org/redis/model/TestUser.java | 16 + 3 files changed, 311 insertions(+) create mode 100644 src/test/java/org/redis/model/RedisMapTest.java create mode 100644 src/test/java/org/redis/model/TestUser.java diff --git a/pom.xml b/pom.xml index f822aac..0dfd304 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,18 @@ jedis 5.1.5 + + org.projectlombok + lombok + 1.18.36 + provided + + + org.testcontainers + testcontainers + 1.20.0 + test + org.junit.jupiter junit-jupiter diff --git a/src/test/java/org/redis/model/RedisMapTest.java b/src/test/java/org/redis/model/RedisMapTest.java new file mode 100644 index 0000000..4edefec --- /dev/null +++ b/src/test/java/org/redis/model/RedisMapTest.java @@ -0,0 +1,283 @@ +package org.redis.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import redis.clients.jedis.Jedis; + +class RedisMapTest { + + private static Jedis jedis; + private Map map; + private TestUser user1 = new TestUser("Name", "LastName", 21); + private TestUser user2 = new TestUser("John", "Doe", 66); + private TestUser user3 = new TestUser("Matvey", "Testoviy", 18); + + @BeforeAll + public static void setup() { + jedis = new Jedis("localhost", 6379); + jedis.connect(); + } + + @AfterAll + public static void shutDown() { + if (jedis != null) { + jedis.close(); + } + } + + @BeforeEach + void setupJedis() { + jedis.del("test:users"); + map = new RedisMap<>(jedis, "test:users", String.class, TestUser.class); + } + + @Test + void size_shouldReturnCorrectSize_When_AddingValues() { + assertEquals(0, map.size()); + + map.put("1", user1); + map.put("2", user2); + map.put("3", user3); + } + + @Test + void size_shouldReturnCorrectSize_When_RemovingValues() { + map.put("1", user1); + map.put("2", user2); + map.put("3", user3); + + assertEquals(3, map.size()); + + map.remove("3"); + + assertEquals(2, map.size()); + + map.clear(); + + assertEquals(0, map.size()); + } + + @Test + void isEmpty_ShouldReturnFalse_When_NotEmpty() { + + map.put("1", user1); + assertFalse(map.isEmpty()); + + map.put("2", user2); + assertFalse(map.isEmpty()); + + map.remove("1"); + assertFalse(map.isEmpty()); + } + + @Test + void isEmpty_ShouldReturnTrue_When_Empty() { + assertTrue(map.isEmpty()); + + map.put("1", user1); + map.put("2", user2); + map.remove("1"); + + map.clear(); + assertTrue(map.isEmpty()); + + } + + @Test + void containsKey() { + assertFalse(map.containsKey("1")); + + map.put("1", user1); + assertTrue(map.containsKey("1")); + assertFalse(map.containsKey("2")); + + map.put("2", user2); + map.remove("1"); + assertFalse(map.containsKey("1")); + assertTrue(map.containsKey("2")); + + map.clear(); + assertFalse(map.containsKey("1")); + assertFalse(map.containsKey("2")); + + } + + @Test + void containsValue_ShouldReturnTrue_After_ValuesAdded() { + + map.put("1", user1); + assertTrue(map.containsValue(user1)); + + map.put("2", user2); + assertTrue(map.containsValue(user2)); + assertTrue(map.containsValue(user1)); + } + + @Test + void containsValue_ShouldReturnFalse_After_ValuesRemoved() { + + map.put("1", user1); + + map.put("2", user2); + + map.remove("1"); + assertFalse(map.containsValue(user1)); + assertTrue(map.containsValue(user2)); + + map.clear(); + assertFalse(map.containsValue(user1)); + assertFalse(map.containsValue(user2)); + } + + @Test + void get_ShouldReturnNull_When_ValuesNotExist() { + assertNull(map.get("1")); + + map.put("1", user1); + map.put("2", user2); + + map.remove("1"); + assertNull(map.get("1")); + + map.clear(); + assertNull(map.get("1")); + assertNull(map.get("2")); + + } + @Test + void get_ShouldReturnValue_When_ValueExist() { + + map.put("1", user1); + assertEquals(user1, map.get("1")); + + map.put("2", user2); + assertEquals(user1, map.get("1")); + assertEquals(user2, map.get("2")); + } + + @Test + void put_ShouldContainNewValueAndReturnNull_When_AddingNonExistentKey() { + assertNull(map.put("1", user1)); + + assertNull(map.put("2", user3)); + + } + + @Test + void put_ShouldContainNewValueAndReturnOldValue_When_OverridingKey() { + + map.put("1", user1); + assertEquals(user1, map.put("1", user2)); + + assertTrue(map.containsValue(user2)); + assertFalse(map.containsValue(user1)); + assertTrue(map.containsKey("1")); + + map.put("2", user3); + assertEquals(user3, map.get("2")); + assertEquals(user3, map.put("2", user1)); + } + + @Test + void remove() { + assertNull(map.remove("1")); + + assertEquals(0, map.size()); + + map.put("2", user2); + assertEquals(1, map.size()); + assertNull(map.remove("1")); + assertEquals(user2, map.remove("2")); + assertEquals(0, map.size()); + + } + + @Test + void putAll() { + assertTrue(map.isEmpty()); + + map.putAll(Map.of("1", user1, "2", user2, "3", user3)); + assertFalse(map.isEmpty()); + assertEquals(3, map.size()); + + map.putAll(Map.of("4", user1, "3", user2, "5", user3)); + assertEquals(5, map.size()); + + } + + @Test + void clear() { + assertTrue(map.isEmpty()); + map.put("1", user1); + map.put("2", user2); + map.putAll(Map.of("3", user1, "4", user2, "5", user3)); + assertFalse(map.isEmpty()); + + map.clear(); + assertTrue(map.isEmpty()); + } + + @Test + void keySet() { + assertEquals(Collections.emptySet(), map.keySet()); + + map.putAll(Map.of("1", user1, "2", user2, "3", user3)); + assertEquals(3, map.keySet().size()); + assertEquals(Set.of("1", "2", "3"), map.keySet()); + + map.put("4", user2); + map.put("5", user1); + assertEquals(5, map.keySet().size()); + assertEquals(Set.of("1", "2", "3", "4", "5"), map.keySet()); + + map.clear(); + assertEquals(Collections.emptySet(), map.keySet()); + } + + @Test + void values() { + assertEquals(Collections.emptyList(), map.values()); + map.putAll(Map.of("1", user1, "2", user2, "3", user3)); + + assertTrue( map.values().containsAll(List.of(user1, user2, user3))); + + map.put("4", user2); + map.put("5", user1); + assertTrue( map.values().containsAll(List.of(user1, user2, user3, user2, user1))); + + map.clear(); + assertEquals(Collections.emptyList(), map.values()); + + } + + @Test + void entrySet() { + assertEquals(Collections.emptySet(), map.entrySet()); + + Map testMap = new java.util.HashMap<>( + Map.of("1", user1, "2", user2, "3", user3)); + map.putAll(testMap); + assertEquals(testMap.entrySet(), map.entrySet()); + + map.put("4", user3); + map.put("5", user1); + testMap.put("4", user3); + testMap.put("5", user1); + + assertEquals(testMap.entrySet(), map.entrySet()); + + map.clear(); + assertEquals(Collections.emptySet(), map.entrySet()); + } +} \ No newline at end of file diff --git a/src/test/java/org/redis/model/TestUser.java b/src/test/java/org/redis/model/TestUser.java new file mode 100644 index 0000000..4424996 --- /dev/null +++ b/src/test/java/org/redis/model/TestUser.java @@ -0,0 +1,16 @@ +package org.redis.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TestUser { + + private String name; + private String lastName; + private int years; + +}