Ordenação não mantém a ordem da primeira chamada de Collections.sort() [RESOLVIDO]

6 respostas
diego_qmota

Pessoal, essa dúvida é com relação ao tópico que postei esses dias: http://www.guj.com.br/posts/list/208351.java

Acredito que se trata de algo mais simples, por isso estou postando aqui.

Eu li na web que quando você executa o método sort() da classe Collections, ela realiza a ordenação de uma List.

Até aí beleza, essa parte eu consegui…

No entanto, quando eu vou realizar a segunda ordenação, ele desfaz a primeira e ordena toda a List pela segunda ordenação…. Ou seja, não mantém a primeira ordenação.

//primeira ordenação - tipo cliente
Collections.sort(faturas, comparadorTipoCliente);

//segunda ordenação
Collections.sort(faturas, comparadorCidade);

Para visualizar o que está ocorrendo, reproduzi um exemplo na imagem anexa.

Gostaria de saber como resolver o problema?


6 Respostas

M

Diego, o comportamento está correto. O Collections.sort tenta manter a ordem apenas nos itens que ele não precisa mexer, mas o comparator que você passa no parâmetro sempre é prioritário (ou seja, ele sempre vai tentar ordenar por ele, mantendo os itens no lugar apenas no caso de empate).

Se você quiser que as duas comparações sejam obedecidas, seu comparator tem que implementar as duas regras.

diego_qmota

Mas aí fica complicado hein…

Se você tiver que ordenar por um monte de atributos, o código vai crescer bastante e ficar lotado de “ifs”.

Posso usar recursão nesse caso?

M

Pode usar recursão sim.
Uma maneira mais fácil é, dentro de um compare de um Comparator, você chamar o compare do outro logo no começo e, caso o resultado seja 0, você prossegue com a lógica normalmente.

diego_qmota

mtakeda:
Pode usar recursão sim.
Uma maneira mais fácil é, dentro de um compare de um Comparator, você chamar o compare do outro logo no começo e, caso o resultado seja 0, você prossegue com a lógica normalmente.

É, eu vi alguns exemplos disso, inclusive no GUJ, nesse molde:

public class ComparadorIdIdade implements Comparator<Pessoa> { 
public int compare(Pessoa pessoa1,Pessoa pessoa2) { 

int result = pessoa1.getId().compareTo(pessoa2.getId()); 
return result == 0 ? pessoa1.getIdade().compareTo(pessoa2.getIdade()) : result; 

} 
}

Pensei um pouco nessa lógica quando vi.
Parece que exige que a classe implemente Comparable

É claro que, alterando um pouco o código dessa lógica, não é necessário implementar a interface Comparable (minha idéia é só usar a ordenação para determinada finalidade e não como via de regra para comparar os objetos da classe).
Nesse molde, pode-se fazer uma recursão para entrar quando o resultado for 0:

Ao invés de:
return result == 0 ? pessoa1.getIdade().compareTo(pessoa2.getIdade()) : result;
Pode ser instanciado um novo Comparator aqui:

if (result == 0)
{ 
     ComparadorNivel2 comparador = new ComparadorNivel2();
     return comparador.compare(pessoa1, pessoa2); 
} else
     return result;

Vou testar para ver se dá.

diego_qmota

Acabei encontrando uma classe pronta e eficaz, que evita esse encadeamento de Comparator’s:

A classe CompositeComparator

package prefuse.util.collections;

import java.util.Comparator;

/**
 * Comparator that makes comparison using an ordered list of
 * individual comparators;
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class CompositeComparator implements Comparator {

    private static final int INCREMENT = 2;
    private Comparator[] m_cmp;
    private int m_rev = 1;
    private int m_size = 0;

    /**
     * Creates an empty CompositeComparator with the given capacity.
     * @param size the starting capacity of this comparator
     */
    public CompositeComparator(int size) {
        this(size, false);
    }
    
    /**
     * Creates an empty CompositeComparator with the given capacity.
     * @param size the starting capacity of this comparator
     * @param reverse when true, reverses the sort order of the included
     * comparators, when false, objects are sorted as usual
     */
    public CompositeComparator(int size, boolean reverse) {
        m_cmp = new Comparator[size];
        m_rev = reverse ? -1 : 1;
    }
    
    /**
     * Creates a new CompositeComparator.
     * @param cmp the constituent comparators of this composite
     */
    public CompositeComparator(Comparator[] cmp) {
        this(cmp, false);
    }
    
    /**
     * Creates a new CompositeComparator.
     * @param cmp the constituent comparators of this composite
     * @param reverse when true, reverses the sort order of the included
     * comparators, when false, objects are sorted as usual
     */
    public CompositeComparator(Comparator[] cmp, boolean reverse) {
        this(cmp.length, reverse);
        System.arraycopy(cmp, 0, m_cmp, 0, cmp.length);
        m_size = cmp.length;
    }
    
    /**
     * Adds an additional comparator to this composite.
     * @param c the Comparator to add
     */
    public void add(Comparator c) {
        if ( c == null ) return;
        if ( m_cmp.length == m_size ) {
            Comparator[] cmp = new Comparator[m_size+INCREMENT];
            System.arraycopy(m_cmp, 0, cmp, 0, m_size);
            m_cmp = cmp;
        }
        m_cmp[m_size++] = c;
    }
    
    /**
     * Removes a comparator from this composite.
     * @param c the Comparator to remove
     * @return true if the comparator was successfully removed,
     * false otherwise
     */
    public boolean remove(Comparator c) {
        for ( int i=0; i<m_size; ++i ) {
            if ( m_cmp[i].equals(c) ) {
                System.arraycopy(m_cmp, i+1, m_cmp, i, m_size-i);
                --m_size;
                return true;
            }
        }
        return false;
    }
    
    // ------------------------------------------------------------------------
    
    /**
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    public int compare(Object o1, Object o2) {
        for ( int i=0; i<m_cmp.length; ++i ) {
            int c = m_cmp[i].compare(o1, o2);
            if ( c != 0 ) {
                return m_rev*c;
            }
        }
        return 0;
    }

} // end of class CompositeComparator

É bom saber como se faz…mas em determinados casos, perdemos muito tempo “reiventando a roda”… rs.

Funcionou certinho. Posso enfileirar vários Comparator agora!

Valeu!
:smiley:

Marky.Vasconcelos

Bem… as vezes voce nao precisa ter varios Comparators, voce pode simplesmente colocar toda lógica em um só.

Olhe esse que eu uso (não se assuste com o operador ternario, voce pode fazer melhor)

public class PcRateExtractComparator implements Comparator {
		public int compare(Object obj1, Object obj2) {
			PcRateExtraction o1 = (PcRateExtraction) obj1;
			PcRateExtraction o2 = (PcRateExtraction) obj2;
			int k1 = o1.getGeoZone().compareTo(o2.getGeoZone());
			int k2 = o1.getPeriodName().compareTo(o2.getPeriodName());
			int k3 = o1.getSocCode().compareTo(o2.getSocCode());
			int k4 = Util.lpad(o1.getPricingItemCode(), '0', 15).compareTo(
					Util.lpad(o2.getPricingItemCode(), '0', 15));
			int k5 = Integer.parseInt(o2.getVersion())
					- Integer.parseInt(o1.getVersion());
			return k1 != 0 ? k1 : k2 != 0 ? k2 : k3 != 0 ? k3 : k4 != 0 ? k4
					: k5;
		}
	}

Literalmente a lista sera ordenado por: GeoZone, , PeriodName, SocCode, PricingItemCode e Version.

Criado 31 de maio de 2010
Ultima resposta 31 de mai. de 2010
Respostas 6
Participantes 3