Eu sei SortedSet
, mas no meu caso preciso de algo que implemente List
, e não Set
. Então, há uma implementação lá fora, na API ou em outro lugar?
Não deve ser difícil de implementar, mas imaginei por que não perguntar às pessoas aqui primeiro?
Eu sei SortedSet
, mas no meu caso preciso de algo que implemente List
, e não Set
. Então, há uma implementação lá fora, na API ou em outro lugar?
Não deve ser difícil de implementar, mas imaginei por que não perguntar às pessoas aqui primeiro?
Respostas:
Não há coleção Java na biblioteca padrão para fazer isso. LinkedHashSet<E>
preserva a ordenação de forma semelhante a a List
, entretanto, se você envolver seu conjunto em a List
quando quiser usá-lo como a, List
obterá a semântica desejada.
Alternativamente, o Commons Collections (ou commons-collections4
, para a versão genérica) tem um List
que faz o que você já quer: SetUniqueList
/ SetUniqueList<E>
.
Aqui está o que eu fiz e funciona.
Supondo que eu tenha um ArrayList
com o qual trabalhar, a primeira coisa que fiz foi criar um novo LinkedHashMap
.
LinkedHashSet<E> hashSet = new LinkedHashSet<E>()
Em seguida, tento adicionar meu novo elemento ao LinkedHashSet
. O método add não altera o LinkedHasSet
e retorna false se o novo elemento for uma duplicata. Portanto, essa se torna uma condição que posso testar antes de adicionar ao ArrayList
.
if (hashSet.add(E)) arrayList.add(E);
Esta é uma maneira simples e elegante de evitar que duplicatas sejam adicionadas a uma lista de arrays. Se você quiser, pode encapsulá-lo e sobrescrever o método add em uma classe que estende o ArrayList
. Apenas lembre-se de lidar com isso addAll
percorrendo os elementos e chamando o método add.
Então aqui está o que eu fiz eventualmente. Espero que isto ajude alguém.
class NoDuplicatesList<E> extends LinkedList<E> {
@Override
public boolean add(E e) {
if (this.contains(e)) {
return false;
}
else {
return super.add(e);
}
}
@Override
public boolean addAll(Collection<? extends E> collection) {
Collection<E> copy = new LinkedList<E>(collection);
copy.removeAll(this);
return super.addAll(copy);
}
@Override
public boolean addAll(int index, Collection<? extends E> collection) {
Collection<E> copy = new LinkedList<E>(collection);
copy.removeAll(this);
return super.addAll(index, copy);
}
@Override
public void add(int index, E element) {
if (this.contains(element)) {
return;
}
else {
super.add(index, element);
}
}
}
Por que não encapsular um conjunto com uma lista, classificar como:
new ArrayList( new LinkedHashSet() )
Isso deixa a outra implementação para alguém que é um verdadeiro mestre em coleções ;-)
Você deve considerar seriamente a resposta de dhiller:
new ArrayList(set)
(ou a new LinkedList(set)
, qualquer coisa).Acho que a solução que você postou com o NoDuplicatesList
tem alguns problemas, principalmente com o contains()
método, além de sua classe não lidar com a verificação de duplicatas na coleção passada para o seu addAll()
método.
Eu precisava de algo assim, então fui às coleções comuns e usei o SetUniqueList
, mas quando executei alguns testes de desempenho, descobri que parece não estar otimizado em comparação com o caso se eu quiser usar um Set
e obter um Array
usando o Set.toArray()
método.
O SetUniqueTest
tomou 20: 1 tempo para preencher e depois transversal 100.000 Cordas comparando à outra implementação, o que é uma grande diferença negócio.
Então, se você se preocupa com o desempenho, eu recomendo que você use Set and Get an Array em vez de usar o SetUniqueList
, a menos que você realmente precise da lógica de SetUniqueList
, então você precisará verificar outras soluções ...
Método principal do código de teste :
public static void main(String[] args) {
SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();
long t1 = 0L;
long t2 = 0L;
String t;
t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;
t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
s.add("a" + Math.random());
}
s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
t = d[i];
}
t2 = System.nanoTime() - t2;
System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2); //comparing results
}
Atenciosamente, Mohammed Sleem
NOTA: não leva em consideração a implementação de subList .
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class UniqueList<T> extends ArrayList<T> {
private static final long serialVersionUID = 1L;
/** Unique elements SET */
private final Set<T> set=new HashSet();
/** Used by addAll methods */
private Collection<T> addUnique(Collection<? extends T> col) {
Collection<T> unique=new ArrayList();
for(T e: col){
if (set.add(e)) unique.add(e);
}
return unique;
}
@Override
public boolean add(T e) {
return set.add(e) ? super.add(e) : false;
}
@Override
public boolean addAll(Collection<? extends T> col) {
return super.addAll(addUnique(col));
}
@Override
public void add(int index, T e) {
if (set.add(e)) super.add(index, e);
}
@Override
public boolean addAll(int index, Collection<? extends T> col) {
return super.addAll(index, addUnique(col));
}
}
A documentação para interfaces de coleção diz:
Conjunto - uma coleção que não pode conter elementos duplicados.
Lista - uma coleção ordenada (às vezes chamada de sequência). As listas podem conter elementos duplicados.
Portanto, se você não quiser duplicatas, provavelmente não deve usar uma lista.
no add
método, por que não usar HashSet.add()
para verificar duplicatas em vez de HashSet.consist()
.
HashSet.add()
retornará true
se não houver duplicata e false
caso contrário.
HashSet#consist()
?
No topo da minha cabeça, as listas permitem duplicatas. Você pode implementar rapidamente ae UniqueArrayList
substituir todas as funções add
/ insert
para verificar contains()
antes de chamar os métodos herdados. Para uso pessoal, você só poderia implementar o add
método que usa e substituir os outros para lançar uma exceção no caso de futuros programadores tentarem usar a lista de uma maneira diferente.
Acabei de fazer minha própria UniqueList em minha pequena biblioteca como esta:
package com.bprog.collections;//my own little set of useful utilities and classes
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Jonathan
*/
public class UniqueList {
private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;
public UniqueList() {
growableUniques = new ArrayList();
}
public UniqueList(int size) {
growableUniques = new ArrayList(size);
}
public void add(Object thing) {
if (!masterSet.contains(thing)) {
masterSet.add(thing);
growableUniques.add(thing);
}
}
/**
* Casts to an ArrayList of unique values
* @return
*/
public List getList(){
return growableUniques;
}
public Object get(int index) {
return growableUniques.get(index);
}
public Object[] toObjectArray() {
int size = growableUniques.size();
returnable = new Object[size];
for (int i = 0; i < size; i++) {
returnable[i] = growableUniques.get(i);
}
return returnable;
}
}
Eu tenho uma classe TestCollections parecida com esta:
package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* @author Jonathan
*/
public class TestCollections {
public static void main(String[] args){
UniqueList ul = new UniqueList();
ul.add("Test");
ul.add("Test");
ul.add("Not a copy");
ul.add("Test");
//should only contain two things
Object[] content = ul.toObjectArray();
Out.pl("Array Content",content);
}
}
Funciona bem. Tudo o que ele faz é adicionar a um conjunto se ainda não o tiver e houver um Arraylist que pode ser retornado, bem como um array de objetos.