Preencher uma terceira lista com os elementos diferentes entre a primeira e a segunda lista

35 respostas
java
B

Pessoal boas?? tenho três listas, allDocuments , documentsLaunch newsDocuments
quero copiar para lista newsDocuments os elementos que estão na lista allDocuments mas que não estejam na lista documentsLaunch tentei mas não tive muito sucesso…

public List novoDocumento() {

int tamanho = 0;

List allDocuments = fnContacorrenteFacade.findAll();

List documentsLaunch = ctLancamentoFacade.findAll();

List newsDocuments = new ArrayList<>();
for (FnContacorrente term : allDocuments) {

        for (CtLancamento lanc : documentsLaunch) {

            if (term.getIdfnContacorrente() != lanc.getIdfnContacorrenteFnContacorrente().getIdfnContacorrente()) {

                tamanho = tamanho + 1;
            }
        }

        if (tamanho == documentsLaunch.size()){
            FnContacorrente item = new FnContacorrente();
            item.setDatadoc(term.getDatadoc());
            item.setDatavencimento(term.getDatavencimento());
            item.setEntidade(term.getEntidade());
            item.setIdfnContacorrente(term.getIdfnContacorrente());
            item.setNumero(term.getNumero());
            item.setValor(term.getValor());
            item.setObservacoes(term.getObservacoes());
            newsDocuments.add(item);
        }

    }

    return newsDocuments;
}

35 Respostas

lvbarbosa
  1. Copie a lista allDocuments para uma variável temporária:

    List temporaria = new ArrayList<>(allDocuments);
    
  2. Remova de temporaria todos os elementos presentes em documentLaunch:

    temporaria.removeAll(documentsLaunch);
    
  3. Defina newsDocuments:

    newsDocuments = new ArrayList<>(temporaria);
    

    ou

    newsDocuments = temporaria;
    

    ou qualquer outra coisa do gênero.

Para isso, você vai ter que sobrescrever os métodos equals e hashCode dos objetos contidos nas listas.

Fonte: https://docs.oracle.com/javase/8/docs/api/java/util/List.html#removeAll-java.util.Collection-

B

A remoção é feita com ciclos?’

lvbarbosa

Por baixo dos panos sim, por você não. A implementação de List que vai fazer o loop quando você chamar o removeAll. Você só precisa fazer isso mesmo, essas 3 linhas. Na verdade nem precisa da temporária, dá pra fazer direto assim:

newsDocuments = new ArrayList<>(allDocuments); // cria uma nova ArrayList com todos os elementos de allDocuments
newsDocuments.removeAll(documentsLaunch); // remove de newsDocuments todos os elementos presentes em documentsLaunch que estão também em newsDocuments
B

Calma fica aí cara vou montar isso… depois te dou um toque.

lvbarbosa

Exemplo:

public static void main(String[] args) {
    List<Integer> naturais = Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    List<Integer> pares = Arrays.asList(new Integer[]{0, 2, 4, 6, 8});
    List<Integer> impares = new ArrayList<>(naturais);
    impares.removeAll(pares);
    System.out.println(impares);
}
B

estou a implementar…

lvbarbosa

Usando Java 8:

public static void main(String[] args) {
    List<Integer> naturais = Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    List<Integer> pares = Arrays.asList(new Integer[]{0, 2, 4, 6, 8});
    List<Integer> impares = naturais.stream().filter(i -> !pares.contains(i)).collect(Collectors.toList());
    System.out.println(impares);
}
B

Vou reiniciar a máquina tive um erro no servidor cara java.lang.OutOfMemoryError: PermGen space

estou usando java 7

B

Não funciona cara

List allDocuments = fnContacorrenteFacade.findAll();

List documentsLaunch = ctLancamentoFacade.findAll();

List newsDocuments = new ArrayList<>(allDocuments);

newsDocuments.removeAll(documentsLaunch);

Estou a pensar em resolver isso com um join entre as tabelas na BD… o que achas?

lvbarbosa

O construtor ArrayList(Collection c) faz uma cópia rasa (só a referência) de todos os elementos contidos na coleção c. Se a coleção for muito grande, isso pode ser um problema. Uma maneira de aliviar um pouco isso é fazer você a lógica de cópia, evitando a cópia completa. Assim:

newDocuments = new ArrayList<>();
for (Document document : allDocuments) {
    if (!documentsLaunch.contains(document)) {
        newDocuments.add(document);
    }
}
lvbarbosa

Você sobrescreveu equals e hashCode no tipo que é contido na lista?

B
<a class="mention" href="/u/override">@Override</a>

public int hashCode() {

int hash = 0;

hash += (idfnContacorrente != null ? idfnContacorrente.hashCode() : 0);

return hash;

}
@Override
public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof FnContacorrente)) {
        return false;
    }
    FnContacorrente other = (FnContacorrente) object;
    if ((this.idfnContacorrente == null && other.idfnContacorrente != null) || (this.idfnContacorrente != null && !this.idfnContacorrente.equals(other.idfnContacorrente))) {
        return false;
    }
    return true;
}
lvbarbosa

A única razão para o método removeAll não funcionar é uma implementação errada de equals e hashCode. Nesse caso, nem mesmo o contains vai funcionar, porque ele usa o equals.

Fonte: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#contains-java.lang.Object-

lvbarbosa

O metodo equals tá errado. Tá retornando true sem comparar os atributos do objeto, só testa se é null ou não. O hashCode tá errado também.

Tem bastante detalhe sobre isso aqui: http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java

Esses métodos devem ser implementados com muito cuidado. Existe uma série de regras que devem ser levadas em conta, para que sejam implementados corretamente. Isso é assunto de certificação, inclusive hahahahah

Geralmente as IDE geram esses dois métodos para ti. Tem gente que é contra usar esse recurso, porque pode sair errado em alguns casos.

B

Vamos uma consulta usando join entre as tabelas CtLancamento e FnContacorrente trazendo todos os elementos da tabela FnContacorrente que não estejam na tabela CtLancamento as duas estão relacionadas… usando join como ficaria??

B

Sim o IDE gerou mesmo…

B

Alguns documentos nessa tabela já foram lançados, eu quero que após o lançamento de um documento eles já não estejam na tabela…

lvbarbosa

Você quer todas as contas que não têm um lançamento? Se o relacionamento é bidirecional, e
é @ManyToOne de Conta para Lancamento, você pode fazer algo como:

SELECT conta FROM FnContacorrente conta WHERE conta.lancamento IS NULL
B

Não, quero todos os documentos(FnContacorrente) que ainda não foram lançados(CtLancamento)…

lvbarbosa

Me diz qual a relação de FnContacorrente com CtLancamento, a cardinalidade do relacionamento. É 1-1, N-M, 1-N/N-1? Qual possui qual?

B

1:N, ou seja, uma documento está associado a um lançamento.

lvbarbosa

É 1:N ou 1:1? hahahahahaha

lvbarbosa

Algo como:

SELECT d FROM Documento d WHERE d.lancamento IS NULL

retorna todos os Documento que não estão associados a um lançamento.

B

Sim, eu relacionei dessa forma, o id do documento está na tabela lançamento… pego a patiri de uma combobox.

B

sim, vou tentar!

lvbarbosa

O IS NULL vai funcionar no caso de uma referência @*ToOne.

Se fosse uma @*ToMany, você poderia fazer algo assim:

SELECT d FROM Documento d WHERE d.lancamentos IS EMPTY
B

a tabela documento não tem como chegar em lançamento cara…

lvbarbosa

Mas não precisa estar. Quando você define um relacionamento de, nesse caso, Documento para Lancamento, ele pode ser definido com um relacionamento reverso. É para isso que serve o atributo mappedBy das anotações de relacionamento.

Vou dar um exemplo bem simples, você adapta pro teu caso:

Imagine que no DB temos assim:

Lancamento

  • id_lancamento (chave primaria)
  • id_documento (chave estrangeira, referencia Documento)

Documento

  • id_documento(chave primaria)

Certo? Note que o dono do relacionamento é Lancamento, pois ele possui a chave estrangeira.

No Java:

class Lancamento {
    @Id
    private long idLancamento; 
    @OneToOne
    private Documento documento;
}

class Documento {
    @Id
    private long idDocumento
    @OneToOne(mappedBy = "documento")
    private Lancamento lancamento;
}

Quando você fizer uma query assim em JPQL:

SELECT lanc FROM Documento d JOIN FETCH d.lancamento lanc WHERE d.id :documento_id

Você vai acessar o lancamento a partir do documento, mesmo a tabela Documento não tendo a referência para Lancamento! Sacou? Isso funciona por causa dos mapeamentos nas entidades em java, utilizando a anotação @OneToOne (ou qualquer outra).

O JPA, por baixo dos panos, vai emitir uma query mais ou menos assim em SQL pro DB:

SELECT * FROM lancamento WHERE lancamento.id_documento = (id_do_documento);
B

Sim, mas neste caso como ficaria a query para me retornar todos os documentos que não foram lançados? Tudo o que você falou faz sentido mas não vejo a query… estou tentando aqui.

lvbarbosa

Na classe Lancamento, você tem uma referência para Documento, certo? (Me ajuda com os nomes, faz de conta que são esses). Você quer fazer referência ao Lancamento a partir de Documento, isso?

Pelo que eu entendi, falta uma referencia para Lancamento dentro da classe Documento. Isso é o que chamamos de relacionamento reverso. É aqui que você vai colocar o mappedBy para poder fazer a query que eu falei com JPQL.

Parece que você tem isso:

class Lancamento {
    @OneToOne
    private Documento varivelDoTipoDocumentoDentroDaClasseLancamento;
}

Mas em Documento, você não tem isso:

class Documento {
    //note que a string é o nome do atributo do tipo Documento na classe Lancamento, exatamente do mesmo jeito
    @OneToOne(mappedBy = "varivelDoTipoDocumentoDentroDaClasseLancamento") 
    private Lancamento lancamento;
}

Por isso você não consegue partir de Documento para Lancamento. Correto? Se for isso, basta você colocar o atributo Lancamento dentro da classe Documento, com a anotação @OneToOne, e com o mappedBy sendo igual ao nome do atributo dentro da classe Lancamento do tipo Documento (isso talvez tenha ficado confuso, olha o código que ajuda).

B

ok, vou acrescentar isso!

B

Alterei a nível de BD, criei uma relação entre as tabelas de 1:1, vou gerar novamente as Entity.

B

Testei, está trazer o inverso, ou seja, está trazer os documentos que foram lançados…

SELECT c FROM FnContacorrente c WHERE c.ctLancamento.idfnContacorrenteFnContacorrente.idfnContacorrente IS NOT NULL

B

O SÍMBOLO != não está funcionar… a ideia é trazer os diferentes que são os que ainda não foram lançados.

lvbarbosa

Tira o NOT, usa só

IS NULL
Criado 12 de fevereiro de 2017
Ultima resposta 13 de fev. de 2017
Respostas 35
Participantes 2