2010-11-15 1 views
0

Я хочу иметь один DateWrapper - представляющий дату (построенный для Hibernate persistance, но это уже другая история) - в большинстве случаев в то же время на ту же дату.Конфликты HashMap: мой код правильный?

Я немного смущен о столкновениях и хороших ключах для хеширования. Я пишу фабрику для объекта DateWrapper, и я думал использовать миллисекунды разбора даты в качестве ключа, как я видел других. Но что происходит, если есть столкновение?. Миллисекунды всегда отличаются друг от друга, но внутренняя таблица может быть меньше, чем Long, которая может существовать. И как только хэш-карта имеет столкновение, она использует равные числа, но как она может отличить два разных объекта от моего Лонга? Может быть, это метод put, чтобы удалить (переписать) какое-то значение, которое я хотел бы вставить ... Итак, этот код безопасен или он прослушивается ??

package myproject.test; 

import java.util.HashMap; 
import java.util.Map; 

import org.joda.time.DateTime; 
import org.joda.time.format.DateTimeFormat; 
import org.joda.time.format.DateTimeFormatter; 

import myproject.utilities.DateWrapper; 

public class DateWrapperFactory { 

    static Map <Long, DateWrapper> cache = new HashMap<Long, DateWrapper>(); 
    static DateTimeFormatter parser = 
     DateTimeFormat.forPattern("yyyy-MM-dd"); 

    static DateWrapperFactory instance = new DateWrapperFactory(); 

    private DateWrapperFactory() { 
    } 

    public static DateWrapperFactory getInstance() { 
     return instance; 
    } 


    public static DateWrapper get(String source) { 
     DateTime d = parser.parseDateTime(source); 
     DateWrapper dw = cache.get(d.getMillis()); 
     if (dw != null) { 
      return dw; 
     } else { 
      dw = new DateWrapper(d); 
      cache.put(d.getMillis(), dw); 
      return dw; 
     } 
    } 

} 

package myproject.test; 

import org.joda.time.DateTime; 

public class DateWrapper { 

    private DateTime date; 

    public DateWrapper(DateTime dt) { 
     this.date = dt; 
    } 

} 
+0

Разве это не эквивалентно набору с использованием DateTime.equals через getMillis()? – extraneon

+1

@extraneon: Это не эквивалентно.Идея здесь (я думаю) заключается в том, чтобы реализовать один канонический/интернированный экземпляр для каждого используемого «DateTime», который требует возможности получить этот экземпляр без итерации ... для этого требуется карта. – ColinD

ответ

0

Если вы используете реальные объекты, которые вы хотите в качестве ключей карты и позволяя заботиться HashMap тейк о деталях того, что она делает с хэш-код этих объектов (и ключи реализации equals и hashCode в соответствии с их контрактом) там будет не проблема, если есть столкновение hashcode, отличное от некоторого возможного снижения производительности из-за необходимости линейно искать каждую запись, которая хэшируется в том же ведре.

Проблема в другом вопросе, в котором возник вопрос о столкновениях, заключалась в том, что вместо использования фактического объекта, который должен был быть ключом, вы использовали хэш-код этого объекта в качестве самого ключа. Это было неверно и привело бы к неправильному поведению ... когда вы пошли посмотреть значение для данного ключа на карте, результатом могло быть значение, которое фактически отображает совершенно другой ключ, который просто имеет тот же хэш-код.

Мораль этой истории такова: использовать фактический ключ или что-то, что, безусловно, эквивалентно (подобно Millis в DateTime в данном случае) в качестве ключа, а не Hashcode этого ключа. HashMap делает то, что нужно, с хэш-кодом для вас.

+0

Хорошо, спасибо, Колин. Я знал это, но путаница исходила от использования ключа, который не был уникальным для объектов в коде, на который вы ссылаетесь (для других читателей см. Http://stackoverflow.com/questions/4179641/caching-objects-built -с-множественные-параметры). Итак, его довольно просто: ключ, используемый в хэшмапе, должен соответствовать одному и только одному объекту, например первичному ключу в РСУБД – cdarwin

0

С помощью HashMap вы можете хранить только одну запись под любым заданным значением ключа (например, длиной в вашем случае).

На стороне примечания, если есть вероятность параллелизма, вы можете использовать ConcurrentHashMap и putIfAbsent() вместо неатомных вызовов get/if/put.

+1

извините, но равно, что использование карты является одним из ключей, не так ли? Итак, я думаю, что это не эквивалент DateWrapper, который будет использоваться, но один из Long. – cdarwin

+0

Что я думал, вы правы, конечно. :) –

0

equals() будет называться длинными клавишами, а не значениями. Ты в порядке.

+0

спасибо. Итак, этот код глючит? – cdarwin

+0

На самом деле это зависит от того, что вы пытаетесь выполнить. Похоже, что структура данных, которую вы выбрали, НЕ собирается делать то, что вы хотите. Пожалуйста, обновите свой вопрос, чтобы указать, что вы пытаетесь сделать. –

+0

Выполнено (добавлено первое предложение). Я хотел бы иметь один DateWrapper в большинстве существующих одновременно с тем же значением DateTime – cdarwin

0

Учитывая то, что вы в конечном итоге пытаетесь достичь с этим, это не кажется слишком ужасно продуктивным. У вас есть высоко оптимизированная структура данных, специально разработанная для быстрого поиска и обеспечения уникальности, называемой индексом базы данных. И чрезвычайно надежное кэширование в памяти и L2 уже доступно для вас из Hibernate. Что, кстати, не имеет проблем с безопасностью потоков для размещения HashMap в статическом поле.

Почему бы не сделать этот номер значением столбца ID в вашей базе данных и позволить надежным технологиям платформы заботиться о том, чтобы быстро найти его и кэшировать его для вас? Кэш-память L2 в памяти на самом деле не намного медленнее, чем HashMap в великой схеме вещей. Это было бы довольно редкое приложение, где разница была бы одной из ваших значимых горячих точек.