diff --git a/.idea/misc.xml b/.idea/misc.xml
index 8dc554d..82ab2cc 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 5c936ee..b1719c8 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,80 @@
-# Практическое задание 2 # Redis Map
+# RedisMap
-В рамках практической задачи нужно реализовать прослойку между Redis и интерфейсом Map
-Достаточно будет реализации Map
+Простая реализация интерфейса `Map` с использованием Redis через Jedis клиент.
-**Описание:**
-✅ Написать реализацию методов Map, которая будет работать с Redis
-✅ Работать с redis можно через Jedis
-✅ Покрыть все методы тестами
-✅ Для поднятия redis, в корне проекта есть docker-compose файл
+## Цель проекта
-Redis поднимается командой
-```
+Показать, как можно создать прослойку между Redis и стандартным Java интерфейсом Map.
+
+## Быстрый старт
+
+### Предварительные требования
+- Java 17 или выше
+- Maven 3.6+
+- Docker Desktop
+
+### 1. Запуск Redis
+```bash
+# Запуск Redis в Docker
docker-compose up -d
+
+# Проверка статуса
+docker ps
+
+# Остановка Redis
+docker-compose down
+```
+
+### 2. Сборка проекта
+```bash
+# Очистка и компиляция
+mvn clean compile
+
+# Сборка JAR файла
+mvn clean package
+```
+
+### 3. Запуск примера
+```bash
+# через JAR файл
+java -cp target/classes org.redis.Main
+```
+
+### 4. Запуск тестов
+```bash
+# Все тесты
+mvn test
```
-**Как выполнить:**
-1. Сделать форк этого проекта
-2. Сделать ветку dev в своем проекте. Выполнять работу в ней
-3. После выполнения работы открыть Pull Request в master
-4. Назначить ментора ревьювером
-5. Отправить ссылку на PR в тг чат ментору
\ No newline at end of file
+## Что делает проект
+
+`RedisMap` - это класс, который реализует интерфейс `Map` и использует Redis для хранения данных.
+
+### Основные возможности:
+- **Полная совместимость** с `Map`
+- **Namespace изоляция** - разные экземпляры не пересекаются
+- **Простота использования** - работает как обычная Map
+- **Автоматическое управление ресурсами** - try-with-resources
+
+### Архитектура:
+- **RedisMap**: Реализует Map
+- **Jedis**: Java клиент для Redis
+- **Redis Hash**: Структура данных для хранения key-value пар
+- **Namespace**: Префикс для изоляции данных
+
+## Тестирование
+
+Проект включает простые тесты, которые проверяют:
+- **Базовые операции Map** (put, get, remove, clear)
+- **Методы коллекций** (keySet, values, entrySet)
+- **Обработку edge cases** (null ключи, неверные типы)
+- **Изоляцию namespace** между разными экземплярами
+- **Метод putAll** для массового добавления
+
+
+### Описание файлов:
+- **`RedisMap.java`**: Реализация интерфейса Map
+- **`Main.java`**: Демонстрация возможностей RedisMap
+- **`RedisMapTest.java`**: JUnit тесты для проверки функциональности
+- **`pom.xml`**: Зависимости (Jedis, JUnit) и плагины Maven
+- **`docker-compose.yaml`**: Конфигурация Redis контейнера
diff --git a/pom.xml b/pom.xml
index b4c0aac..2fd0d02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,7 @@
jedis
5.1.5
+
org.junit.jupiter
junit-jupiter
@@ -28,4 +29,23 @@
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+ org.redis.Main
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/org/redis/Main.java b/src/main/java/org/redis/Main.java
index 68b3db6..48d3813 100644
--- a/src/main/java/org/redis/Main.java
+++ b/src/main/java/org/redis/Main.java
@@ -1,7 +1,74 @@
package org.redis;
+import redis.clients.jedis.Jedis;
+import java.util.Map;
+
+/**
+ * Простой пример с демонстрацией основных возможностей использования RedisMap.
+ */
public class Main {
public static void main(String[] args) {
- System.out.println("Hello world!");
+
+ try (Jedis jedis = new Jedis("localhost", 6379)) {
+ System.out.println(" Redis connection: " + jedis.ping());
+
+ RedisMap redisMap = new RedisMap(jedis, "example:map");
+
+ System.out.println("Empty map size: " + redisMap.size());
+ System.out.println("Map is empty: " + redisMap.isEmpty());
+
+ System.out.println("\nAdding elements...");
+ redisMap.put("name", "John");
+ redisMap.put("age", "25");
+ redisMap.put("city", "Moscow");
+
+ System.out.println("Size after adding: " + redisMap.size());
+
+ System.out.println("\nGetting elements:");
+ System.out.println("name: " + redisMap.get("name"));
+ System.out.println("age: " + redisMap.get("age"));
+ System.out.println("city: " + redisMap.get("city"));
+
+ System.out.println("\nChecking existence:");
+ System.out.println("Contains key 'name': " + redisMap.containsKey("name"));
+ System.out.println("Contains key 'email': " + redisMap.containsKey("email"));
+
+ System.out.println("\nUpdating element:");
+ String oldAge = redisMap.put("age", "26");
+ System.out.println("Old age: " + oldAge);
+ System.out.println("New age: " + redisMap.get("age"));
+
+ System.out.println("\nAll keys: " + redisMap.keySet());
+ System.out.println("All values: " + redisMap.values());
+
+ System.out.println("\nRemoving element:");
+ String removedCity = redisMap.remove("city");
+ System.out.println("Removed city: " + removedCity);
+ System.out.println("Size after removal: " + redisMap.size());
+
+ System.out.println("\nUsing putAll:");
+ Map additionalData = Map.of(
+ "profession", "Programmer",
+ "experience", "3 years"
+ );
+ redisMap.putAll(additionalData);
+ System.out.println("Size after putAll: " + redisMap.size());
+
+ System.out.println("\nAll entries:");
+ for (Map.Entry entry : redisMap.entrySet()) {
+ System.out.println(" " + entry.getKey() + " = " + entry.getValue());
+ }
+
+ System.out.println("\nClearing map:");
+ redisMap.clear();
+ System.out.println("Size after clearing: " + redisMap.size());
+ System.out.println("Map is empty: " + redisMap.isEmpty());
+
+ System.out.println("\n Demonstration completed successfully!");
+
+ } catch (Exception e) {
+ System.err.println(" Error: " + e.getMessage());
+ e.printStackTrace();
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/org/redis/RedisMap.java b/src/main/java/org/redis/RedisMap.java
new file mode 100644
index 0000000..4522e5b
--- /dev/null
+++ b/src/main/java/org/redis/RedisMap.java
@@ -0,0 +1,122 @@
+package org.redis;
+
+import redis.clients.jedis.Jedis;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Простая реализация интерфейса Map с использованием Redis через Jedis.
+ */
+public class RedisMap implements Map {
+
+ private final Jedis jedis;
+ private final String namespace;
+
+ /**
+ * Создает новый экземпляр RedisMap.
+ */
+ public RedisMap(Jedis jedis, String namespace) {
+ if (jedis == null) {
+ throw new IllegalArgumentException("Jedis instance cannot be null");
+ }
+ if (namespace == null || namespace.trim().isEmpty()) {
+ throw new IllegalArgumentException("Namespace cannot be null or empty");
+ }
+
+ this.jedis = jedis;
+ this.namespace = namespace.trim();
+ }
+
+ @Override
+ public int size() {
+ long size = jedis.hlen(namespace);
+ return Math.toIntExact(size);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ if (!(key instanceof String)) {
+ return false;
+ }
+ return jedis.hexists(namespace, (String) key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ if (!(value instanceof String)) {
+ return false;
+ }
+ return jedis.hvals(namespace).contains(value);
+ }
+
+ @Override
+ public String get(Object key) {
+ if (!(key instanceof String)) {
+ return null;
+ }
+ return jedis.hget(namespace, (String) key);
+ }
+
+ @Override
+ public String put(String key, String value) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key cannot be null");
+ }
+
+ String old = jedis.hget(namespace, key);
+ jedis.hset(namespace, key, value);
+ return old;
+ }
+
+ @Override
+ public String remove(Object key) {
+ if (!(key instanceof String)) {
+ return null;
+ }
+
+ String old = jedis.hget(namespace, (String) key);
+ jedis.hdel(namespace, (String) key);
+ return old;
+ }
+
+ @Override
+ public void putAll(Map extends String, ? extends String> m) {
+ if (m != null && !m.isEmpty()) {
+ Map stringMap = new java.util.HashMap<>();
+ for (Map.Entry extends String, ? extends String> entry : m.entrySet()) {
+ if (entry.getKey() != null) {
+ stringMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ jedis.hset(namespace, stringMap);
+ }
+ }
+
+ @Override
+ public void clear() {
+ jedis.del(namespace);
+ }
+
+ @Override
+ public Set keySet() {
+ return jedis.hkeys(namespace);
+ }
+
+ @Override
+ public Collection values() {
+ return jedis.hvals(namespace);
+ }
+
+ @Override
+ public Set> entrySet() {
+ Map all = jedis.hgetAll(namespace);
+ return new HashSet<>(all.entrySet());
+ }
+}
diff --git a/src/test/java/org/redis/RedisMapTest.java b/src/test/java/org/redis/RedisMapTest.java
new file mode 100644
index 0000000..6e19dd9
--- /dev/null
+++ b/src/test/java/org/redis/RedisMapTest.java
@@ -0,0 +1,160 @@
+package org.redis;
+
+import org.junit.jupiter.api.*;
+import redis.clients.jedis.Jedis;
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Простые тесты RedisMap для учебных целей.
+ * Требует запущенный Redis на localhost:6379
+ */
+class RedisMapTest {
+
+ private static Jedis jedis;
+ private RedisMap redisMap;
+ private static final String TEST_NAMESPACE = "test:simple";
+
+ @BeforeAll
+ static void setUp() {
+ try {
+ jedis = new Jedis("localhost", 6379);
+ jedis.ping();
+ System.out.println(" Redis подключен успешно");
+ } catch (Exception e) {
+ System.out.println(" Redis недоступен: " + e.getMessage());
+ System.out.println("Запустите Redis: docker-compose up -d");
+ Assumptions.assumeTrue(false, "Redis недоступен");
+ }
+ }
+
+ @AfterAll
+ static void tearDown() {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+
+ @BeforeEach
+ void setUpEach() {
+ redisMap = new RedisMap(jedis, TEST_NAMESPACE);
+ jedis.del(TEST_NAMESPACE);
+ }
+
+ @AfterEach
+ void tearDownEach() {
+ jedis.del(TEST_NAMESPACE);
+ }
+
+ @Test
+ @DisplayName("Тест 1: Базовые операции Map")
+ void testBasicOperations() {
+ assertTrue(redisMap.isEmpty());
+ assertEquals(0, redisMap.size());
+
+ assertNull(redisMap.put("key1", "value1"));
+ assertNull(redisMap.put("key2", "value2"));
+
+ assertEquals(2, redisMap.size());
+ assertFalse(redisMap.isEmpty());
+
+ assertEquals("value1", redisMap.get("key1"));
+ assertEquals("value2", redisMap.get("key2"));
+
+ assertTrue(redisMap.containsKey("key1"));
+ assertFalse(redisMap.containsKey("nonexistent"));
+
+ assertEquals("value1", redisMap.put("key1", "newValue1"));
+ assertEquals("newValue1", redisMap.get("key1"));
+
+ assertEquals("newValue1", redisMap.remove("key1"));
+ assertFalse(redisMap.containsKey("key1"));
+ assertEquals(1, redisMap.size());
+
+ redisMap.clear();
+ assertTrue(redisMap.isEmpty());
+ assertEquals(0, redisMap.size());
+ }
+
+ @Test
+ @DisplayName("Тест 2: Методы коллекций")
+ void testCollectionMethods() {
+ redisMap.put("a", "1");
+ redisMap.put("b", "2");
+ redisMap.put("c", "3");
+
+ Set keys = redisMap.keySet();
+ assertEquals(3, keys.size());
+ assertTrue(keys.contains("a"));
+ assertTrue(keys.contains("b"));
+ assertTrue(keys.contains("c"));
+
+ Collection values = redisMap.values();
+ assertEquals(3, values.size());
+ assertTrue(values.contains("1"));
+ assertTrue(values.contains("2"));
+ assertTrue(values.contains("3"));
+
+ Set> entries = redisMap.entrySet();
+ assertEquals(3, entries.size());
+
+ boolean foundA = false, foundB = false, foundC = false;
+ for (Map.Entry entry : entries) {
+ if ("a".equals(entry.getKey()) && "1".equals(entry.getValue())) foundA = true;
+ if ("b".equals(entry.getKey()) && "2".equals(entry.getValue())) foundB = true;
+ if ("c".equals(entry.getKey()) && "3".equals(entry.getValue())) foundC = true;
+ }
+ assertTrue(foundA && foundB && foundC);
+ }
+
+ @Test
+ @DisplayName("Тест 3: putAll операция")
+ void testPutAll() {
+ Map data = new HashMap<>();
+ data.put("x", "10");
+ data.put("y", "20");
+ data.put("z", "30");
+
+ redisMap.putAll(data);
+
+ assertEquals(3, redisMap.size());
+ assertEquals("10", redisMap.get("x"));
+ assertEquals("20", redisMap.get("y"));
+ assertEquals("30", redisMap.get("z"));
+ }
+
+ @Test
+ @DisplayName("Тест 4: Обработка null и неверных типов")
+ void testEdgeCases() {
+ assertFalse(redisMap.containsKey(null));
+ assertNull(redisMap.get(null));
+ assertNull(redisMap.remove(null));
+
+ assertFalse(redisMap.containsKey(123));
+ assertNull(redisMap.get(123));
+ assertNull(redisMap.remove(123));
+
+ redisMap.put("emptyKey", "");
+ assertTrue(redisMap.containsKey("emptyKey"));
+ assertEquals("", redisMap.get("emptyKey"));
+ }
+
+ @Test
+ @DisplayName("Тест 5: Изоляция namespace")
+ void testNamespaceIsolation() {
+ RedisMap map1 = new RedisMap(jedis, "namespace1");
+ RedisMap map2 = new RedisMap(jedis, "namespace2");
+
+ map1.put("key", "value1");
+ map2.put("key", "value2");
+
+ assertEquals("value1", map1.get("key"));
+ assertEquals("value2", map2.get("key"));
+ assertEquals(1, map1.size());
+ assertEquals(1, map2.size());
+
+ jedis.del("namespace1");
+ jedis.del("namespace2");
+ }
+}