Список Java, который показывает элементы только один раз, но считает их появление?

Я хотел бы создать список, в котором хранятся объекты одного типа, например:

class Card {
    String title;
    int point;

    public Card(String t, int p) {
        title = t;
        point = p;
    }
}

И я добавляю несколько объектов в список:

list.add(new Card("Fred",3));
list.add(new Card("Fred",1));
list.add(new Card("Luke",5));
list.add(new Card("John",3));

Как я могу выполнить следующие задачи? Создайте новый список, который содержит предыдущие элементы, но каждый заголовок один раз, и подсчитывает их вхождения. Например:

Fred 2
Luke 1
John 1

Я могу сделать первую часть, я просто создаю HashSet и перезаписываю в песне методы "equals" и "hashCode". Но я не хочу полностью удалять два с одинаковым названием.

2 ответа

Решение
Map<String, Card> cardsByTitle = new TreeMap<>();

void add(String title, int point) {
    Card card = cardsByTitle.get(title);
    if (card == null) {
        card = new Card(title, point);
        cardsByTitle.put(title, point);
    } else {
        card.point += point;
    }
}

for (Card card : cardsByTitle.values) { }

Или что-то подобное. TreeMap (io HashMap) сохраняет упорядоченные заголовки.

Вы можете реализовать свой собственный список, который внутренне хранит свои элементы в ArrayList и хранит карту количества карт с заданным заголовком:

public static class CountingList extends AbstractList<Card>{

    private final List<Card> list;
    private final Map<String, Integer> counterMap;

    public CountingList() {
        this(10);
    }

    public CountingList(Collection<? extends Card> c) {
        this(c.size());
        addAll(c);
    }

    public CountingList(int initialCapacity) {
        super();
        this.list = new ArrayList<>(initialCapacity);
        this.counterMap = new HashMap<>(initialCapacity);
    }


    @Override
    public boolean add(Card e) {
        Integer count = counterMap.get(e.title);
        String key = e.title;
        if (count == null) {
            list.add(e);
            count = 0;
        }
        counterMap.put(key, ++count);
        return count == 1;
    }

    @Override
    public void add(int index, Card element) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Card remove(int index) {
         throw new UnsupportedOperationException();
    }

    @Override
    public Card get(int index) {
        return list.get(index);
    }

    @Override
    public int size() {
        return list.size();
    }

    public int getNumberOfCards(int index){
        return getNumberOfCards(list.get(index).title);
    }

    public int getNumberOfCards(String title){
        Integer count = counterMap.get(title);
        return count == null ? 0 : count;
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append('[');
        for (Card c : this) {
            b.append(c.title).append('(').append(getNumberOfCards(c.title)).append(')');
        }
        b.append(']');
        return b.toString();
    }
}

Тогда вы можете использовать этот список следующим образом:

public static void main(String[] args) {
    List<Card> list = new ArrayList<>();
    list.add(new Card("Fred",3));
    list.add(new Card("Fred",1));
    list.add(new Card("Luke",5));
    list.add(new Card("John",3));

    CountingList countingList = new CountingList(list);
    System.out.println(countingList);

    countingList.add(new Card("Tom", 1));
    countingList.add(new Card("Tom", 10));
    countingList.add(new Card("Tom", 15));
    System.out.println(countingList);

    countingList.add(new Card("Fred", 4));
    System.out.println(countingList);
}

Обратите внимание, что вставка элементов по заданному индексу и удаление элементов для этого списка еще не поддерживается.

Чтобы узнать количество карточек с заданным названием, вы можете вызвать CountingList#getNumberOfCards.

Другие вопросы по тегам