From 96960dcaecb8bd8aada4ae923a8f977254708b37 Mon Sep 17 00:00:00 2001 From: Daniil Khanukov Date: Fri, 17 Oct 2025 01:47:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20Map?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D1=81=20Redis=20(+Jedis).=20=D0=9D=D0=B0?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=B0=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/redis/RedisMap.java | 163 ++++++++++++++++++++++ src/test/java/org/redis/RedisMapTest.java | 111 +++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 src/main/java/org/redis/RedisMap.java create mode 100644 src/test/java/org/redis/RedisMapTest.java diff --git a/src/main/java/org/redis/RedisMap.java b/src/main/java/org/redis/RedisMap.java new file mode 100644 index 0000000..9ddecd7 --- /dev/null +++ b/src/main/java/org/redis/RedisMap.java @@ -0,0 +1,163 @@ +package org.redis; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +import java.util.*; + +public class RedisMap implements Map, AutoCloseable { + + private final JedisPool pool; + private final String redisKey; + + public RedisMap(JedisPool pool, String redisKey) { + Objects.requireNonNull(pool, "JedisPool не должен быть null"); + Objects.requireNonNull(redisKey, "redisKey не должен быть null"); + this.pool = pool; + this.redisKey = redisKey; + } + + private void requireNonNullKey(Object key) { + if (key == null) { + throw new NullPointerException("null ключи не поддерживаются"); + } + } + + private void requireNonNullValue(Object value) { + if (value == null) { + throw new NullPointerException("null значения не поддерживаются"); + } + } + + @Override + public void close() { + pool.close(); + } + + @Override + public int size() { + try (Jedis jedis = pool.getResource()) { + Long l = jedis.hlen(redisKey); + return l.intValue(); + } + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object key) { + requireNonNullKey(key); + try (Jedis jedis = pool.getResource()) { + return jedis.hexists(redisKey, key.toString()); + } + } + + @Override + public boolean containsValue(Object value) { + requireNonNullValue(value); + try (Jedis jedis = pool.getResource()) { + List values = jedis.hvals(redisKey); + return values.contains(value.toString()); + } + } + + @Override + public String get(Object key) { + requireNonNullKey(key); + try (Jedis jedis = pool.getResource()) { + return jedis.hget(redisKey, key.toString()); + } + } + + @Override + public String put(String key, String value) { + requireNonNullKey(key); + requireNonNullValue(value); + try (Jedis jedis = pool.getResource()) { + String previous = jedis.hget(redisKey, key.toString()); + jedis.hset(redisKey, key, value); + return previous; + } + } + + @Override + public String remove(Object key) { + requireNonNullKey(key); + try (Jedis jedis = pool.getResource()) { + String previous = jedis.hget(redisKey, key.toString()); + jedis.hdel(redisKey, key.toString()); + return previous; + } + } + + @Override + public void putAll(Map m) { + Objects.requireNonNull(m, "map не должна быть null"); + if (m.isEmpty()) return; + Map toSet = new HashMap<>(); + for (Entry entry : m.entrySet()) { + if (entry.getKey() == null) throw new NullPointerException("null ключи не поддерживаются"); + if (entry.getValue() == null) throw new NullPointerException("null значения не поддерживаются"); + toSet.put(entry.getKey(), entry.getValue()); + } + try (Jedis jedis = pool.getResource()) { + jedis.hmset(redisKey, toSet); + } + + } + + @Override + public void clear() { + try (Jedis jedis = pool.getResource()) { + jedis.del(redisKey); + } + } + + @Override + public Set keySet() { + try (Jedis jedis = pool.getResource()) { + return new HashSet<>(jedis.hkeys(redisKey)); + } + } + + @Override + public Collection values() { + try (Jedis jedis = pool.getResource()) { + return new ArrayList<>(jedis.hvals(redisKey)); + } + } + + @Override + public Set> entrySet() { + try (Jedis jedis = pool.getResource()) { + Map all = jedis.hgetAll(redisKey); + Set> entries = new HashSet<>(); + for (Map.Entry entry : all.entrySet()) { + entries.add(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue())); + } + return entries; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Map)) return false; + Map other = (Map) o; + try (Jedis jedis = pool.getResource()) { + Map all = jedis.hgetAll(redisKey); + return all.equals(other); + } + } + + @Override + public int hashCode() { + try (Jedis j = pool.getResource()) { + Map all = j.hgetAll(redisKey); + return all.hashCode(); + } + } +} diff --git a/src/test/java/org/redis/RedisMapTest.java b/src/test/java/org/redis/RedisMapTest.java new file mode 100644 index 0000000..812ee87 --- /dev/null +++ b/src/test/java/org/redis/RedisMapTest.java @@ -0,0 +1,111 @@ +package org.redis; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import redis.clients.jedis.JedisPool; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +class RedisMapTest { + private JedisPool pool; + private RedisMap map; + private final String KEY = "test-map"; + + @BeforeEach + void setup() { + pool = new JedisPool("localhost", 6379); + map = new RedisMap(pool, KEY); + map.clear(); + } + + @AfterEach + void teardown() { + map.clear(); + map.close(); + } + + @Test + void testPutGetSizeIsEmpty() { + assertTrue(map.isEmpty()); + assertEquals(0, map.size()); + + assertNull(map.put("a", "1")); + assertEquals("1", map.get("a")); + assertFalse(map.isEmpty()); + assertEquals(1, map.size()); + + assertEquals("1", map.put("a", "2")); + assertEquals("2", map.get("a")); + assertEquals(1, map.size()); + } + + @Test + void testContainsKeyAndValue() { + map.put("k1", "v1"); + map.put("k2", "v2"); + assertTrue(map.containsKey("k1")); + assertTrue(map.containsValue("v2")); + assertFalse(map.containsKey("no")); + } + + @Test + void testRemove() { + map.put("x", "y"); + assertEquals("y", map.remove("x")); + assertNull(map.get("x")); + assertFalse(map.containsKey("x")); + assertFalse(map.containsValue("y")); + } + + @Test + void testPutAllAndClear() { + Map src = new HashMap<>(); + src.put("a", "1"); + src.put("b", "2"); + map.putAll(src); + assertEquals(2, map.size()); + map.clear(); + assertTrue(map.isEmpty()); + } + + @Test + void testKeySetValuesEntrySet() { + map.put("k1", "v1"); + map.put("k2", "v2"); + + Set keys = map.keySet(); + assertEquals(new HashSet<>(Arrays.asList("k1", "k2")), keys); + + Collection vals = map.values(); + assertTrue(vals.containsAll(Arrays.asList("v1", "v2"))); + + Set> entries = map.entrySet(); + assertEquals(2, entries.size()); + } + + @Test + void testEqualsHashCode() { + map.put("a", "1"); + map.put("b", "2"); + + Map other = new HashMap<>(); + other.put("a", "1"); + other.put("b", "2"); + + assertEquals(other, map); + assertEquals(other.hashCode(), map.hashCode()); + } + + @Test + void testNullKeyOrValueThrows() { + assertThrows(NullPointerException.class, () -> map.put(null, "v")); + assertThrows(NullPointerException.class, () -> map.put("k", null)); + Map m = new HashMap<>(); + m.put("k1", null); + assertThrows(NullPointerException.class, () -> map.putAll(m)); + } + +}