ماذا تفعل مع الحقول الفارغة في المقارنة ()؟

StackOverflow https://stackoverflow.com/questions/128042

  •  02-07-2019
  •  | 
  •  

سؤال

في Java، أستخدم فئة يمكن أن تكون فيها بعض الحقول null.على سبيل المثال:

class Foo {
    String bar;
    //....
}

أريد أن أكتب BarComparator لهذا الفصل،

    private static class BarComparator
            implements Comparator<Foo> {
        public int compare( final Foo o1, final Foo o2 )
        {
            // Implementation goes here
        }
    }

هل هناك طريقة قياسية للتعامل مع حقيقة أن أيًا من o1, o2, o1.bar, o2.bar يمكن ان يكون null, ، دون كتابة الكثير من المتداخلة if...else?

هتافات!

هل كانت مفيدة؟

المحلول

أعتقد أنه يمكنك التفاف المكالمة إلى طريقة مقارنة الحقل بطريقة ثابتة صغيرة لفرز القيم الخالية العالية أو المنخفضة:

static <T extends Comparable<T>> int cp(T a, T b) {
     return
         a==null ?
         (b==null ? 0 : Integer.MIN_VALUE) :
         (b==null ? Integer.MAX_VALUE : a.compareTo(b));
}

الاستخدام البسيط (الحقول المتعددة كما تفعل عادةً):

public int compare( final Foo o1, final Foo o2 ) {
    return cp(o1.field, o2.field);
}

نصائح أخرى

شكرا على الردود!تبدو الطريقة العامة ومقارنات Google مثيرة للاهتمام.

ووجدت أن هناك NullComparator في ال مجموعات أباتشي كومنز (الذي نستخدمه حاليًا):

private static class BarComparator
        implements Comparator<Foo>
{
    public int compare( final Foo o1, final Foo o2 )
    {
        // o1.bar & o2.bar nulleness is taken care of by the NullComparator.
        // Easy to extend to more fields.
        return NULL_COMPARATOR.compare(o1.bar, o2.bar);
    }

    private final static NullComparator NULL_COMPARATOR =
                                            new NullComparator(false);
}

ملحوظة:ركزت على bar الحقل هنا لإبقائه في صلب الموضوع.

يعتمد ذلك على ما إذا كنت تعتبر الإدخال الفارغ بمثابة قيمة سلسلة صالحة تستحق المقارنة.خالية < أو > "تفاحة".الشيء الوحيد الذي يمكنني قوله على وجه اليقين هو أن null == null.إذا كان بإمكانك تحديد المكان الذي يناسبه null في الترتيب، فيمكنك كتابة الكود بشكل مناسب.

في هذه الحالة، قد أختار طرح NullPointerExcpetion أو IllegalArgumentException ومحاولة التعامل مع القيمة الخالية على مستوى أعلى من خلال عدم وضعها في المقارنة في المقام الأول.

يمكنك كتابة المقارنة الخاصة بك لذلك.لنفترض أن لديك فئة شخص باسم سلسلة كحقل خاص.طريقة getName() وsetName() للوصول إلى اسم الحقل.يوجد أدناه المقارنة لفئة الأشخاص.

    Collections.sort(list, new Comparator<Person>() {
        @Override
        public int compare(Person a, Person b) {
            if (a == null) {
                if (b == null) {
                    return 0;
                }
                return -1;
            } else if (b == null) {
                return 1;
            }
            return a.getName().compareTo(b.getName());
        }
    });

تحديث:

اعتبارًا من Java 8، يمكنك استخدام واجهة برمجة التطبيقات أدناه للقائمة.

// Push nulls at the end of List
Collections.sort(subjects1, Comparator.nullsLast(String::compareTo));

// Push nulls at the beginning of List
Collections.sort(subjects1, Comparator.nullsFirst(String::compareTo));

الشيء الأساسي هنا هو معرفة الطريقة التي تريد أن يتم بها التعامل مع القيم الخالية.بعض الخيارات هي:أ) تفترض أن القيم الخالية تأتي قبل كافة الكائنات الأخرى في ترتيب الفرز ب) تفترض أن القيم الخالية تأتي بعد جميع الكائنات الأخرى في ترتيب الفرز ج) تعامل مع القيم الخالية على أنها مكافئة لبعض القيمة الافتراضية د) تعامل مع القيم الخالية كشروط خطأ.أي واحد تختاره سيعتمد كليًا على التطبيق الذي تعمل عليه.

في الحالة الأخيرة بالطبع قمت برمي استثناء.بالنسبة للآخرين، تحتاج إلى حالة if/else رباعية الاتجاهات (حوالي ثلاث دقائق من الترميز، حيث تكون قد حددت ما تريد أن تكون عليه النتائج).

إذا كنت تستخدم مجموعات Google، فقد تجد مقارنات فئة مفيدة.إذا كان لديه طرق مساعدة لترتيب القيم الخالية كأكبر أو أصغر العناصر في المجموعة.يمكنك استخدام مقارنات مركبة للمساعدة في تقليل كمية التعليمات البرمجية.

هناك أيضا الطبقة org.springframework.util.comparator.NullSafeComparator في إطار الربيع يمكنك استخدامه.

مثال (جافا 8):

SortedSet<Foo> foos = new TreeSet<>( ( o1, o2 ) -> {
        return new NullSafeComparator<>( String::compareTo, true ).compare( o1.getBar(), o2.getBar() );
    } );

    foos.add( new Foo(null) );
    foos.add( new Foo("zzz") );
    foos.add( new Foo("aaa") );

    foos.stream().forEach( System.out::println );

سيؤدي هذا إلى طباعة:

Foo{bar='null'}
Foo{bar='aaa'}
Foo{bar='zzz'}

يبدو لي أنه لا توجد طريقة للقيام بذلك، ولكن على أي حال فإن الكود ليس طويلاً.

يجب ألا تستخدم NullComparator بالطريقة التي تستخدمها - فأنت تقوم بإنشاء مثيل جديد للفئة لكل عملية مقارنة، وإذا كان على سبيل المثال.أنت تقوم بفرز قائمة تحتوي على 1000 إدخال، والتي ستكون 1000 * log2(1000) كائنات غير ضرورية تمامًا.يمكن أن يصبح هذا مشكلة بسرعة.

إما أن تصنفها فرعيًا، أو تفوضها، أو ببساطة قم بتنفيذ التحقق الفارغ الخاص بك - فالأمر ليس بهذا التعقيد حقًا:

private static class BarComparator
        implements Comparator<Foo> {
    private NullComparator delegate = new NullComparator(false);

    public int compare( final Foo o1, final Foo o2 )
    {
        return delegate.compare(o1.bar, o2.bar);
    }
}

اعتبار العميل بمثابة POJO. إجابتي ستكون:

Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId()));

أو

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));

أعتقد أن بيانات العودة المبكرة ستكون البديل الآخر للكثير من ifs

على سبيل المثال

if(o1==null) return x;
if(o2==null) return x;
if(o1.getBar()==null) return x;
if(o2.getBar()==null) return x;

// No null checks needed from this point.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top