Кто-нибудь может объяснить это странное поведение Java HashMap? - PullRequest
0 голосов
/ 07 июля 2011

Я много раз использовал HashMap в Java, но никогда не сталкивался с таким поведением. У меня есть типы Item и ItemGroup . Они определены, как показано в следующих фрагментах кода.

public class Item {
    String id;
    float total;
}

public class ItemGroup {
    String keyword;
    int frequency;
    List<Item> items;
}

Итак ItemGroup состоит из 0 .. * предметов. Эти элементы имеют общее ключевое слово, и это ключевое слово появляется в системе с определенной частотой. Теперь самое интересное, у меня есть следующий метод, который дает список элементов, создает список групп.

public static ItemGroup[] createGroups(Item[] items){
    HashMap<String, ItemGroup> groups = new HashMap<String, ItemGroup>();
    String[] words;

    for (int i=0; i<items.length; i++){
        words = items[i].getId().split(REGEX);

        // Process keywords
        for (int j=0; j<words.length; j++){
            if (words[j].isEmpty()) break;

            ItemGroup group = groups.get(words[j]);
            if (group != null){
                group.incrementFrequency();
                group.getItems().add(items[i]);
            }else {
                group = EconomFactory.eINSTANCE.createItemGroup();
                group.setKeyword(words[j]);
                group.incrementFrequency();
                group.getItems().add(items[i]);
                groups.put(words[j], group);
            }
        }
    }

    return groups.values().toArray(new ItemGroup[0]);                       
}

Часть, где это становится странным, - при добавлении элемента в группу элементов (строка group.getItems (). Add (items [i]);). Во время перефразирования группа теряет свои предметы странным образом. Используя отладку, я вижу, что группа содержит элемент сразу после операции, но позже, например. при возврате значения метода все группы потеряли свои элементы.

Я пробовал это:

public static ItemGroup[] createGroups(Item[] items){
    HashMap<String, ItemGroup> groups = new HashMap<String, ItemGroup>();
    String[] words;

    for (int i=0; i<items.length; i++){
        words = items[i].getId().split(REGEX);

        // Create a new item based on the current one in the list
        Item item = EconomFactory.eINSTANCE.createItem();
        item.setId(items[i].getId());
        item.setTotal(items[i].getTotal());

        // Process key words
        for (int j=0; j<words.length; j++){
            if (words[j].isEmpty()) break;

            ItemGroup group = groups.get(words[j]);
            if (group != null){
                group.incrementFrequency();
                group.getItems().add(item);
            }else {
                group = EconomFactory.eINSTANCE.createItemGroup();
                group.setKeyword(words[j]);
                group.incrementFrequency();
                group.getItems().add(item);
                groups.put(words[j], group);
            }
        }
    }

    return groups.values().toArray(new ItemGroup[0]);                       
}   

но получил тот же результат. Следующее решение, однако, работает просто отлично.

public static ItemGroup[] createGroups(Item[] items){
    HashMap<String, ItemGroup> groups = new HashMap<String, ItemGroup>();
    String[] words;

    for (int i=0; i<items.length; i++){
        words = items[i].getId().split(REGEX);

        // Process key words
        for (int j=0; j<words.length; j++){
            if (words[j].isEmpty()) break;

            // Create a new item based on the current one in the list
            Item item = EconomFactory.eINSTANCE.createItem();
            item.setId(items[i].getId());
            item.setTotal(items[i].getTotal());

            ItemGroup group = groups.get(words[j]);
            if (group != null){
                group.incrementFrequency();
                group.getItems().add(item);
            }else {
                group = EconomFactory.eINSTANCE.createItemGroup();
                group.setKeyword(words[j]);
                group.incrementFrequency();
                group.getItems().add(item);
                groups.put(words[j], group);
            }
        }
    }

    return groups.values().toArray(new ItemGroup[0]);                       
}

Метод EconomFactory.eINSTANCE.createItemGroup () реализован следующим образом:

public ItemGroup createItemGroup() {
    ItemGroupImpl itemGroup = new ItemGroupImpl();
    return itemGroup;
}

где ItemGroupImpl является реализацией ItemGroup, т.е. это подклассы ItemGroup. Это потому, что я использую EMF (Eclipse Modeling Framework).

Может ли кто-нибудь объяснить это поведение (почему объекты ItemGroup теряют свои предметы)?

Вот коды для ItemGroup и ItemGroupImpl. Аналогичным образом выглядят коды для Item и ItemImpl.

public interface ItemGroup extends EObject {
String getKeyword();

void setKeyword(String value);

int getFrequency();

void setFrequency(int value);

EList<Item> getItems();

void incrementFrequency();
}

public class ItemGroupImpl extends EObjectImpl implements ItemGroup {

protected static final String KEYWORD_EDEFAULT = null;

protected String keyword = KEYWORD_EDEFAULT;

protected static final int FREQUENCY_EDEFAULT = 0;

protected int frequency = FREQUENCY_EDEFAULT;

protected EList<Item> items;

protected ItemGroupImpl() {
    super();
}

@Override
protected EClass eStaticClass() {
    return EconomPackage.Literals.ITEM_GROUP;
}

public String getKeyword() {
    return keyword;
}

public void setKeyword(String newKeyword) {
    String oldKeyword = keyword;
    keyword = newKeyword;
    if (eNotificationRequired())
        eNotify(new ENotificationImpl(this, Notification.SET,
            EconomPackage.ITEM_GROUP__KEYWORD, oldKeyword, keyword));
}

public int getFrequency() {
    return frequency;
}

public void setFrequency(int newFrequency) {
    int oldFrequency = frequency;
    frequency = newFrequency;
    if (eNotificationRequired())
        eNotify(new ENotificationImpl(this, Notification.SET, 
            EconomPackage.ITEM_GROUP__FREQUENCY, oldFrequency, frequency));
}

public EList<Item> getItems() {
    if (items == null) {
        items = new EObjectContainmentEList<Item>(Item.class, this, 
            EconomPackage.ITEM_GROUP__ITEMS);
    }
    return items;
}

public void incrementFrequency() {
    this.frequency = getFrequency() + 1;
}

@Override
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, 
            NotificationChain msgs) {
    switch (featureID) {
        case EconomPackage.ITEM_GROUP__ITEMS:
            return ((InternalEList<?>)getItems()).basicRemove(otherEnd,
             msgs);
    }
    return super.eInverseRemove(otherEnd, featureID, msgs);
}

@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
    switch (featureID) {
        case EconomPackage.ITEM_GROUP__KEYWORD:
            return getKeyword();
        case EconomPackage.ITEM_GROUP__FREQUENCY:
            return getFrequency();
        case EconomPackage.ITEM_GROUP__ITEMS:
            return getItems();
    }
    return super.eGet(featureID, resolve, coreType);
}

@SuppressWarnings("unchecked")
@Override
public void eSet(int featureID, Object newValue) {
    switch (featureID) {
        case EconomPackage.ITEM_GROUP__KEYWORD:
            setKeyword((String)newValue);
            return;
        case EconomPackage.ITEM_GROUP__FREQUENCY:
            setFrequency((Integer)newValue);
            return;
        case EconomPackage.ITEM_GROUP__ITEMS:
            getItems().clear();
            getItems().addAll((Collection<? extends Item>)newValue);
            return;
    }
    super.eSet(featureID, newValue);
}

@Override
public void eUnset(int featureID) {
    switch (featureID) {
        case EconomPackage.ITEM_GROUP__KEYWORD:
            setKeyword(KEYWORD_EDEFAULT);
            return;
        case EconomPackage.ITEM_GROUP__FREQUENCY:
            setFrequency(FREQUENCY_EDEFAULT);
            return;
        case EconomPackage.ITEM_GROUP__ITEMS:
            getItems().clear();
            return;
    }
    super.eUnset(featureID);
}

@Override
public boolean eIsSet(int featureID) {
    switch (featureID) {
        case EconomPackage.ITEM_GROUP__KEYWORD:
            return KEYWORD_EDEFAULT == null ? keyword != null : 
            !KEYWORD_EDEFAULT.equals(keyword);
        case EconomPackage.ITEM_GROUP__FREQUENCY:
            return frequency != FREQUENCY_EDEFAULT;
        case EconomPackage.ITEM_GROUP__ITEMS:
            return items != null && !items.isEmpty();
    }
    return super.eIsSet(featureID);
}

@Override
public String toString() {
    if (eIsProxy()) return super.toString();

    StringBuffer result = new StringBuffer();
    result.append("(keyword: ");
    result.append(keyword);
    result.append(", frequency: ");
    result.append(frequency);
    result.append(')');
    return result.toString();
}
}

Ответы [ 2 ]

0 голосов
/ 07 июля 2011

Расскажите, пожалуйста, что происходит в EconomFactory.eINSTANCE.createItemGroup ()?Я не могу сказать, создается ли каждый раз уникальный экземпляр с уникальным списком элементов.

Я опробовал его с некоторыми изменениями (в настоящее время я не использую Java 6, поэтому не могу использовать String.isEmpty) и предполагаю, что регулярносоздание объекта, и это работает для меня.См. Ниже мой исполняемый пример (что-то вроде того, что вы должны опубликовать в следующий раз, когда задаете вопрос):

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ItemGrouper {

    public static class Item {
        String id;
        float total;

        Item(String id) {
            this.id = id;
        }

        public String getId() {
            return id;
        }
    }

    public static class ItemGroup {
        String keyword;
        int frequency;
        List<Item> items = new ArrayList<ItemGrouper.Item>();

        public void incrementFrequency() {
            frequency++;
        }

        public List<Item> getItems() {
            return items;
        }

        public void setKeyword(String string) {
            keyword = string;
        }

        @Override
        public String toString() {
            return String.format("key=%s freq=%s", keyword, frequency);
        }
    }

    public static ItemGroup[] createGroups(Item[] items) {

        Map<String, ItemGroup> groups = new HashMap<String, ItemGroup>();
        String[] words;
        for (int i = 0; i < items.length; i++) {
            words = items[i].getId().split(" ");

            // Process keywords
            for (int j = 0; j < words.length; j++) {
                if (words[j].length() == 0) {
                    break;
                }

                ItemGroup group = groups.get(words[j]);
                if (group == null) {
                    group = new ItemGroup();
                    group.setKeyword(words[j]);
                    groups.put(words[j], group);
                }
                group.incrementFrequency();
                group.getItems().add(items[i]);
            }
        }
        return groups.values().toArray(new ItemGroup[0]);
    }

    public static void main(String[] args) {
        Item[] items = new Item[] {new Item("one two"), new Item("two three")};
        ItemGroup[] itemgroups = createGroups(items);
        System.out.println(Arrays.toString(itemgroups));
    }
}

Вывод:

[key=one freq=1, key=two freq=2, key=three freq=1]
0 голосов
/ 07 июля 2011

Во втором наборе кода, где я инициализирован?

 public static ItemGroup[] createGroups(Item[] items){    
  HashMap<String, ItemGroup> groups = new HashMap<String, ItemGroup>(); 
    String[] words; 
     // Create a new item based on the current one in the list
     Item item = EconomFactory.eINSTANCE.createItem();
     item.setId(items[i].getId());
     item.setTotal(items[i].getTotal()); 

Переменная i не была инициализирована в этом контексте, поэтому она может иметь любое значение (так как она не взрывается, я полагаю,у вас есть глобальное «i», которое еще есть в вашем коде, и вы запускаете свой метод с этим значением, равным 0.

В вашем работающем коде я инициализировался в методе перед тем, как получить к нему доступ:

   public static ItemGroup[] createGroups(Item[] items){
     HashMap<String, ItemGroup> groups = new HashMap<String, ItemGroup>();
     String[] words;
      for (int i=0; i<items.length; i++){ 
        words = items[i].getId().split(REGEX);
          // Process key words  
       for (int j=0; j<words.length; j++){ 
            if (words[j].isEmpty()) break; 

Итак, я предполагаю, почему вы видите поведение, которое вы видите ... ВСЕГДА ИНИЦИАЛИЗИРУЙТЕ ПЕРЕМЕННЫЕ ПЕРЕД ДОСТУПОМ ИХ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...