4

Consider this code

String[] colours1 = new String[]{"red", "green", "blue"};
String[] colours2 = new String[]{"red", "yellow", "blue"};
String[] colours3 = new String[]{"red", "green", "blue"};

List<String[]> distinct = Stream.of(colours1, colours2, colours3)
        .distinct() // Do something here to compare arrays better
        .collect(Collectors.toList());

I want the distinct list to contain only 2 elements colours1 and colours2 (as 1 and 3 are equivalent). However because the stream distinct() method performs equals comparison it still contains all 3 colours arrays. I want a custom distinct function where you can provide a comparator. In this case Objects#deepEquals would be sufficient. Is there a easy way to achieve this?

James Mudd
  • 1,816
  • 1
  • 20
  • 25

1 Answers1

5

Wrap the arrays in lists, use distinct(), unwrap them again:

Stream.of(colours1, colours2, colours3)
    .map(Arrays::asList)
    .distinct()
    .map(list -> list.toArray(new String[0]))
    .collect(toList())

This works because List.equals considers two lists equal if they have the same contents (the same number of elements, and equality of the elements at corresponding positions in the two lists).

However, this doesn't return the same array instances. If you need (some of) the same array instances in the final list, you can do something like so:

class MyArrayList<T> extends AbstractList<T> {
  final T[] arr;

  MyArrayList(T[] arr) { this.arr = arr; }
  @Override public int size() { return arr.length; }
  @Override public T get(int i) { return arr[i]; }
}

List<String> distinct =
    Stream.of(colours1, colours2, colours3)
        .map(MyArrayList::new)
        .distinct()
        .map(el -> el.arr)
        .collect(toList());
Andy Turner
  • 137,514
  • 11
  • 162
  • 243