← Назад к вопросам

Может ли поток в synchronized методе войти еще раз в этот же метод?

2.0 Middle🔥 141 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Примитив как ключ в Map

Краткий ответ: Нет, примитивы НЕ могут быть ключами в HashMap/TreeMap, но их обёрнутые версии (Integer, Double, Long) могут и часто используются.

Почему примитивы не могут быть ключами

Причина: HashMap и TreeMap требуют методов hashCode() и equals(), которые есть только у объектов. Примитивы не наследуют эти методы.

// ❌ Синтаксическая ошибка: примитивы не допускаются
Map<int, String> map1 = new HashMap<>();     // ❌ Ошибка компиляции
Map<double, String> map2 = new HashMap<>();  // ❌ Ошибка компиляции
Map<boolean, String> map3 = new HashMap<>(); // ❌ Ошибка компиляции

// ✓ Правильно: используем обёрнутые типы
Map<Integer, String> map1 = new HashMap<>();    // ✓ OK
Map<Double, String> map2 = new HashMap<>();     // ✓ OK
Map<Boolean, String> map3 = new HashMap<>();    // ✓ OK

Использование обёрнутых примитивов

public class WrapperTypesAsKeys {
    public static void main(String[] args) {
        // Integer как ключ
        Map<Integer, String> intMap = new HashMap<>();
        intMap.put(1, "One");
        intMap.put(2, "Two");
        intMap.put(3, "Three");
        System.out.println(intMap);  // {1=One, 2=Two, 3=Three}
        
        // Double как ключ
        Map<Double, String> doubleMap = new HashMap<>();
        doubleMap.put(1.5, "One-point-five");
        doubleMap.put(2.7, "Two-point-seven");
        System.out.println(doubleMap);
        
        // Boolean как ключ (максимум 2 элемента!)
        Map<Boolean, String> boolMap = new HashMap<>();
        boolMap.put(true, "Yes");
        boolMap.put(false, "No");
        System.out.println(boolMap);
    }
}

Почему обёрнутые типы работают

// Integer имеет методы hashCode() и equals()
public class IntegerExample {
    public static void main(String[] args) {
        Integer a = 42;
        Integer b = 42;
        Integer c = new Integer(42);
        
        System.out.println(a.equals(b));         // true
        System.out.println(a.hashCode());        // 42
        System.out.println(b.hashCode());        // 42
        System.out.println(a.hashCode() == c.hashCode());  // true
        
        // Поэтому они работают как ключи
        Map<Integer, String> map = new HashMap<>();
        map.put(a, "First");
        map.put(b, "Second");  // Перезаписывает значение для того же ключа
        System.out.println(map.get(c));  // "Second"
    }
}

Autoboxing/Unboxing

public class AutoboxingExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        
        // ✓ Autoboxing: примитив автоматически оборачивается
        map.put(1, "One");     // int → Integer
        map.put(2, "Two");
        
        // ✓ Unboxing: Integer автоматически разворачивается
        Integer key = 1;
        String value = map.get(key);
        System.out.println(value);  // "One"
    }
}

Сравнение типов

public class TypeComparison {
    public static void main(String[] args) {
        // ❌ int: примитив
        int primitive = 5;
        System.out.println(primitive.hashCode());  // ❌ Ошибка: int не имеет методов
        
        // ✓ Integer: объект (обёртка)
        Integer wrapped = 5;
        System.out.println(wrapped.hashCode());    // ✓ OK: 5
        
        // Они эквивалентны в коллекциях
        Map<Integer, String> map = new HashMap<>();
        map.put(5, "Five");
        System.out.println(map.get(wrapped));      // "Five"
        System.out.println(map.get(5));            // "Five" (autoboxing)
    }
}

Осторожность с Double/Float

public class DoubleKeyWarning {
    public static void main(String[] args) {
        Map<Double, String> map = new HashMap<>();
        
        // ⚠️ Осторожность: floating-point ошибки
        double d1 = 0.1 + 0.2;  // 0.30000000000000004
        double d2 = 0.3;        // 0.3
        
        map.put(d1, "Sum");
        System.out.println(map.get(d2));  // null (не найдено!)
        
        // Лучше использовать BigDecimal
        Map<BigDecimal, String> bdMap = new HashMap<>();
        bdMap.put(BigDecimal.valueOf(0.1).add(BigDecimal.valueOf(0.2)), "Sum");
        bdMap.put(BigDecimal.valueOf(0.3), "Direct");
    }
}

Практические примеры

public class PracticalExamples {
    
    // ✓ Хорошо: Integer как ключ (стабильный hashCode)
    public Map<Integer, User> getUsersById(List<User> users) {
        return users.stream()
            .collect(Collectors.toMap(User::getId, Function.identity()));
    }
    
    // ✓ Хорошо: String как ключ
    public Map<String, Integer> getWordFrequency(List<String> words) {
        return words.stream()
            .collect(Collectors.groupingBy(
                Function.identity(),
                Collectors.summingInt(w -> 1)
            ));
    }
    
    // ⚠️ Осторожность: Boolean (максимум 2 ключа)
    public Map<Boolean, Integer> getCountByStatus(List<Data> data) {
        return data.stream()
            .collect(Collectors.groupingBy(
                Data::isActive,
                Collectors.summingInt(d -> 1)
            ));
    }
    
    // ❌ Избегай: Double из-за точности
    // Используй BigDecimal вместо Double
    public Map<BigDecimal, Price> getPrices() {
        Map<BigDecimal, Price> map = new HashMap<>();
        return map;
    }
}

Сравнение примитивов и обёрток

ТипПримитивОбёрткаКак ключ
целое числоintInteger✓ Integer
веществоdoubleDouble⚠️ осторожно Double
логическийbooleanBoolean✓ Boolean (макс 2)
символcharCharacter✓ Character

Вывод: Примитивы не могут быть ключами напрямую. Используй обёрнутые типы (Integer, Double, Boolean и т.д.). Осторожнее с Double/Float из-за точности вычислений.

Может ли поток в synchronized методе войти еще раз в этот же метод? | PrepBro