How do I know which Arraylist objects are being modified or deleted?

Asked

Viewed 608 times

4

I own a ArrayList that this preloaded with some objects of the type of my class Person, see:

ArrayList<Pessoa> listaDePessoas = new ArrayList<>();
listaDePessoas.add(new Pessoa("Joao", 29));
listaDePessoas.add(new Pessoa("Ana", 21));
listaDePessoas.add(new Pessoa("Maria", 25));

Let’s assume that at some point I alter one of these objects contained in the ArrayList listaDePessoas see:

listaDePessoas.get(0).setNome("Jao carlos");

And then in another moment I remove one of these objects contained in the ArrayList listaDePessoas see below:

listaDePessoas.remove(listaDePessoas.get(2));

I removed the object in the third position of the list.

Class structure Person:

public class Pessoa {    
    private String nome;
    public String getNome() { return nome; }
    public void setNome(String nome) { this.nome = nome; }
    private int idade;
    public int getIdade() { return idade; }
    public void setIdade(int idade) { this.idade = idade; }

    public Pessoa(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public Pessoa() { }
}

Question

So I’d like to know how I could know which objects are being modified and which ones are being removed from the list? So that in the future the modifications applied in these objects (amendment and exclusion) are applied elsewhere.

  • I’m not sure I understand what information you want to get from the API.

  • Check the modified ones I have no idea, but the deleted ones, it seems to me that the Arraylist itself reorders the Indice when an item is removed. See the method description remove(): Shifts any subsequent Elements to the left (subtracts one from their indices).

  • @diegofm did not know the method remove(int index) me returns the element removed however the method remove(Object o) returns true if the deleted element that was specified exists in the list.

  • @bigown need to get (a copy of the objects) the objects being removed and changed in the list.

  • So suddenly the only possibility I find is to have a class that manipulates the array and the creation/change of the Person objects. Both the array and the objects will be deprived of this class.

  • 2

    It would not be a case of implementing the Observer standard, to monitor changes?

  • @Denercarvalho can post the class Pessoa? I may be mistaken in your need, but it doesn’t seem to be the case with Observer, at least not with what is in the question. You need another object to need to be notified that something has changed in that?

  • Para que futuramente as modificações aplicadas nesses objetos (alteração e exclusão) sejam aplicadas em outro lugar. Where? You can give details?

  • @mustache I’ll edit here and put the class Pessoa

  • @Brunocost modifications will be applied to an XML file that contains several objects of the Person type.

  • @bigown yes need to be notified, to apply the modifications elsewhere.

  • @The question got a little wide :) Not that I think it needs to be closed. You can change this class Pessoa? Could implement the interface Clonable?

  • @bigown if the changes ñ greatly affect the class structure it be changed yes. The interface Clonable solucionaria? I even thought of adding the objects that are being deleted and changed in another list listaPessoasExcluidas and listaPessoasAlteradas but I don’t know if that would be ideal :P

  • To Clonable It is one of the steps, you can do without, but it complicates the use a lot. As it needs to be observable, you will have to put other things anyway, it will be "complicated", there is no way. But that’s it, without knowing what you need any solution is wrong, if you will create a list, if you will notify another class, if you will only return the object, all this requires different solutions. A [mcve] on something so wide and even not clear would help a lot. It is missing right requirements to answer. I do not close because I know you will tidy :)

  • @bigown I could add the whole context, but this would make the question too broad and also very difficult to test the example. I’ll check the interface Clonable and see what I can find. It’s okay to close the question, if the community thinks it should be closed I’ll also vote to close it ;)

Show 10 more comments

2 answers

5


Simplistic response

You can’t do this in Java.

Impractical response

You could instrumentalize Java classes so as to intercept the accesses and changes made to the objects.

Reasonable response

Implement an object access standard that allows you to reproduce actions performed.

This reminds me of the architectural pattern Event Sourcing, where the operations performed on the data are stored as events, it is then possible to reproduce, undo and browse the history of the system events.

Any change in the data must be made by a class that manages the data and will be represented by an object that contains the event data.

Example

I made a simple implementation based on the question code:

Class Pessoa

I changed it to be immutable because it’s good practice.

public class Pessoa {
    private final String nome;
    private final int idade;

    public Pessoa(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public String getNome() { return nome; }
    public int getIdade() { return idade; }

    @Override
    public String toString() { return nome + " / " + idade; }
}

Event interface

interface Event {
    void apply(List<Pessoa> lista);
}

Event to add a person

public class AddPessoaEvent implements Event {
    private final Pessoa p;

    public AddPessoaEvent(Pessoa p) {
        this.p = p;
    }

    @Override
    public void apply(List<Pessoa> lista) {
        System.out.println("* Adicionando " + p);
        lista.add(p);
    }

    @Override
    public String toString() { return "Add " + p.getNome(); }
}

Event to remove a person

public class RemovePessoaEvent implements Event {
    private final int index;

    public RemovePessoaEvent(int index) {
        this.index = index;
    }

    @Override
    public void apply(List<Pessoa> lista) {
        System.out.println("* Removendo " + index);
        lista.remove(index);
    }

    @Override
    public String toString() { return "Remove " + index; }
}

Event to update a person’s name

public class AtualizaNomePessoaEvent implements Event {
    private final int index;
    private final String nome;

    AtualizaNomePessoaEvent(int index, String nome) {
        this.index = index;
        this.nome = nome;
    }

    @Override
    public void apply(List<Pessoa> lista) {
        System.out.println("* Atualizando " + index + " com nome " + nome);
        lista.set(index, new Pessoa(nome, lista.get(index).idade));
    }

    @Override
    public String toString() {
        return "Atualiza " + index + " com nome " + nome;
    }
}

People List Manager Class

public class PessoasManager {
    private final List<Pessoa> listaDePessoas = new ArrayList<>();
    private final List<Event> eventos = new ArrayList<>();

    public void add(Pessoa p) {
        AddPessoaEvent e = new AddPessoaEvent(p);
        eventos.add(e);
        e.apply(listaDePessoas);
    }

    public void remove(int index) {
        RemovePessoaEvent e = new RemovePessoaEvent(index);
        eventos.add(e);
        e.apply(listaDePessoas);
    }

    public void atualizaNome(int index, String nome) {
        AtualizaNomePessoaEvent e = new AtualizaNomePessoaEvent(index, nome);
        eventos.add(e);
        e.apply(listaDePessoas);
    }

    public List<Pessoa> getListaDePessoas() { return listaDePessoas; }
    public List<Event> getEventos() { return eventos; }

    public void replay(List<Event> eventos) {
        for (Event e : eventos) {
            this.eventos.add(e);
            e.apply(listaDePessoas);
        }
    }
}

Using the classes

The main code to perform the operations mentioned in the question would be the following:

PessoasManager pm = new PessoasManager();
pm.add(new Pessoa("Joao", 29));
pm.add(new Pessoa("Ana", 21));
pm.add(new Pessoa("Maria", 25));
pm.atualizaNome(0, "Jao Carlos");
pm.remove(2);

Each person manager method creates the respective event and applies it to the people list. Like each method apply has a println, this would print on the console:

  • Adding John / 29

  • Adding Ana / 21

  • Adding Mary / 25

  • Updating 0 under the name Jao Carlos

  • Removing 2

Printing lists of people and events:

System.out.println("Pessoas " + pm.getListaDePessoas());
System.out.println("Eventos " + pm.getEventos());

We have the result:

People [Jao Carlos / 29, Ana / 21]

Events [Add Joao, Add Ana, Add Maria, Update 0 with name Jao Carlos, Remove 2]

So, since we have the events, we can apply them again in another list, creating another people manager and using the method replay:

PessoasManager pm2 = new PessoasManager();
pm2.replay(pm.getEventos());

The second line above will produce the same exact output on the referring console, as the same events will be applied again. And if you print the new list of people it will also be the same as the previous one.

Something interesting about this pattern is that you could replicate events in another data structure by creating another method analogous to apply. Instead of a list, it could be a connection to the bank, for example.

Disadvantages

The pattern, as applied above, can generate unnecessary events. Imagine a person’s name updated several times.

Therefore, if the history is not important to you, another alternative would be to store the initial list, create a copy of it, apply the changes and in the end generate a diff, that is, a list of differences between initial and final status, so as to perform the minimum number of operations possible when applying them to your other data source.

Another simpler alternative, when the list is small and there are no dependencies, is simply not trying to replicate the process, but replacing the target data with the new data. For example, I worked on a system a few years ago where the user could edit a table. Updating each field and record in the database according to the row and column of the screen was complicated, mainly because the user could arbitrarily include and exclude rows and columns. In this particular situation, we decided that the most viable solution at the time was simply to delete the existing records and recreate them all from the new values.

4

Since you know which object you want just make a copy of it before doing some operation. It is a basic technique, there is no "magic".

Altering

Pessoa pessoaAlterada = listaDePessoas.get(0).clone();
listaDePessoas.get(0).setNome("Jao carlos");

I did not make a single copy. As the type is by reference the copy would be from the reference, so pessoaAlterada would point to the same memory location where the object is pointing to the element in the list, then the change would be seen in that copy. The solution was to clone the object. That’s why I had to implement the interface Cloneable in class. It even has how to do in hand, but is not the ideal.

With cloning the copy will point to a new object with the same data as will be changed next. Being an independent object the change of the referenced object in the list will not affect the cloned object.

It is only important to understand that this is a shallow cloning, so all bits of the object will be copied, including the references contained in these objects. We conclude that the new object will point to the same objects that compose it that the original object is pointing to. If the original object changes these compositing objects, the cloned object will see the changes. To avoid this you would need a deep cloning that clones the entire object tree, making everything independent. You need to see if this is what you want.

If you want deep cloning, or the type of data (Pessoa) must have a method clone() to do it for you, or you’ll have to create an algorithm in the code to do all the cloning of the entire tree at hand. I didn’t do it because it doesn’t seem to be necessary in this case, but it could be if you put new attributes. Even the String which is a reference type has semantics of type by value and is immutable, so it doesn’t have this problem, an alteration would create a new object.

That kind of copy is dangerous if you don’t know what you’re doing.

I talk about shallow copy and deep copy on Class copy in C#. It is very similar to Java.

Removal

Pessoa pessoaRemovida = listaDePessoas.remove(2);

Note that you don’t need to catch the object if you already know the index. And this overload of the method returns the object itself (the reference), so you don’t even need to make a previous copy.

To remove do not need to make any copy of the object itself, after all you removed it from the list, so it will no longer be accessible in the list, so can use it itself. Of course, if you have any other reason to clone the object you can do as well, like the previous example.

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Notifying

It’s not clear yet whether or not you need the notification, it’s possible to do this in the class, but you’d need to see how it’s consumed. You have some questions on the subject:

The basic idea is to put in Pessoa one array:

private List<Observer> observers = new ArrayList<Observer>();

A way to sign:

public void subscribe(Observer observer) {
    observers.add(observer);       
}

The notifier:

public void notifyAllObservers() {
    for (Observer observer : observers) {
        observer.update(this);
    }
}

When the property is modified you need to trigger the notification:

public void setNome(String nome) {
    this.nome = nome;
    notifyAllObservers();
}

The interface Observer it would be something like that:

public interface ObserverPessoa {
    public void update(Pessoa pessoa);
}

And consumers would be something like that:

public class Consumidor extends Observer {
    @Override
    public void update(Pessoa pessoa) {
        //faz o que quiser aqui 
    }
}

I put in the Github for future reference.

It’s an idea, there are several others to do. I did this without thinking too much, without knowing the real case. It has advantages and disadvantages. I just posted it to give you an idea, doesn’t mean I’d do it.

If you need the notification in the list you need to see where the list is and assemble something similar to the one above in this class. May have general or specialised notifications.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.