มีไลบรารี Java ที่สามารถ“ แตกต่าง” สองอ็อบเจกต์ได้หรือไม่?


90

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

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

นี่คือตัวอย่างของสิ่งที่ฉันกำลังมองหา:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffDataStructure diff = OffTheShelfUtility.diff(a, b);  // magical recursive comparison happens here

หลังจากเปรียบเทียบยูทิลิตี้จะบอกฉันว่า "prop1" แตกต่างกันระหว่างวัตถุทั้งสองและ "prop2" นั้นเหมือนกัน ฉันคิดว่ามันเป็นเรื่องธรรมดาที่สุดสำหรับ DiffDataStructure ที่จะเป็นต้นไม้ แต่ฉันจะไม่จู้จี้จุกจิกถ้ารหัสนั้นเชื่อถือได้


9
ตรวจสอบสิ่งนี้code.google.com/p/jettison
denolk


14
นี่ไม่ใช่รายการที่ซ้ำกันของ "จะทดสอบความเท่าเทียมกันของกราฟออบเจ็กต์เชิงซ้อนได้อย่างไร" ฉันกำลังมองหาห้องสมุดไม่ใช่คำแนะนำในการทำ นอกจากนี้ฉันสนใจเดลต้าไม่ใช่ว่ามันเท่ากันหรือไม่
Kaypro II

1
ดูที่github.com/jdereg/java-utilซึ่งมียูทิลิตี้ GraphComparator คลาสนี้จะสร้างรายการของเดลต้าระหว่างกราฟวัตถุ นอกจากนี้ยังสามารถผสาน (ใช้) เดลต้ากับกราฟ ได้อย่างมีประสิทธิภาพคือ List <Delta> = GraphComparator (rootA, rootB) เช่นเดียวกับ GraphComparator.applyDelta (rootB, List <Delta>)
John DeRegnaucourt

ฉันรู้ว่าคุณไม่ต้องการใช้การสะท้อน แต่นั่นเป็นวิธีที่ง่าย / ง่ายที่สุดในการนำสิ่งนี้ไปใช้
RobOhRob

คำตอบ:


44

อาจจะช้าไปหน่อย แต่ฉันก็ตกอยู่ในสถานการณ์เดียวกันกับคุณและลงเอยด้วยการสร้างไลบรารีของตัวเองสำหรับกรณีการใช้งานของคุณ เนื่องจากฉันถูกบังคับให้คิดวิธีแก้ปัญหาด้วยตัวเองฉันจึงตัดสินใจปล่อยมันบน Github เพื่อให้คนอื่นได้ทำงานหนัก คุณสามารถค้นหาได้ที่นี่: https://github.com/SQiShER/java-object-diff

--- แก้ไข ---

นี่คือตัวอย่างการใช้งานเล็กน้อยตามรหัส OPs:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffNode diff = ObjectDifferBuilder.buildDefault().compare(a, b);

assert diff.hasChanges();
assert diff.childCount() == 1;
assert diff.getChild('prop1').getState() == DiffNode.State.CHANGED;

1
ฉันขอแนะนำให้คุณไปที่โพสต์นี้ที่แสดงกรณีการใช้งานของ java-object-diff ได้ไหม stackoverflow.com/questions/12104969/…ขอบคุณมากสำหรับไลบรารีที่เป็นประโยชน์นี้!
Matthias Wuttke

ฉันได้ดูที่โพสต์ของคุณมีคำตอบที่เขียนไว้ซึ่งกลายเป็นว่านานเกินไปที่จะโพสต์เป็นความคิดเห็นดังนั้นนี่คือ "ส่วนสำคัญ" ของมัน: gist.github.com/3555555
SQiShER

2
เรียบร้อย. ฉันลงเอยด้วยการเขียนการใช้งานของตัวเองด้วย ฉันไม่แน่ใจว่าจะมีโอกาสได้สำรวจการใช้งานของคุณจริงๆ แต่ฉันอยากรู้ว่าคุณจัดการ Lists อย่างไร ฉันลงเอยด้วยการจัดการความแตกต่างของคอลเลกชันทั้งหมดเป็นความแตกต่างของแผนที่ (โดยการกำหนดคีย์ในรูปแบบต่างๆขึ้นอยู่กับว่าเป็นรายการชุดหรือแผนที่จากนั้นใช้การดำเนินการตั้งค่าบนชุดคีย์) อย่างไรก็ตามวิธีการดังกล่าวมีความอ่อนไหวต่อการเปลี่ยนแปลงดัชนีรายการ ฉันไม่ได้แก้ปัญหานั้นเพราะฉันไม่จำเป็นต้องใช้แอปพลิเคชันของฉัน แต่ฉันอยากรู้ว่าคุณจัดการมันอย่างไร (ฉันคิดว่ามันเหมือนกับ text-diff)
Kaypro II

2
คุณนำเสนอจุดที่ดี คอลเล็กชันเป็นเรื่องยากที่จะจัดการ โดยเฉพาะอย่างยิ่งถ้าคุณสนับสนุนการควบรวมกิจการเช่นฉัน ฉันแก้ไขปัญหาโดยใช้ข้อมูลประจำตัวของวัตถุเพื่อตรวจสอบว่ามีการเพิ่มเปลี่ยนแปลงหรือลบรายการหรือไม่ (ผ่าน hashCode เท่ากับและมี) สิ่งที่ดีคือฉันสามารถดูแลคอลเลกชันทั้งหมดเหมือนกันได้ สิ่งที่แย่คือฉันไม่สามารถจัดการได้อย่างถูกต้อง (เช่น) ArrayLists ที่มีวัตถุเดียวกันหลาย ๆ ครั้ง ฉันชอบที่จะเพิ่มการสนับสนุนสำหรับสิ่งนั้น แต่ดูเหมือนว่าจะค่อนข้างซับซ้อนและโชคดีที่ยังไม่มีใครร้องขอ :-)
SQiShER

Daniel ขอบคุณสำหรับความคิดเห็นของคุณ! คุณพูดถูกการสร้างวัตถุดั้งเดิมขึ้นมาใหม่จากความแตกต่างเป็นเรื่องยาก แต่ในกรณีของฉันไม่สำคัญเกินไป แต่เดิมฉันเก็บวัตถุเวอร์ชันก่อนหน้าไว้ในฐานข้อมูลตามที่คุณแนะนำ ปัญหาคือส่วนใหญ่ของเอกสารขนาดใหญ่ไม่เปลี่ยนแปลงมีข้อมูลที่ซ้ำกันจำนวนมาก นี่คือเหตุผลที่ฉันเริ่มค้นหาทางเลือกอื่นและพบ java-object-diff ฉันยังประสบปัญหากับคอลเล็กชัน / แผนที่และแก้ไขวิธีการ "เท่ากับ" เพื่อตรวจสอบความเท่าเทียมกันของฐานข้อมูลหลัก (ไม่ใช่ทั้งวัตถุ) สิ่งนี้ช่วยได้
Matthias Wuttke

40

http://javers.orgเป็นไลบรารีที่ทำสิ่งที่คุณต้องการอย่างยิ่ง: มีวิธีการเช่นการเปรียบเทียบ (Object leftGraph, Object rightGraph) ที่ส่งคืนวัตถุ Diff Diff มีรายการการเปลี่ยนแปลง (ReferenceChange, ValueChange, PropertyChange) เช่น

given:
DummyUser user =  dummyUser("id").withSex(FEMALE).build();
DummyUser user2 = dummyUser("id").withSex(MALE).build();
Javers javers = JaversTestBuilder.newInstance()

when:
Diff diff = javers.compare(user, user2)

then:
diff.changes.size() == 1
ValueChange change = diff.changes[0]
change.leftValue == FEMALE
change.rightValue == MALE

สามารถจัดการกับรอบในกราฟ

นอกจากนี้คุณจะได้รับ Snapshot ของวัตถุกราฟใด ๆ Javers มี JSON serializers และ deserializers สำหรับ snapshot และการเปลี่ยนแปลงเพื่อให้คุณสามารถบันทึกลงในฐานข้อมูลได้อย่างง่ายดาย ด้วยไลบรารีนี้คุณสามารถใช้โมดูลสำหรับการตรวจสอบได้อย่างง่ายดาย


1
ไม่ได้ผล คุณสร้างอินสแตนซ์ Javers ได้อย่างไร
Ryan Vettese

2
Javers มีเอกสารที่ยอดเยี่ยมที่อธิบายทุกอย่าง: javers.org
Paweł Szymczyk

2
ฉันพยายามจะใช้ แต่ใช้กับ Java เท่านั้น> = 7 ยังมีคนทำโปรเจ็กต์บน Java 6 อยู่ !!!
jesantana

2
จะเกิดอะไรขึ้นถ้าวัตถุทั้งสองที่ฉันเปรียบเทียบมีรายการวัตถุภายในและฉันจะเห็นความแตกต่างเหล่านั้นด้วยหรือไม่? ระดับของลำดับชั้นที่ผู้คุมสามารถเปรียบเทียบได้คืออะไร?
Deepak

1
ใช่คุณจะเห็นความแตกต่างของวัตถุภายใน ในขณะนี้ไม่มีเกณฑ์สำหรับระดับลำดับชั้น Javers จะลงลึกที่สุดเท่าที่จะทำได้ข้อ จำกัด คือขนาดกอง
Paweł Szymczyk

16

ใช่ไลบรารีjava-utilมีคลาส GraphComparator ที่จะเปรียบเทียบ Java Object Graph สองรายการ จะคืนค่าความแตกต่างเป็นรายการเดลต้า GraphComparator ยังอนุญาตให้คุณรวม (ใช้) เดลต้าด้วย รหัสนี้มีการอ้างอิงเฉพาะกับ JDK เท่านั้นไม่มีไลบรารีอื่น ๆ


13
ดูเหมือนห้องสมุดที่ดีคุณควรพูดถึงว่าคุณเป็นผู้มีส่วนร่วม
Christos

4

คุณสามารถดูวิธีแก้ปัญหาจาก Apache โครงการส่วนใหญ่มีอยู่แล้วใน classpath เนื่องจากเป็นส่วนหนึ่งของ commons-lang

ตรวจสอบความแตกต่างสำหรับฟิลด์เฉพาะ:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html

ตรวจสอบความแตกต่างโดยใช้การสะท้อน:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html


นี่ควรเป็นคำตอบที่ได้รับการยอมรับ!
Janaka Bandara

3

ไลบรารี Javers ทั้งหมดรองรับเฉพาะ Java 7 เท่านั้นฉันตกอยู่ในสถานการณ์เนื่องจากฉันต้องการใช้สิ่งนี้สำหรับโปรเจ็กต์ Java 6 ดังนั้นฉันจึงใช้ซอร์สและเปลี่ยนวิธีการทำงานสำหรับ Java 6 ด้านล่างคือรหัส github .

https://github.com/sand3sh/javers-forJava6

ลิงค์ Jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar

ฉันได้เปลี่ยนเฉพาะ Java 7 ที่รองรับการแปลงการแคสต์ '<>' โดยธรรมชาติเป็นการสนับสนุน Java 6 ฉันไม่รับประกันว่าฟังก์ชันทั้งหมดจะทำงานได้เนื่องจากฉันได้แสดงความคิดเห็นเกี่ยวกับโค้ดที่ไม่จำเป็นสำหรับฉันมันใช้ได้กับการเปรียบเทียบอ็อบเจ็กต์ที่กำหนดเองทั้งหมด



1

บางทีนี่อาจช่วยได้ขึ้นอยู่กับว่าคุณใช้รหัสนี้ที่ไหนซึ่งอาจเป็นประโยชน์หรือมีปัญหา ทดสอบรหัสนี้

    /**
 * @param firstInstance
 * @param secondInstance
 */
protected static void findMatchingValues(SomeClass firstInstance,
        SomeClass secondInstance) {
    try {
        Class firstClass = firstInstance.getClass();
        Method[] firstClassMethodsArr = firstClass.getMethods();

        Class secondClass = firstInstance.getClass();
        Method[] secondClassMethodsArr = secondClass.getMethods();


        for (int i = 0; i < firstClassMethodsArr.length; i++) {
            Method firstClassMethod = firstClassMethodsArr[i];
            // target getter methods.
            if(firstClassMethod.getName().startsWith("get") 
                    && ((firstClassMethod.getParameterTypes()).length == 0)
                    && (!(firstClassMethod.getName().equals("getClass")))
            ){

                Object firstValue;
                    firstValue = firstClassMethod.invoke(firstInstance, null);

                logger.info(" Value "+firstValue+" Method "+firstClassMethod.getName());

                for (int j = 0; j < secondClassMethodsArr.length; j++) {
                    Method secondClassMethod = secondClassMethodsArr[j];
                    if(secondClassMethod.getName().equals(firstClassMethod.getName())){
                        Object secondValue = secondClassMethod.invoke(secondInstance, null);
                        if(firstValue.equals(secondValue)){
                            logger.info(" Values do match! ");
                        }
                    }
                }
            }
        }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
}

2
ขอบคุณ แต่เรามีโค้ดที่เดินบนกราฟวัตถุโดยใช้การสะท้อนรายการการเปลี่ยนแปลงอยู่แล้ว คำถามนี้ไม่เกี่ยวกับวิธีการทำ แต่เป็นการพยายามหลีกเลี่ยงการประดิษฐ์วงล้อขึ้นมาใหม่
Kaypro II

การคิดค้นล้อใหม่ไม่ใช่เรื่องเลวร้ายหากการคิดค้นใหม่ได้เกิดขึ้นแล้ว (IE: ล้อที่คิดค้นขึ้นใหม่ได้รับการชำระเงินแล้ว)
Thomas Eding

1
ฉันเห็นด้วยกับคุณ แต่ในกรณีนี้รหัสที่สร้างขึ้นใหม่เป็นสิ่งที่ไม่สามารถเข้าถึงได้
Kaypro II

3
ฉันจะตรวจสอบFieldsไม่ใช่วิธีการ getRandomNumber()วิธีการจะเป็นอะไรเช่น
โบฮีเมียน

-3

วิธีที่ง่ายกว่าในการบอกคุณอย่างรวดเร็วว่าวัตถุสองชิ้นแตกต่างกันหรือไม่คือการใช้ไลบรารี apache commons

    BeanComparator lastNameComparator = new BeanComparator("lname");
    logger.info(" Match "+bc.compare(firstInstance, secondInstance));

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