Existe uma ordem de inserção que preserva Set que também implementa List?

85

Estou tentando encontrar uma implementação de java.util.Liste java.util.Setao mesmo tempo em Java. Quero que essa classe permita apenas elementos exclusivos (as Set) e preserve sua ordem (como List). Ele existe no JDK 6?

É importante ter List<T>#add(int, T)para que eu possa inserir em uma posição específica.

Yegor256
fonte
Você quer dizer preservar seu pedido de inserção ou algum pedido definido por um Comparator? Você também quer a semântica da Listinterface?

Respostas:

208

TreeSeté classificado por ordem de elemento; LinkedHashSetretém o pedido de inserção. Esperançosamente, um desses é o que você estava procurando.

Você especificou que deseja inserir em um local arbitrário , suspeito que você terá que escrever o seu próprio - basta criar uma classe contendo um HashSet<T>e um ArrayList<T>; ao adicionar um item, verifique se ele está ou não no conjunto antes de adicioná-lo à lista.

Como alternativa, as ofertas commons-Collections4 do Apache ListOrderedSete SetUniqueList, que se comportam de maneira semelhante e devem atender aos requisitos fornecidos.

Jon Skeet
fonte
12

Você quer dizer gostar LinkedHashSet? Isso preserva a ordem de entrada, mas não permite duplicatas.

IMHO, é um requisito incomum, mas você pode escrever uma lista sem duplicatas.

class SetList<T> extends ArrayList<T> {
    @Override
    public boolean add(T t) {
        return !super.contains(t) && super.add(t);
    }

    @Override
    public void add(int index, T element) {
        if (!super.contains(element)) super.add(index, element);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        boolean added = false;
        for (T t : c)
            added |= add(t);
        return added;
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        boolean added = false;
        for (T t : c)
            if (!super.contains(t)) {
                super.add(index++, t);
                added = true;
            }
        return added;
    }
}
Peter Lawrey
fonte
Ele não implementa Listinterface, veja minhas alterações na pergunta
yegor256
2
stackoverflow.com/a/8185105/253468 parece melhor porque não tem O(n)complexidade de inserção, há uma troca que deve ser considerada entre armazenamento duplo e O(log(n))operação de inserção.
TWiStErRob
8

Você não pode implementar Liste de Setuma vez sem violação do contrato. Veja, por exemplo, o Set.hashCodecontrato:

O código hash de um conjunto é definido como a soma dos códigos hash dos elementos do conjunto, onde o código hash de um elemento nulo é definido como zero.

Por outro lado, aqui está o contrato de List.hashCode:

O código hash de uma lista é definido para ser o resultado do seguinte cálculo:

int hashCode = 1;
for (E e : list)
    hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

Portanto, é impossível implementar aula única que garanta o cumprimento de ambos os contratos. O mesmo problema para equalsimplementação.

Tagir Valeev
fonte
4

Se você não se limitar ao JDK 6, poderá usar a biblioteca de coleções comuns do Apache, que oferece a correspondência exata para sua necessidade - ListOrderedSet . É como Liste Setcombinado :)

Ondrej Bozek
fonte
sem a Listinterface
LateralFractal
0

Eu tive um problema semelhante, então escrevi o meu próprio. Veja aqui . O IndexedArraySetestende ArrayListe implementa Set, portanto, deve oferecer suporte a todas as operações de que você precisa. Observe que inserir elementos em locais no meio de uma ArrayListpode ser lento para listas grandes porque todos os elementos a seguir precisam ser movidos. Meu IndexedArraySetnão muda isso.

JanKanis
fonte
0

Outra opção (sem o Listrequisito de interface) é o Guava's ImmutableSet, que preserva a ordem de inserção. De sua página wiki :

Except for sorted collections, order is preserved from construction time. For example,

ImmutableSet.of("a", "b", "c", "a", "d", "b")

will iterate over its elements in the order "a", "b", "c", "d".

kuporific
fonte