2015-01-08 2 views
5

Я хочу создавать коллекции, которые могут содержать повторяющиеся значения, в определенном порядке.Какая коллекция Java считает перестановки равными?

Другими словами:

{ 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 } 

На самом деле, я хочу, чтобы иметь набор этих коллекций, так что если я пытаюсь добавить как { 1, 1, 2 } и { 2, 1, 1 }, второй .add() не делает ничего.

Есть ли стандартная коллекция, которая уже ведет себя таким образом?

Если я правильно понимаю:

  • ArrayList позволяет дублирующие значения, но имеет фиксированный порядок
  • HashSet позволяет для того, чтобы быть произвольным, но дубликат не дорожит
  • TreeSet гарантирует, что заказ константа, но не допускает повторяющихся значений

Есть ли коллекция, которую я упустил, которая допускает как повторяющиеся значения, так и произвольные или фиксированные порядок, так что две коллекции с одинаковыми элементами считаются равными?


@asteri спросил о моем прецеденте. В игре у меня есть блоки разной длины, которые можно уложить от конца до конца, чтобы заполнить определенное расстояние. Например, если расстояние равно 10, оно может быть заполнено 2-3-5 или 5-2-3 или 3-3-4 или 3-4-3 или любое количество других перестановок. В зависимости от того, какие блоки доступны, я хочу составить список всех возможных коллекций, которые помогут заполнить пробел.


CUSTOM РЕШЕНИЕ
@sprinter предложил создать подкласс ArrayList. @dasblinkenlight и @Dici предложили использовать карту для хранения { Element : Count } записей. Я решил объединить эти два предложения. Ниже представлен подкласс TreeMap. Клавиши всегда сохраняются в том же порядке, чтобы гарантировать, что метод hashCode() производит одно и то же значение, например, с теми же ключами и значениями.

Я использовал метод increment, чтобы упростить добавление нового вхождения определенного целочисленного значения.

package com.example.treematch; 

import java.util.Map; 
import java.util.TreeMap; 

public class TreeMatch<K> extends TreeMap<K, Integer> { 

    @Override 
    public boolean equals(Object other) { 
    if (this == other) { 
     return true; 
    } 

    if (!(other instanceof TreeMatch)) { 
     return false; 
    } 

    TreeMatch otherMatch = (TreeMatch) other; 
    if (size() != otherMatch.size()) { 
     return false; 
    } 

    for (Object key : this.keySet()) { 
     if (!otherMatch.containsKey(key)) { 
      return false; 
     } 
    } 

    for (Object key : otherMatch.keySet()) { 
     if (!this.containsKey(key)) { 
     return false; 
     } 

     if (this.get(key) != otherMatch.get(key)) { 
     return false; 
     } 
    } 

    return true; 
    } 

    public void increment(K key) { 
    Integer value; 

    if (this.containsKey(key)) { 
     value = (this.get(key)) + 1; 
    } else { 
     value = 1; 
    } 

    this.put(key, value); 
    } 


    @Override 
    public int hashCode() { 
    int hashCode = 0; 

    for (Map.Entry entry : this.entrySet()) { 
     hashCode += entry.getKey().hashCode(); 
     hashCode = hashCode << 1; 
     hashCode += entry.getValue().hashCode(); 
     hashCode = hashCode << 1; 
    } 

    return hashCode; 
    } 
} 
+0

Mmm ... интересующийся вопрос. Не то, о чем я могу думать, хотя может быть что-то, что помогает в Apache Commons или Guava. Могу ли я попросить ваш случай использования для этого, из любопытства? – asteri

+0

Не ответ на ваш вопрос, но легким обходным путем было бы иметь их в виде списков, а затем сортировать их и сравнивать. – asteri

+0

Интересно: [Есть ли способ проверить, содержат ли две коллекции одни и те же элементы независимо от порядка?] (Http://stackoverflow.com/a/1565262/1762224) '->' 'HashMultiset.create (c1). equals (HashMultiset.create (c2)); ' –

ответ

2

Я думаю, что ваш лучший вариант, чтобы обернуть Map таким образом:

  • ключей являются вашими значениями
  • значения числа вхождений ключей
  • равного метода, основанным на Map.keySet() равенство, если число случаев имеет значение, Map.equals() в противном случае
+0

Я думаю, что OP предпочтет равные реализации, основанные на 'Map.equals', а не на' keySet', поскольку они, похоже, хотят, чтобы количество вхождений каждого элемента имело значение? –

+0

@LouisWasserman На самом деле, повторив вопрос снова, я думаю, что я был прав, так как '{1, 1, 2} == {2, 1, 1} == {1, 2, 1}' – Dici

+0

Сравнивая с keySet, 1,1,2} = {1,2}, что я считаю неправильным? –

6

В встроенных библиотеках Java нет ничего, но Guava Multiset делает это.

+0

Bam. Apache или Guava всегда поставляет. – asteri

2

HashMap<Integer,Integer> мог бы представлять вашу коллекцию, если вы храните значение в качестве ключа и количество раз, которое это значение отображается в качестве значения на карте. Например, ваша коллекция {1, 1, 2} будет представлена ​​следующим образом:

{{ 1 : 2 }, { 2 : 1 }} 

означает, что есть два элемента с значением 1 (первая пара целых чисел), и один элемент с значением 2 (вторая пара целых чисел).

3

Этот тип коллекции обычно известен как multiset. В стандартную библиотеку не включены реализации мультимножеств, но внешние библиотеки Guava включают мультимножества.

1

Я хотел бы предложить создать подкласс ArrayList, который переопределяет метод Equals:

class MultiMemberList extends ArrayList { 
    public boolean equals(Object other) { 
     if (this == other) 
      return true; 
     if (!(other instanceof MultiMemberList)) 
      return false; 
     MultiMemberList otherList = (MultiMemberList)other; 
     if (size() != otherList.size()) 
      return false; 
     return stream().distinct().allMatch(element -> countElement(element) == otherList.countElement(element)); 
    } 

    private int countElement(Object element) { 
     return stream().filter(element::equals).count(); 
    } 
} 

Я не сделал это родовое, чтобы держать это простой, но очевидно, что вы должны сделать это.

Вы также должны переопределить хеш-функцию: простейшей задачей было бы отсортировать список перед вызовом хэша суперкласса.

После того, как вы осуществили это, установите, что добавленные объекты MultiMemberList будут работать так, как вы ожидаете: он не добавит второй список, который будет равен первому в соответствии с этим определением.

+0

Разве ваш метод равен 'O (n^2) '? – Dici

+0

Существует множество способов повышения производительности. Я пошел просто, чтобы не усложнять ответ. – sprinter

3

Вы можете использовать Bag от Eclipse Collections, также известный как Multiset.

MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2); 
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1); 
Assert.assertEquals(bag1, bag2); 

Поскольку ваша коллекция будет содержать только ints, лучше использовать примитивный пакет. IntHashBag опирается на int[] массивы, а не Object[] массивы, использует меньше памяти и может быть быстрее.

MutableIntBag intBag1 = IntBags.mutable.with(1, 1, 2); 
MutableIntBag intBag2 = IntBags.mutable.with(1, 2, 1); 
Assert.assertEquals(intBag1, intBag2); 

Примечание: Я коммиттер для коллекций Eclipse.

Смежные вопросы