programing

null 값이있는 비교기

nasanasas 2021. 1. 6. 08:24
반응형

null 값이있는 비교기


좌표 사이의 거리에 따라 주소 목록을 정렬하는 코드가 있습니다. 이것은 custom comparator와 함께 collections.sort를 통해 이루어집니다.

그러나 때때로 좌표가없는 주소가 목록에 있으면 NullPointerException이 발생합니다. 이 문제를 해결하기위한 나의 초기 아이디어는 좌표 중 적어도 하나가 null 인 주소에 대한 거리로 비교기가 0을 반환하도록하는 것이 었습니다. 이로 인해 목록의 '유효한'요소가 순서가 손상 될 수 있습니다.

그래서 비교기에서 null 데이터에 대해 '0'값을 반환 하거나이 문제를 해결할 수있는 더 깨끗한 방법이 있습니까?


마치 null무한히 멀리 떨어져있는 것처럼 처리하십시오 . 그러므로:

  • comp(1234, null) == -1
  • comp(null, null) == 0
  • comp(null, 1234) == 1

이를 통해 일관된 주문을 얻을 수 있습니다.


Willi Schönborn의 답변을 확장하기 위해 Google 컬렉션 이 정확히 여기에서 추구하는 것이라고 말하려고 여기에 왔습니다 .

일반적인 경우에는 Comparatornull을 무시하도록 직접 작성한 다음 (null이 아닌 것으로 가정하여 중요한 논리에 집중할 수 있음) Ordering 을 사용하여 null을 처리 할 수 ​​있습니다.

Collections.sort(addresses, Ordering.from(new AddressComparator()).nullsLast());

그러나 귀하의 경우에는 정렬에 사용되는 주소 (좌표) 내의 데이터입니다. 이 경우 google-collections가 훨씬 유용합니다. 따라서 다음과 같은 것이있을 수 있습니다.

// Seems verbose at first glance, but you'll probably find yourself reusing 
// this a lot and it will pay off quickly.
private static final Function<Address, Coordinates> ADDRESS_TO_COORDINATES = 
  new Function<Address, Coordinates>() {
      public Coordinates apply(Address in) {
          return in.getCoordinates();
      }
  };

private static final Comparator<Coordinates> COORDINATE_SORTER = .... // existing

그런 다음 정렬하려는 경우 :

Collections.sort(addresses,
    Ordering.from(COORDINATE_SORTER)
            .nullsLast()
            .onResultOf(ADDRESS_TO_COORDINATES));

Google 컬렉션의 힘이 실제로 효과를보기 시작하는 곳입니다.


제가 생각하는 것은 null좌표 를 "좋은"것으로 만들기 위해하려는 모든 작업 은 균열을 덧씌우는 것입니다. 정말 필요한 것은 가짜 null좌표를 주입하는 버그를 찾아 수정하는 것 입니다.

내 경험상 NPE 버그의 침입은 다음과 같은 나쁜 코딩 습관으로 인해 자주 발생합니다.

  • 입력 매개 변수의 부적절한 검증,
  • null빈 배열 또는 컬렉션 생성을 방지하기 위해 사용 ,
  • null예외가 발생해야 할 때 반환 , 또는
  • null더 나은 솔루션이있을 때 "가치 없음"을 나타내는 데 사용 합니다.

( "값 없음"문제에 대한 더 나은 솔루션은 일반적으로 이를 표현할 필요 가 없도록 코드를 다시 작성 하거나 대신 null이 아닌 값을 사용하는 것입니다 (예 : 빈 문자열, 특수 인스턴스, 예약 된 값). 항상 더 나은 솔루션을 찾을 수는 없지만 자주 찾을 수 있습니다.)

이것이 응용 프로그램에 해당하는 경우 NPE를 숨기는 방법을 생각하기보다는 코드 문제를 근절하는 데 시간을 투자해야합니다.


내 솔루션 (여기를 찾는 사람에게 유용 할 수 있음)은 null 값이 0이 아니라 가능한 최대 값 (예 : Integer.MAX_VALUE)으로 대체되는 비교 정상을 수행하는 것입니다. 자체적으로 0 인 값이있는 경우 0을 반환하는 것은 일관성이 없습니다. 올바른 예는 다음과 같습니다.

        public int compare(YourObject lhs, YourObject rhs) {
            Integer l = Integer.MAX_VALUE;
            Integer r = Integer.MAX_VALUE;
            if (lhs != null) {
                l = lhs.giveMeSomeMeasure();
            }
            if (rhs != null) {
                r = rhs.giveMeSomeMeasure();
            }
            return l.compareTo(r);
        }

I just wanted to add that you don't necessary need the max value for integer. It depends of what your giveMeSomeMeasure() method can return. If for example you compare Celsius degrees for weather, you can set l and r to -300 or +300, depending where you want to set the null objects - to the head or the tail of the list.


If you are using Java 8, you have 2 new static methods in the Comparator class, which come in handy:

public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)

The comparison will be null safe and you can choose where to place the null values in the sorted sequence.

The following example:

List<String> monkeyBusiness = Arrays.asList("Chimp", "eat", "sleep", "", null, "banana",
            "throw banana peel", null, "smile", "run");
Comparator<? super String> comparator = (a, b) -> a.compareTo(b);
monkeyBusiness.stream().sorted(Comparator.nullsFirst(comparator))
            .forEach(x -> System.out.print("[" + x + "] "));

will print: [null] [null] [] [Chimp] [banana] [eat] [run] [sleep] [smile] [throw banana peel]


You probably dont want to return 0 as that implies the addresses are equidistant and you really dont know. This is quite a classic problem where you are trying to deal with bad input data. I dont think its the responsibility of the comparator to try and determin how far the address is in realtive terms when you dont know the distance. I would remove those addresses from the list before sorting.

The hack would be to move them to the bottom of the list (but thats ugly !)


Instead of looking at this like it's a technical problem with the comparator, it's better to take a look at the requirements again: what you're really trying to do here, what are you going to do with this sorted list?

  • If you are trying to sort them to show the most relevant solutions first to a user, it might be a good idea to put the unknown locations last, so treat it like infinity (returning 0/-1/1 depending on which of them is null).
  • If you are going to use this result to draw some graph or do some other calculations that depend on them really being sorted by their distance, then the nulls probably shouldn't have been in there anyways (so either remove them first, or throw an exception if at that point there actually weren't supposed to be any addresses with a null location).

As you already realize, always returning 0 when one of them is null is not a good idea here; it can corrupt the result indeed. But what you should do instead depends on what you need, not on what others usually do/need. How your program behaves with addresses that don't have a location (so what the user is going to see) should not depend on some technical detail like what the "best practice" for comparators is. (To me, asking what the "best practice" is here, sounds like asking what the "best requirements" are).


No, there is no cleaner way. Perhaps:

  • if the coordinates of both compared objects are null, return 0
  • if the coordinates of one of the objects are null, return -1 / 1 (depending on whether it's the first or the second argument)

But more importantly - try to get rid of / fill in the missing coordinates, or, better: don't put addresses with missing coordinates in the list.

Actually, not putting them in the list is the most logical behaviour. If you put them in the list, the result won't be actually ordered by distance.

You may create another list, containing the addresses with missing coordinates, and make it clear to whoever needs that information (end-user, API user), that the first list only contains addresses with the needed data, while the second list contains addresses that lack required information.


I personally hate dealing with special null cases everywhere in my comparators so i was looking for a cleaner soluation and finally found google collections. Their Ordering is just awesome. They support compound comparators, offer to sort nulls to the top and to the end and allow to run certain functions before comparing. Writing comparators has never been so easy. You should give it a try.

ReferenceURL : https://stackoverflow.com/questions/2401606/comparator-with-null-values

반응형