title: RecyclerView Items Duplicating When Search Filtering But Only When Using Object ArrayList Instead Of String ArrayList
body: <p>I have an empty RecyclerView which populates with different Strings as I enter the characters in the SearchView. When I use a String ArrayList to pass it on the setItems method of the Adapter the application works as intended but I wish to do it with an object ArrayList instead so I can choose on the onBindViewHolder method which data I want to print on the RecyclerView by using the objects getters methods but the RecyclerView duplicates the items only when entering the first charcters. I´m using the same logic as when using a String ArrayList but I can´t manage to make it work properly. Any help would be much appreciated, I´m kinda desperated now as I don´t understand why it repeats the items when in the end the setText() methods uses Strings anyway...</p>
  <p>This are the Adapter and the SearchActivity when using String ArrayList:</p>
  <pre><code>public class SearchViewAdapter extends RecyclerView.Adapter&lt;RecyclerView.ViewHolder&gt; {
  
      private Context context;
      ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
  
      public SearchViewAdapter(Context ctx) {
          this.context = ctx;
      }
  
      public void setItems(ArrayList&lt;String&gt; queryList) {
          this.list.addAll(queryList);
      }
  
      @NonNull
      @Override
      public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          View view = LayoutInflater.from(context).inflate(R.layout.search_view_item, parent, false);
          return new SearchViewAdapterViewHolder(view);
      }
  
      @Override
      public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint(&quot;RecyclerView&quot;) int position) {
  
          SearchViewAdapterViewHolder searchViewAlbumHolder = (SearchViewAdapterViewHolder) holder;
          String string = list.get(position);
  
          searchViewAlbumHolder.queryText.setText(string);
  
          holder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  Bundle bundle = new Bundle();
              }
          });
      }
  
      @Override
      public int getItemCount() {
          return list.size();
      }
  }
  </code></pre>
  <p>SearchActivity:</p>
  <pre><code>public class SearchActivity extends AppCompatActivity implements Filterable {
  
      ActivitySearchBinding binding;
      SearchView searchView;
      SearchViewAdapter searchViewAdapter;
      RecyclerView recyclerView;
      ArrayList&lt;String&gt; fileredList;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          binding = ActivitySearchBinding.inflate(getLayoutInflater());
          View view = binding.getRoot();
          setContentView(view);
  
          getSupportActionBar().hide(); //escondemos la action bar
  
          searchView = binding.searhViewID;
          searchView.setIconified(false); //desiconificamos la lupa para poder hacer focus en el searchview y poder abrir el teclado automáticamente al iniciar la activity
          searchView.requestFocus();
          searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); //para cambiar la lupa del teclado por un tick
  
          recyclerView = binding.searchRecyclerViewID;
          recyclerView.setHasFixedSize(true);
          LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL, false);
          recyclerView.setLayoutManager(manager);
          searchViewAdapter = new SearchViewAdapter(getApplicationContext());
          recyclerView.setAdapter(searchViewAdapter);
      }
  
      @Override
      protected void onStart() {
          super.onStart();
  
          searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
              @Override
              public boolean onQueryTextSubmit(String s) {
                  return false;
              }
  
              @Override
              public boolean onQueryTextChange(String charSequence) {
                  getFilter().filter(charSequence);
                  return false;
              }
          });
      }
  
      @Override
      public Filter getFilter() {
          return filtrado; //devuelve el objeto filter que creamos abajo
      }
  
      private Filter filtrado = new Filter() {
          @Override
          protected FilterResults performFiltering(CharSequence charSequence) {
  
              fileredList = new ArrayList&lt;&gt;();
  
              if (charSequence == null || charSequence.length() == 0) {
                  fileredList.clear(); //si no existe ningún patrón limpiamos y vaciamos la lista
              } else {
                  String filterPattern = charSequence.toString().toLowerCase().trim();
                  DatabaseReference dbr = FirebaseDatabase.getInstance().getReference();
  
                  dbr.child(&quot;artists&quot;).addValueEventListener(new ValueEventListener() {
                      @Override
                      public void onDataChange(@NonNull DataSnapshot snapshot) {
  
                          for (DataSnapshot data : snapshot.getChildren()) {
                              Artist artist = data.getValue(Artist.class);
  
                              if (artist.getName().toLowerCase().contains(filterPattern)) {
                                  fileredList.add(artist.getName());
                              } else {
                                  ArrayList&lt;Album&gt; albums = artist.getAlbums();
                                  for (Album album : albums) {
                                      if (album.getAlbumTitle().toLowerCase().contains(filterPattern) || album.getYear().toLowerCase().contains(filterPattern) || album.getGenre().toLowerCase().contains(filterPattern)) {
                                          fileredList.add(album.getAlbumTitle() + &quot; &quot; + artist.getName());
                                      } else {
                                          ArrayList&lt;Song&gt; songs = album.getSongs();
                                          for (Song song : songs) {
                                              if (song.getSongTitle().toLowerCase().contains(filterPattern)) {
                                                  fileredList.add(song.getSongTitle() + &quot; &quot; + artist.getName());
                                              }
                                          }
                                      }
                                  }
                              }
                          }
                          searchViewAdapter.setItems(fileredList);
                          searchViewAdapter.notifyDataSetChanged();
                      }
  
                      @Override
                      public void onCancelled(@NonNull DatabaseError error) {
  
                      }
                  });
              }
              FilterResults filterResults = new FilterResults();
              filterResults.values = fileredList;
  
              return filterResults;
          }
  
          @Override
          protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
              searchViewAdapter.list.clear(); //limpiamos la lista actual
              searchViewAdapter.list.addAll((ArrayList) filterResults.values);
              searchViewAdapter.notifyDataSetChanged(); //notificamos los cambios realizados
          }
      };
  }
  </code></pre>
  <p>And these are the Adapter and the SearchActivity when using Object &quot;Artist&quot; ArrayList:</p>
  <pre><code>public class SearchViewAdapter extends RecyclerView.Adapter&lt;RecyclerView.ViewHolder&gt; {
  
      private Context context;
      ArrayList&lt;Artist&gt; list = new ArrayList&lt;&gt;();
      int queryCombination;
      int albumPosition;
      int songPosition;
  
      public SearchViewAdapter(Context ctx) {
          this.context = ctx;
      }
  
      public void setItems(ArrayList&lt;Artist&gt; queryList, int queryCombination, int albumPosition, int songPosition) {
          this.list.addAll(queryList);
          this.queryCombination = queryCombination;
          this.albumPosition = albumPosition;
          this.songPosition = songPosition;
      }
  
      @NonNull
      @Override
      public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          View view = LayoutInflater.from(context).inflate(R.layout.search_view_item, parent, false);
          return new SearchViewAdapterViewHolder(view);
      }
  
      @Override
      public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint(&quot;RecyclerView&quot;) int position) {
  
          SearchViewAdapterViewHolder searchViewAlbumHolder = (SearchViewAdapterViewHolder) holder;
          Artist artist = list.get(position);
  
          switch (queryCombination) {
              case 1: {
                  searchViewAlbumHolder.queryText.setText(artist.getName());
                  break;
              }
              case 2: {
                  searchViewAlbumHolder.queryText.setText(artist.getAlbums().get(albumPosition).getAlbumTitle() + &quot; &quot; + artist.getName());
                  break;
              }
              case 3: {
                  searchViewAlbumHolder.queryText.setText(artist.getAlbums().get(albumPosition).getSongs().get(songPosition).getSongTitle() + &quot; &quot; + artist.getName());
              }
          }
  
          holder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  Bundle bundle = new Bundle();
                  bundle.putSerializable(&quot;artist&quot;, artist);
                  bundle.putInt(&quot;queryCombination&quot;, queryCombination);
                  Intent intent = new Intent(context, prueba.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// addFlags para que no me de error al pasar a la nueva activity
                  intent.putExtras(bundle);
                  context.startActivity(intent);
              }
          });
      }
  
      @Override
      public int getItemCount() {
          return list.size();
      }
  }
  </code></pre>
  <p>SearchActivity:</p>
  <pre><code>public class SearchActivity extends AppCompatActivity implements Filterable {
  
      ActivitySearchBinding binding;
      SearchView searchView;
      SearchViewAdapter searchViewAdapter;
      RecyclerView recyclerView;
      ArrayList&lt;Artist&gt; fileredList;
      int queryCombination;
      int albumPosition;
      int songPosition;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          binding = ActivitySearchBinding.inflate(getLayoutInflater());
          View view = binding.getRoot();
          setContentView(view);
  
          getSupportActionBar().hide(); //escondemos la action bar
  
          searchView = binding.searhViewID;
          searchView.setIconified(false); //desiconificamos la lupa para poder hacer focus en el searchview y poder abrir el teclado automáticamente al iniciar la activity
          searchView.requestFocus();
          searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); //para cambiar la lupa del teclado por un tick
  
          recyclerView = binding.searchRecyclerViewID;
          recyclerView.setHasFixedSize(true);
          LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL, false);
          recyclerView.setLayoutManager(manager);
          searchViewAdapter = new SearchViewAdapter(getApplicationContext());
          recyclerView.setAdapter(searchViewAdapter);
      }
  
      @Override
      protected void onStart() {
          super.onStart();
  
          searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
              @Override
              public boolean onQueryTextSubmit(String s) {
                  return false;
              }
  
              @Override
              public boolean onQueryTextChange(String charSequence) {
                  getFilter().filter(charSequence);
                  return false;
              }
          });
      }
  
      @Override
      public Filter getFilter() {
          return filtrado; //devuelve el objeto filter que creamos abajo
      }
  
      private Filter filtrado = new Filter() {
          @Override
          protected FilterResults performFiltering(CharSequence charSequence) {
  
  
              if (charSequence == null || charSequence.length() == 0) {
                  fileredList.clear(); //si no existe ningún patrón limpiamos y vaciamos la lista
              } else {
                  String filterPattern = charSequence.toString().toLowerCase().trim();
                  DatabaseReference dbr = FirebaseDatabase.getInstance().getReference();
  
                  fileredList = new ArrayList&lt;&gt;();
  
                  dbr.child(&quot;artists&quot;).addValueEventListener(new ValueEventListener() {
                      @Override
                      public void onDataChange(@NonNull DataSnapshot snapshot) {
  
                          for (DataSnapshot data : snapshot.getChildren()) {
                              Artist artist = data.getValue(Artist.class);
                              if (artist.getName().toLowerCase().contains(filterPattern)) {
                                  queryCombination = 1;
                                  fileredList.add(artist);
                              } else {
                                  ArrayList&lt;Album&gt; albums = artist.getAlbums();
                                  for (Album album : albums) {
                                      if (album.getAlbumTitle().toLowerCase().contains(filterPattern)) {
                                          albumPosition = albums.indexOf(album);
                                          queryCombination = 2;
                                          fileredList.add(artist);
                                      } else {
                                          ArrayList&lt;Song&gt; songs = album.getSongs();
                                          for (Song song : songs) {
                                              if (song.getSongTitle().toLowerCase().contains(filterPattern)) {
                                                  albumPosition = albums.indexOf(album);
                                                  songPosition = songs.indexOf(song);
                                                  queryCombination = 3;
                                                  fileredList.add(artist);
                                              }
                                          }
                                      }
                                  }
                              }
                          }
                          searchViewAdapter.setItems(fileredList, queryCombination, albumPosition, songPosition);
                          searchViewAdapter.notifyDataSetChanged();
                      }
  
                      @Override
                      public void onCancelled(@NonNull DatabaseError error) {
  
                      }
                  });
              }
              FilterResults filterResults = new FilterResults();
              filterResults.values = fileredList;
  
              return filterResults;
          }
  
          @Override
          protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
              searchViewAdapter.list.clear(); //limpiamos la lista actual
              searchViewAdapter.list.addAll((ArrayList) filterResults.values);
              searchViewAdapter.notifyDataSetChanged(); //notificamos los cambios realizados
          }
      };
  }
  </code></pre>