Qualquer implementação de Ordered Set em Java?


98

Se alguém estiver familiarizado com Objective-C, há uma coleção chamada NSOrderedSetque atua como Set e seus itens podem ser acessados ​​como os de um Array .

Existe algo assim em Java?

Ouvi dizer que existe uma coleção chamada LinkedHashMap, mas não encontrei nada parecido para um conjunto.


Estou trabalhando em um problema semelhante em c ++. com NSOrderedSet, podemos acessar os elementos na ordem em que inserimos nele?
Vinay

Você sabe como obter a funcionalidade acima em C ++? i, e agindo como SET e pode ser acessado como elementos de um Array?
Vinay

Respostas:


118

Dê uma olhada na classe LinkedHashSet

Do documento Java :

Tabela de hash e implementação de lista vinculada da interface Set, com ordem de iteração previsível . Essa implementação difere do HashSet porque mantém uma lista duplamente vinculada em todas as suas entradas. Esta lista vinculada define a ordem das iterações, que é a ordem em que os elementos foram inseridos no conjunto (ordem de inserção) . Observe que a ordem de inserção não é afetada se um elemento for inserido novamente no conjunto . (Um elemento e é reinserido em um conjunto s se s.add (e) for invocado quando s.contains (e) retornaria verdadeiro imediatamente antes da invocação.).


Muito obrigado. Parece ser trivial de olhar, LinkedHashMapmas eu não encontrei de alguma forma.
Uko


9
Por que essa resposta está recebendo tantos votos positivos? Esta não é uma resposta à pergunta, de forma alguma. Não há nenhuma função LinkedHashSetque permita descobrir em qual índice o elemento está também.
searchengine27

31

Cada conjunto possui um iterador (). O iterador de um HashSet normal é bastante aleatório, um TreeSet faz isso por ordem de classificação, um iterador LinkedHashSet itera por ordem de inserção.

Você não pode substituir um elemento em um LinkedHashSet, entretanto. Você pode remover um e adicionar outro, mas o novo elemento não estará no lugar do original. Em um LinkedHashMap, você pode substituir um valor por uma chave existente e, então, os valores ainda estarão na ordem original.

Além disso, você não pode inserir em uma determinada posição.

Talvez seja melhor você usar um ArrayList com uma verificação explícita para evitar a inserção de duplicatas.


Eu quero ser capaz de definir / obter elementos em uma posição específica e obtê-los na ordem em que os adicionei. Parece que LinkedHashSetdeve fazer isso. Obrigado pela resposta
Uko

11

Dê uma olhada no documento da API padrão Java . Ao lado LinkedHashMap, há um LinkedHashSet. Mas observe que a ordem nesses é a ordem de inserção, não a ordem natural dos elementos. E você só pode iterar nessa ordem, não fazer acesso aleatório (exceto contando as etapas de iteração).

Também existe uma interface SortedSetimplementada por TreeSete ConcurrentSkipListSet. Ambos permitem iteração na ordem natural de seus elementos ou um Comparator, mas não acesso aleatório ou ordem de inserção.

Para uma estrutura de dados que tenha acesso eficiente por índice e possa implementar com eficiência o critério definido, você precisaria de uma lista de pular , mas não há implementação com essa funcionalidade na API Java Standard, embora esteja certo de que é fácil encontrar uma na internet.


Posso não ter entendido seu comentário, mas tive a impressão de que, desde o Java 1.6, havia várias coleções padrão baseadas em listas de pular (como, digamos, ConcurrentSkipListSet etc.).
TacticalCoder

@ user988052: sim, mas eles não implementam acesso aleatório por índice (embora meu entendimento de listas de omissão diga que isso deve ser possível), o que parece ser o que Uko deseja.
Michael Borgwardt

@MichaelBorgwardt Java 6 e posterior inclui um par de implementações de Lista de Pulos: ConcurrentSkipListMape ConcurrentSkipListSet. Ambos mantêm uma classificação baseada na ordem natural ou um Comparador. Não entendo se eles fornecem o acesso aleatório ou a ordem de entrada que você discute.
Basil Bourque

@BasilBourque: bom achado, e obrigado pelas edições. OP queria acesso por índice, e agora que olhei e pensei sobre isso, acho que as listas de pular também não têm essa capacidade ...
Michael Borgwardt

5

Essa é a resposta correta. Ao contrário LHSet, TreeSet não implementar java.util.SortedSet.
vemv

40
ordenado e classificado são coisas diferentes. TreeSet está classificado, não ordenado
andrii

2
Exatamente, ordenado refere-se ao pedido de inserção (a forma como uma Lista) funciona, enquanto classificado se refere à ordenação posterior dos elementos com base em alguns critérios.
Cornel Masson

5

Tente usar java.util.TreeSetesses implementos SortedSet.

Para citar o documento:

"Os elementos são ordenados usando sua ordem natural ou por um Comparador fornecido no momento da criação do conjunto, dependendo de qual construtor é usado"

Observe que adicionar, remover e contém um log de custo de tempo (n).

Se você deseja acessar o conteúdo do conjunto como um Array, pode convertê-lo fazendo:

YourType[] array = someSet.toArray(new YourType[yourSet.size()]); 

Este array será classificado com os mesmos critérios do TreeSet (natural ou por um comparador) e, em muitos casos, isso terá uma vantagem em vez de um Arrays.sort ()


1
Eu preciso encomendar como em ArrayList ei se eu colocar primeiro elemento ce, em seguida, elemento a, como eu iterar sobre uma coleção que eu quero para obtê-los na mesma ordem: c, aetc.
Uko

1

treeet é um conjunto ordenado, mas você não pode acessar por meio de um índice de itens, apenas iterar ou ir para o início / fim.


Com treeSet você incorrerá em um aumento de custo. LinkedHashSet tem um custo menor.
Carlos

0

Se estamos falando de implementação barata da lista de saltos, eu me pergunto, em termos de big O, qual é o custo desta operação:

YourType [] array = someSet.toArray (new YourType [yourSet.size ()]);

Quero dizer, sempre fica preso em uma criação de array inteiro, então é O (n):

java.util.Arrays#copyOf

1
Isso depende das características de desempenho do iterador e do size()método do conjunto subjacente. A iteração é geralmente O(n), o tamanho geralmente é, O(1)exceto ConcurrentSkipListSetonde está O(n).
Ian Roberts,


0

Você também pode obter alguma utilidade de um mapa bidirecional como o BiMapdo Google Guava

Com a BiMap, você pode mapear com bastante eficiência um Integer (para acesso ao índice aleatório) para qualquer outro tipo de objeto. BiMaps são um para um, portanto, qualquer dado inteiro tem, no máximo, um elemento associado a ele e qualquer elemento tem um inteiro associado. É habilmente sustentado por duas HashTableinstâncias, então usa quase o dobro da memória, mas é muito mais eficiente do que um costume no Listque diz respeito ao processamento porque contains()(que é chamado quando um item é adicionado para verificar se já existe) é um tempo constante e operação paralela amigável como HashSeta de, enquanto Lista implementação de é MUITO mais lenta.


0

Eu tive um problema parecido. Eu não precisava exatamente de um conjunto ordenado, mas sim de uma lista com um indexOf/ rápido contains. Como não encontrei nada por aí, implementei um sozinho. Aqui está o código, ele implementa ambos Sete List, embora nem todas as operações de lista em massa sejam tão rápidas quanto as ArrayListversões.

isenção de responsabilidade: não testado

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;

/**
 * An ArrayList that keeps an index of its content so that contains()/indexOf() are fast. Duplicate entries are
 * ignored as most other java Set's do.
 */
public class IndexedArraySet<E> extends ArrayList<E> implements Set<E> {

    public IndexedArraySet() { super(); }

    public IndexedArraySet(Iterable<E> c) {
        super();
        addAll(c);
    }

    private HashMap<E, Integer> indexMap = new HashMap<>();

    private void reindex() {
        indexMap.clear();
        int idx = 0;
        for (E item: this) {
            addToIndex(item, idx++);
        }
    }

    private E addToIndex(E e, int idx) {
        indexMap.putIfAbsent(requireNonNull(e), idx);
        return e;
    }

    @Override
    public boolean add(E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), size()) != null) return false;
        super.add(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll((Iterable<? extends E>) c);
    }
    public boolean addAll(Iterable<? extends E> c) {
        boolean rv = false;
        for (E item: c) {
            rv |= add(item);
        }
        return rv;
    }

    @Override
    public boolean contains(Object e) {
        return indexMap.containsKey(e);
    }

    @Override

    public int indexOf(Object e) {
        if (e == null) return -1;
        Integer i = indexMap.get(e);
        return (i == null) ? -1 : i;
    }

    @Override
    public int lastIndexOf(Object e) {
        return indexOf(e);
    }

    @Override @SuppressWarnings("unchecked")
    public Object clone() {
        IndexedArraySet clone = (IndexedArraySet) super.clone();
        clone.indexMap = (HashMap) indexMap.clone();
        return clone;
    }

    @Override
    public void add(int idx, E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), -1) != null) return;
        super.add(idx, e);
        reindex();
    }

    @Override
    public boolean remove(Object e) {
        boolean rv;
        try { rv = super.remove(e); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void clear() {
        super.clear();
        indexMap.clear();
    }

    @Override
    public boolean addAll(int idx, Collection<? extends E> c) {
        boolean rv;
        try {
            for(E item : c) {
                // check uniqueness
                addToIndex(item, -1);
            }
            rv = super.addAll(idx, c);
        } finally {
            reindex();
        }
        return rv;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean rv;
        try { rv = super.removeAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean rv;
        try { rv = super.retainAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        boolean rv;
        try { rv = super.removeIf(filter); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void replaceAll(final UnaryOperator<E> operator) {
        indexMap.clear();
        try {
            int duplicates = 0;
            for (int i = 0; i < size(); i++) {
                E newval = requireNonNull(operator.apply(this.get(i)));
                if(indexMap.putIfAbsent(newval, i-duplicates) == null) {
                    super.set(i-duplicates, newval);
                } else {
                    duplicates++;
                }
            }
            removeRange(size()-duplicates, size());
        } catch (Exception ex) {
            // If there's an exception the indexMap will be inconsistent
            reindex();
            throw ex;
        }

    }

    @Override
    public void sort(Comparator<? super E> c) {
        try { super.sort(c); }
        finally { reindex(); }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.