วิธีเปรียบเทียบวัตถุด้วยหลาย ๆ ฟิลด์


237

สมมติว่าคุณมีวัตถุบางอย่างที่มีหลายฟิลด์สามารถเปรียบเทียบได้โดย:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

ดังนั้นในตัวอย่างนี้เมื่อคุณถามว่า:

a.compareTo(b) > 0

คุณอาจจะถามว่านามสกุลมาก่อน b หรือไม่หรือว่าเก่ากว่า b เป็นต้น

อะไรคือวิธีที่สะอาดที่สุดในการเปิดใช้การเปรียบเทียบหลายอย่างระหว่างวัตถุประเภทนี้โดยไม่เพิ่มความยุ่งเหยิงหรือค่าใช้จ่ายที่ไม่จำเป็น?

  • java.lang.Comparable อินเตอร์เฟสอนุญาตให้ทำการเปรียบเทียบโดยหนึ่งฟิลด์เท่านั้น
  • การเพิ่มวิธีการเปรียบเทียบจำนวนมาก (เช่นcompareByFirstName(), compareByAge()ฯลฯ ... ) มีความยุ่งเหยิงในความคิดของฉัน

ดังนั้นวิธีที่ดีที่สุดที่จะไปเกี่ยวกับเรื่องนี้คืออะไร?


3
ทำไมถึงเป็น cw มันเป็นคำถามการเขียนโปรแกรมที่ถูกต้องสมบูรณ์
Elie

2
คุณทราบหรือไม่ว่า Comparable อนุญาตให้ทำการเปรียบเทียบตามฟิลด์ได้มากเท่าที่คุณต้องการ
DJClayworth

คำตอบ:


81

คุณสามารถใช้วัตถุComparatorที่เปรียบเทียบสองPersonวัตถุและคุณสามารถตรวจสอบฟิลด์ได้มากเท่าที่คุณต้องการ คุณสามารถใส่ตัวแปรลงในตัวเปรียบเทียบของคุณซึ่งจะบอกให้ฟิลด์ใดให้เปรียบเทียบแม้ว่ามันอาจจะง่ายกว่าที่จะเขียนตัวเปรียบเทียบหลายตัว


5
ฉันชอบไอเดียที่จะใช้ Comparator เพียงตัวเดียว ฉันไม่คิดว่าคำตอบนี้ผิด แต่ใครก็ตามที่อ่านควรตรวจสอบคำตอบของ Steve Kuo ด้านล่างอย่างแน่นอน
Felipe Leão

ตัวเปรียบเทียบหลายตัวก็ต่อเมื่อคุณต้องการวิธีการเปรียบเทียบที่แตกต่างกันซึ่งไม่ใช่ฟังก์ชั่นของตัวข้อมูลเอง - นั่นคือบางครั้งคุณต้องการเปรียบเทียบตามชื่อเวลาอื่น ๆ ตามอายุ ฯลฯ หากต้องการเปรียบเทียบด้วยหลาย ๆ เขตข้อมูลในเวลาเดียวกัน จะมีความจำเป็น
Elie

399

ด้วย Java 8:

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

หากคุณมีวิธีการเข้าถึง:

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

ถ้าคลาสดำเนินการ Comparable แล้วตัวเปรียบเทียบดังกล่าวอาจถูกใช้ใน method comparTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}

5
โดยเฉพาะการคัดเลือกนักแสดง(Person p)เป็นสิ่งสำคัญสำหรับการเปรียบเทียบที่ถูกล่ามโซ่
Membersound

5
มันมีประสิทธิภาพแค่ไหนเมื่อเปรียบเทียบวัตถุจำนวนมากเช่นเมื่อเรียงลำดับ? ต้องสร้างComparatorอินสแตนซ์ใหม่ในทุกการโทรหรือไม่?
jjurm

4
ฉันได้รับ NullPointerException เมื่อหนึ่งในสาขาที่ฉันเปรียบเทียบเป็นโมฆะเช่นสตริง มีอยู่เพื่อเก็บรูปแบบการเปรียบเทียบ แต่อนุญาตให้ปลอดภัยหรือไม่
rveach

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- ตัวเลือกฟิลด์แรกจากนั้นเปรียบเทียบ
gavenkoa

2
@jjurm เมื่อคุณใช้compareToดังที่แสดงไว้ด้านบนComparatorจะถูกสร้างขึ้นทุกครั้งที่มีการเรียกใช้เมธอด คุณสามารถป้องกันสิ่งนี้ได้ด้วยการจัดเก็บเครื่องมือเปรียบเทียบลงในฟิลด์สุดท้ายคงที่ส่วนตัว
แกนดัล์ฟ

165

Comparable <Person>คุณควรใช้ สมมติว่าเขตข้อมูลทั้งหมดจะไม่เป็นโมฆะ (เพราะความเรียบง่าย) อายุนั้นเป็น int และเปรียบเทียบการจัดอันดับเป็นครั้งแรกที่ผ่านมาอายุcompareToวิธีการที่ค่อนข้างง่าย:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

10
ถ้าคุณใช้ <Person> Comparable แล้วเมธอดคือ comparTo (Person p) .. ดูเหมือนว่าคำตอบนี้ถูกนำมาผสมกับวิธีเปรียบเทียบ <T o1, T o2> เปรียบเทียบ
Mike

5
ไม่แนะนำ ใช้เครื่องมือเปรียบเทียบเมื่อคุณมีหลายฟิลด์
indika

1
นั่นเป็นทางออกที่ดีที่สุดในขณะนี้ (ดีกว่าผู้เปรียบเทียบมากกว่า)
Vasile Surdu

4
@indika ฉันอยากรู้: ทำไมถึงไม่แนะนำเช่นนี้? เปรียบเทียบกับการใช้มากกว่าหนึ่งคุณสมบัติที่ดูเหมือนว่าดีกับฉัน
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, ถ้าคุณใช้การเปรียบเทียบแล้วการเรียงลำดับลอจิกจะต้องอยู่ในคลาสเดียวกับที่วัตถุถูกเรียงลำดับดังนั้นจึงเรียกว่าการเรียงลำดับตามธรรมชาติของวัตถุ ด้วยการใช้ Comparator คุณสามารถเขียนตรรกะการเรียงลำดับที่กำหนดเองนอกคลาสบุคคล หากคุณต้องการเปรียบเทียบวัตถุบุคคลโดยใช้ชื่อหรือนามสกุลคุณไม่สามารถใช้ตรรกะนี้ได้ คุณต้องเขียนอีกครั้ง
indika

78

(จากวิธีเรียงลำดับรายการของวัตถุใน Java ขึ้นอยู่กับหลายสาขา )

รหัสการทำงานในส่วนสำคัญนี้

ใช้ Java 8 lambda's (เพิ่ม 10 เมษายน 2019)

Java 8 แก้ปัญหานี้ได้ดีโดยแลมบ์ดา (แม้ว่า Guava และ Apache Commons อาจยังให้ความยืดหยุ่นมากกว่า):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

ขอขอบคุณที่ @ gaoagong ของคำตอบด้านล่าง

ยุ่งและซับซ้อน: จัดเรียงด้วยมือ

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

สิ่งนี้ต้องการการพิมพ์การบำรุงรักษาและข้อผิดพลาดมากมาย

วิธีการไตร่ตรอง: การเรียงลำดับด้วย BeanComparator

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

เห็นได้ชัดว่านี่คือรัดกุม แต่มีข้อผิดพลาดมากขึ้นในขณะที่คุณสูญเสียการอ้างอิงโดยตรงไปยังเขตข้อมูลโดยใช้ Strings แทน (ไม่มี typesafety, refactorings อัตโนมัติ) ตอนนี้ถ้ามีการเปลี่ยนชื่อฟิลด์คอมไพเลอร์จะไม่รายงานปัญหา นอกจากนี้เนื่องจากวิธีนี้ใช้การสะท้อนการเรียงลำดับจึงช้ากว่ามาก

การเดินทาง: การจัดเรียงด้วยการเปรียบเทียบเชนของ Google Guava

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

สิ่งนี้ดีกว่ามาก แต่ต้องใช้รหัสแผ่นบอยเลอร์สำหรับกรณีการใช้งานทั่วไป: ค่า Null ควรจะมีค่าน้อยลงตามค่าเริ่มต้น สำหรับเขตข้อมูลที่ว่างคุณต้องให้คำสั่งพิเศษแก่ Guava ในกรณีนั้น นี่เป็นกลไกที่ยืดหยุ่นหากคุณต้องการทำสิ่งที่เฉพาะเจาะจง แต่บ่อยครั้งที่คุณต้องการตัวพิมพ์เริ่มต้น (เช่น 1, a, b, z, null)

การเรียงลำดับด้วย Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

เช่นเดียวกับการเปรียบเทียบของ Guava คลาสไลบรารีนี้จะเรียงลำดับได้อย่างง่ายดายบนหลายฟิลด์ แต่ยังกำหนดพฤติกรรมเริ่มต้นสำหรับค่า Null (เช่น 1, a, b, z, null) อย่างไรก็ตามคุณไม่สามารถระบุสิ่งอื่นใดได้เว้นแต่คุณจะระบุตัวเปรียบเทียบของคุณเอง

ดังนั้น

ในที่สุดมันก็ลงมาถึงรสชาติและความต้องการความยืดหยุ่น (GuChand's ComparChain) เทียบกับรหัสย่อ (Apache's CompareToBuilder)

วิธีโบนัส

ฉันพบโซลูชันที่ดีที่รวมตัวเปรียบเทียบหลายตัวตามลำดับความสำคัญกับ CodeReviewในMultiComparator:

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

Ofcourse Apache Commons Collections มีประโยชน์สำหรับเรื่องนี้แล้ว:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));

22

@ แพทริกหากต้องการเรียงลำดับมากกว่าหนึ่งฟิลด์ให้ลองComparatorChainอย่างต่อเนื่อง

ComparatorChain เป็น Comparator ที่หุ้มตัวเปรียบเทียบอย่างน้อยหนึ่งตัวตามลำดับ ComparatorChain เรียกแต่ละ Comparator ตามลำดับจนกระทั่งทั้ง 1) Comparator เดี่ยวใด ๆ ส่งคืนผลลัพธ์ที่ไม่เป็นศูนย์ (และผลลัพธ์นั้นจะถูกส่งกลับ) หรือ 2) ComparatorChain หมดแล้ว (และส่งคืนศูนย์) การเรียงลำดับชนิดนี้คล้ายกับการเรียงลำดับหลายคอลัมน์ใน SQL และคลาสนี้อนุญาตให้คลาส Java เลียนแบบพฤติกรรมดังกล่าวเมื่อเรียงลำดับรายการ

เพื่ออำนวยความสะดวกในการเรียงลำดับคล้ายกับ SQL เพิ่มเติมสามารถสลับลำดับของตัวเปรียบเทียบเดี่ยวใด ๆ ในรายการได้

การเรียกเมธอดที่เพิ่มตัวเปรียบเทียบใหม่หรือเปลี่ยนแปลงการเรียงขึ้น / ลงหลังจากการเปรียบเทียบ (Object, Object) ถูกเรียกจะส่งผลให้เกิด UnsupportedOperationException อย่างไรก็ตามระวังที่จะไม่เปลี่ยนแปลง List of Comparators หรือ BitSet ที่กำหนดลำดับการเรียง

อินสแตนซ์ของ ComparatorChain จะไม่ถูกซิงโครไนซ์ คลาสไม่ปลอดภัยสำหรับเธรดในเวลาก่อสร้าง แต่เป็นเธรดที่ปลอดภัยสำหรับทำการเปรียบเทียบหลายครั้งหลังจากการตั้งค่าทั้งหมดเสร็จสมบูรณ์


20

ตัวเลือกอื่นที่คุณสามารถพิจารณาได้คือ Apache Commons มันมีตัวเลือกมากมาย

import org.apache.commons.lang3.builder.CompareToBuilder;

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}


10
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}

1
ฉันชอบกลยุทธ์นี้มาก ขอบคุณ!
นาย Polywhirl

วิธีที่มีประสิทธิภาพมากที่สุด! ขอบคุณ
Zakaria Bouazza

8

สำหรับผู้ที่สามารถใช้ Java 8 สตรีมมิ่ง API ได้มีวิธีการเรียงต่อกันที่ได้รับการบันทึกไว้ที่นี่: Lambdas และการเรียงลำดับ

ฉันกำลังมองหาเทียบเท่าของ C # LINQ:

.ThenBy(...)

ฉันพบกลไกใน Java 8 บน Comparator:

.thenComparing(...)

ดังนั้นนี่คือตัวอย่างข้อมูลที่แสดงให้เห็นถึงอัลกอริทึม

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

ลองดูลิงค์ด้านบนเพื่อดูวิธีการเรียงลำดับและคำอธิบายเกี่ยวกับวิธีการอนุมานประเภทของ Java ทำให้การกำหนดมากกว่า clunky เปรียบเทียบกับ LINQ

นี่คือการทดสอบหน่วยเต็มรูปแบบสำหรับการอ้างอิง:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}

7

การเขียนComparatorด้วยตนเองสำหรับกรณีการใช้งานดังกล่าวเป็นวิธีการที่ดี IMO แนวทางเฉพาะกิจดังกล่าวมีข้อบกพร่องมากมาย:

  • ไม่มีการใช้รหัสซ้ำ ละเมิด DRY
  • สำเร็จรูป
  • เพิ่มความเป็นไปได้ของข้อผิดพลาด

ดังนั้นทางออกคืออะไร?

ครั้งแรกที่ทฤษฎีบางอย่าง

ขอให้เราแสดง "ประเภทเรื่องAเปรียบเทียบสนับสนุน" Ord Aโดย (จากมุมมองของโปรแกรมคุณสามารถคิดว่าOrd Aเป็นวัตถุที่มีลอจิกสำหรับการเปรียบเทียบสองAs ใช่เหมือนComparator)

ตอนนี้ถ้าOrd AและOrd Bแล้วคอมโพสิตของพวกเขา(A, B)ควรสนับสนุนการเปรียบเทียบ Ord (A, B)กล่าวคือ ถ้าOrd A, Ord Bและแล้วOrd COrd (A, B, C)

เราสามารถขยายข้อโต้แย้งนี้ไปสู่ความเป็นอันขาดโดยพลการและพูดว่า:

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

เรียกคำสั่งนี้ว่า 1

การเปรียบเทียบคอมโพสิตนั้นจะใช้งานได้ตามที่คุณอธิบายไว้ในคำถามของคุณ: การเปรียบเทียบครั้งแรกจะถูกลองก่อนจากนั้นจึงทำการเปรียบเทียบต่อไปจากนั้นต่อไปและต่อไปเรื่อย ๆ

นั่นเป็นส่วนแรกของการแก้ปัญหาของเรา ตอนนี้ส่วนที่สอง

ถ้าคุณรู้ว่าOrd A, และรู้วิธีที่จะเปลี่ยนBไปA(โทรที่ฟังก์ชั่นการเปลี่ยนแปลงf) Ord Bแล้วคุณยังสามารถมี อย่างไร? ดีเมื่อทั้งสองBกรณีจะได้รับการเทียบแรกคุณเปลี่ยนพวกเขาที่จะAใช้แล้วใช้fOrd A

ที่นี่เรามีการทำแผนที่การเปลี่ยนแปลงไปB → A Ord A → Ord Bเรื่องนี้เป็นที่รู้จักกันในชื่อแผนที่ contravariant (หรือcomapสั้น)

Ord A, (B → A)comap Ord B

เรียกคำสั่งนี้ว่า 2


ตอนนี้เราจะนำสิ่งนี้ไปใช้กับตัวอย่างของคุณ

คุณมีชนิดข้อมูลชื่อที่ประกอบด้วยสามด้านของประเภทPersonString

  • Ord Stringเรารู้ว่า ตามคำสั่งที่ Ord (String, String, String)1

  • เราสามารถเขียนฟังก์ชั่นจากไปPerson (String, String, String)(เพียงกลับทุ่งสาม.) เนื่องจากเราทราบOrd (String, String, String)และPerson → (String, String, String)โดยคำสั่งที่ 2 เราสามารถใช้ที่จะได้รับcomapOrd Person

QED


ฉันจะใช้แนวคิดเหล่านี้ทั้งหมดได้อย่างไร

ข่าวดีก็คือคุณไม่จำเป็นต้อง มีห้องสมุดที่ใช้แนวคิดทั้งหมดที่อธิบายไว้ในโพสต์นี้แล้ว (หากคุณอยากรู้ว่าวิธีการใช้งานเหล่านี้คุณสามารถดูภายใต้ประทุนได้)

นี่คือลักษณะของโค้ดที่จะดู:

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

คำอธิบาย:

  • stringOrdOrd<String>เป็นวัตถุของพิมพ์ สิ่งนี้สอดคล้องกับข้อเสนอ "รองรับการเปรียบเทียบ" ดั้งเดิมของเรา
  • p3Ordเป็นวิธีการที่ใช้เวลาOrd<A>, Ord<B>, และผลตอบแทนOrd<C> Ord<P3<A, B, C>>สิ่งนี้สอดคล้องกับคำแถลงที่ 1 ( P3ย่อมาจากผลิตภัณฑ์ที่มีสามองค์ประกอบผลิตภัณฑ์เป็นคำพีชคณิตสำหรับคอมโพสิต)
  • comapสอดคล้องกับดีcomap.
  • F<A, B>A → Bแสดงให้เห็นถึงฟังก์ชั่นการเปลี่ยนแปลง
  • p เป็นวิธีการจากโรงงานสำหรับการสร้างผลิตภัณฑ์
  • การแสดงออกทั้งหมดสอดคล้องกับคำสั่ง 2

หวังว่าจะช่วย


5

แทนที่จะใช้วิธีการเปรียบเทียบคุณอาจต้องการกำหนด subclasses "Comparator" หลายประเภทภายในคลาส Person ด้วยวิธีนี้คุณจะสามารถส่งผ่านไปยังวิธีการเรียงลำดับคอลเลกชันมาตรฐาน


3

ฉันคิดว่ามันจะสับสนมากกว่านี้หากอัลกอริทึมการเปรียบเทียบของคุณ "ฉลาด" ฉันจะไปกับวิธีการเปรียบเทียบมากมายที่คุณแนะนำ

ข้อยกเว้นเพียงอย่างเดียวสำหรับฉันคือความเสมอภาค สำหรับการทดสอบหน่วยฉันจะมีประโยชน์ในการแทนที่. Equal (ใน. net) เพื่อตรวจสอบว่ามีหลายเขตข้อมูลที่เท่ากันระหว่างวัตถุสองชิ้น (ไม่ใช่การอ้างอิงที่เท่ากัน)


3

หากมีหลายวิธีที่ผู้ใช้อาจสั่งคนคุณสามารถตั้งค่าComparatorหลาย ๆค่าเป็นค่าคงที่ การดำเนินการเรียงลำดับส่วนใหญ่และคอลเลกชันเรียงลำดับใช้ตัวเปรียบเทียบเป็นพารามิเตอร์


3
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}

3

การใช้โค้ดของเดียวกันอยู่ที่นี่ถ้าเราต้องเรียงลำดับวัตถุบุคคลตามหลายฟิลด์

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}

2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);

ฉันมาถึงคำถามนี้เพราะรหัสของฉันเริ่มที่จะชอบคำตอบของคุณ;)
ยานโกรธ

0

หากคุณใช้อินเทอร์เฟซที่เปรียบเทียบได้คุณจะต้องเลือกคุณสมบัติง่าย ๆ ที่จะสั่งซื้อ สิ่งนี้เรียกว่าการจัดเรียงแบบธรรมชาติ คิดว่ามันเป็นค่าเริ่มต้น มันถูกใช้เสมอเมื่อไม่มีการเปรียบเทียบตัวระบุพิเศษ โดยปกติจะเป็นชื่อ แต่กรณีการใช้งานของคุณอาจมีความแตกต่าง คุณมีอิสระที่จะใช้เครื่องมือเปรียบเทียบอื่น ๆ จำนวนเท่าใดก็ได้ที่คุณสามารถจัดหาให้กับ API คอลเลกชันต่างๆเพื่อแทนที่ลำดับธรรมชาติ

นอกจากนี้โปรดทราบว่าโดยทั่วไปหาก a.compareTo (b) == 0 ดังนั้น a.equals (b) == จริง ไม่เป็นไรถ้าไม่ใช่ แต่มีผลข้างเคียงที่ต้องระวัง ดู javadocs ที่ยอดเยี่ยมบนอินเทอร์เฟซที่เปรียบเทียบได้และคุณจะพบข้อมูลที่ยอดเยี่ยมมากมายเกี่ยวกับสิ่งนี้


0

บล็อกต่อไปนี้ได้รับตัวอย่าง Comparator ที่ดี

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

โทรเปรียบเทียบ

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );


0

มันง่ายที่จะเปรียบเทียบสองวัตถุกับวิธีแฮชโค้ดใน java`

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}

โปรดอย่าทำอย่างนั้น Hashcodes ไม่ควรใช้สำหรับการเปรียบเทียบความเท่าเทียมกัน แต่สำหรับการทำดัชนี hashtable การชนของแฮชอาจส่งผลให้เกิดความเท่าเทียมกันสำหรับวัตถุสองชนิดที่แตกต่างกัน แม้แต่แฮชเทเบิลก็ขึ้นอยู่กับความเสมอภาคที่แท้จริงหากเกิดการชนกันของแฮช
Noel Widmer

0

ฉันมักจะแทนที่compareTo()วิธีการของฉันเช่นนี้เมื่อใดก็ตามที่ฉันต้องทำการเรียงลำดับหลายระดับ

public int compareTo(Song o) {
    // TODO Auto-generated method stub
    int comp1 = 10000000*(movie.compareTo(o.movie))+1000*(artist.compareTo(o.artist))+songLength;
    int comp2 = 10000000*(o.movie.compareTo(movie))+1000*(o.artist.compareTo(artist))+o.songLength;
    return comp1-comp2;
} 

ที่นี่การตั้งค่าแรกจะได้รับชื่อภาพยนตร์แล้วศิลปินและสุดท้ายเพื่อ songLength คุณต้องตรวจสอบให้แน่ใจว่าตัวคูณนั้นอยู่ห่างพอที่จะไม่ข้ามขอบเขตของกันและกัน


-2

มันง่ายที่จะทำโดยใช้ห้องสมุดฝรั่งของ Google

เช่น Objects.equal(name, name2) && Objects.equal(age, age2) && ...

ตัวอย่างเพิ่มเติม:


การเปรียบเทียบสิ่งต่างจากการตรวจสอบความเท่าเทียมกัน
Clint Eastwood
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.