อะไรคือสิ่งที่เทียบเท่าของ C ++ Pair <L, R> ใน Java?


671

มีเหตุผลที่ดีที่ไม่มีPair<L,R>ใน Java หรือไม่ อะไรจะเทียบเท่ากับโครงสร้าง C ++ นี้ ฉันควรหลีกเลี่ยงการนำของฉันไปใช้ใหม่

ดูเหมือนว่า1.6กำลังให้อะไรที่คล้ายกัน ( AbstractMap.SimpleEntry<K,V>) แต่มันดูค่อนข้างซับซ้อน


7
ทำไมต้องเลือกที่AbstractMap.SimpleEntryซับซ้อน?
CurtainDog

27
เนื่องจาก namig ให้ตั้งชื่อหนึ่งคีย์และหนึ่งค่าโดยพลการ
Enerccio


2
@sffc JavaFX ไม่ได้อยู่ใน classpaths เริ่มต้นใน JDK7 โดยใช้มันต้องเพิ่ม JFX runtime library ด้วยตนเอง
Cord Rehn

3
@Eerccio: จริง ๆ แล้วคุณระบุว่า "ครั้งแรก" และ "สอง" ไม่ได้เป็นอิสระในขณะที่ "สำคัญ" และ "ค่า" - คืออะไร? นี่เป็นเหตุผลที่ดีอย่างหนึ่งที่ไม่มีคลาสดังกล่าวใน SDK จะมีข้อโต้แย้งที่ไม่สิ้นสุดเกี่ยวกับการตั้งชื่อ "เหมาะสม"
fdreger

คำตอบ:


400

ในเธรดบนcomp.lang.java.help Hunter Gratzner ให้ข้อโต้แย้งบางอย่างกับการปรากฏตัวของการPairสร้างใน Java อาร์กิวเมนต์หลักคือคลาสPairไม่ได้ถ่ายทอดความหมายใด ๆ เกี่ยวกับความสัมพันธ์ระหว่างค่าทั้งสอง (คุณจะรู้ได้อย่างไรว่า "แรก" และ "สอง" หมายความว่าอย่างไร)

วิธีปฏิบัติที่ดีกว่าคือการเขียนคลาสที่ง่ายมากเช่นเดียวกับที่ไมค์เสนอให้สำหรับแต่ละแอปพลิเคชันที่คุณสร้างขึ้นจากPairคลาส Map.Entryเป็นตัวอย่างของคู่ที่มีความหมายในชื่อของมัน

สรุปแล้วในความคิดของฉันมันจะดีกว่าถ้ามีคลาสPosition(x,y)คลาสRange(begin,end)และคลาสEntry(key,value)มากกว่าแบบทั่วไปPair(first,second)ที่ไม่ได้บอกอะไรฉันเกี่ยวกับสิ่งที่ควรทำ


143
Gratzner กำลังแยกเส้นผม เรามีความสุขมากที่ได้คืนค่าหนึ่งเดียวเป็นคลาสดั้งเดิมหรือในตัวโดยไม่ต้องใส่ในคลาส ถ้าเราจะคืนค่า tuple ขององค์ประกอบโหลไม่มีใครจะไม่เห็นด้วยก็ควรจะมีระดับของตัวเอง บางจุดที่อยู่ตรงกลางคือเส้นแบ่ง (fuzzy) ฉันคิดว่าสมองจิ้งจกของเราสามารถรับมือกับคู่ได้ง่ายพอ
เอียน

25
ฉันเห็นด้วยกับเอียน Java ช่วยให้คุณกลับ int; มันไม่ได้บังคับให้คุณสร้างนามแฝงสำหรับ int ทุกครั้งที่คุณใช้ คู่ไม่แตกต่างกันมาก
Clément

5
หากเราสามารถแยกคู่โดยตรงกับตัวแปรท้องถิ่นของคุณหรือส่งต่อไปยังวิธีการที่ใช้สองข้อโต้แย้งคู่จะเป็นชั้นที่มีประโยชน์ เนื่องจากเราไม่สามารถแกะกล่องออกเป็นแบบนี้ได้การสร้างคลาสที่มีความหมายและการรักษาคุณค่าไว้ด้วยกันจึงไม่ได้ดูแย่เกินไป และถ้าคุณต้องการคู่จริงๆแม้จะมีข้อ จำกัด ก็มี Object [2] + ปลดเปลื้อง :-)
marcus

สิ่งนี้คือถ้าคุณไม่เห็นด้วยกับ Gratzner มีการใช้งานคู่ในหลายสถานที่ Apache Commons และ Guava มีทั้ง IIRC ใช้สิ่งเหล่านั้น แต่การใส่บางอย่างในไลบรารี Java หลักหมายความว่ามันเป็นวิธีที่ดีที่สุดในการทำสิ่งต่าง ๆ (ด้วยตัวพิมพ์ใหญ่) และเนื่องจากผู้คนไม่เห็นด้วยกับมันเราไม่ควรใส่มัน มี cruft เพียงพอใน libs เก่าตามที่เป็นอยู่เราไม่จำเป็นต้องใส่เพิ่มเติมที่นั่น
Haakon Løtveit

1
@Dragas เมื่อฉันต้องการคู่ค่าแล้วนั่นไม่ใช่ Java ... อย่างจริงจัง?
idclev 463035818

156

นี่คือ Java คุณต้องสร้างคลาส Pair ที่เหมาะกับคุณด้วยคลาสที่อธิบายและชื่อฟิลด์และอย่าลืมว่าคุณจะสร้างวงล้อใหม่ด้วยการเขียน hashCode () / equals () หรือใช้การเปรียบเทียบได้อีกครั้งและอีกครั้ง


61
ไม่ตอบคำถาม "ทำไม" (เว้นแต่คุณจะพิจารณาคำตอบ 'นี่คือ java')
Nikita Rybak

127
+1 สำหรับการเยาะเย้ยชวาของ Java -1 สำหรับการไม่ตอบคำถาม
Bennett McElwee

19
Java-mockery นั้นใช้ได้ถ้าคุณชี้ไปที่ Apache Commong Lang ซึ่งมีคลาส Pair
haylem

6
หรือคุณสามารถใช้SimpleImmutableEntry
CurtainDog

33
ประโยคแรกตอบคำถามที่ว่า "ทำไม" นี่คือ Java และนั่นแหละ
masterziv

103

HashMap เข้ากันได้กับคลาส Pair:

public class Pair<A, B> {
    private A first;
    private B second;

    public Pair(A first, B second) {
        super();
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        int hashFirst = first != null ? first.hashCode() : 0;
        int hashSecond = second != null ? second.hashCode() : 0;

        return (hashFirst + hashSecond) * hashSecond + hashFirst;
    }

    public boolean equals(Object other) {
        if (other instanceof Pair) {
            Pair otherPair = (Pair) other;
            return 
            ((  this.first == otherPair.first ||
                ( this.first != null && otherPair.first != null &&
                  this.first.equals(otherPair.first))) &&
             (  this.second == otherPair.second ||
                ( this.second != null && otherPair.second != null &&
                  this.second.equals(otherPair.second))) );
        }

        return false;
    }

    public String toString()
    { 
           return "(" + first + ", " + second + ")"; 
    }

    public A getFirst() {
        return first;
    }

    public void setFirst(A first) {
        this.first = first;
    }

    public B getSecond() {
        return second;
    }

    public void setSecond(B second) {
        this.second = second;
    }
}

136
คุณอาจต้องการที่จะลบ setters และทำครั้งแรกและครั้งสุดท้ายที่สองจึงทำให้คู่ไม่เปลี่ยนรูป (ถ้ามีคนเปลี่ยนส่วนประกอบหลังจากใช้มันเป็นกุญแจแฮชสิ่งแปลก ๆ จะเกิดขึ้น)
Thilo

21
return "(" + first.toString () + "," + second.toString () + ")" ในเมธอด toString () อาจส่งผลให้ NullPointerExceptions สิ่งนี้ดีกว่า: ส่งคืน "(" + แรก + "," + วินาที + ")";
Juha Syrjälä

6
นอกจากนี้ให้ทำเครื่องหมายว่าทั้งคู่เป็น "ขั้นสุดท้าย" หรือเปลี่ยนบรรทัดแรกให้เท่ากับ 'if (other! = null && this.getClass () == other.getClass ())'
sargas

8
ขออภัยสำหรับคำถาม nooby แบบสุ่ม แต่ทำไมคุณถึงเรียก super () ในนวกรรมิก?
อิบราฮิม

6
@ อิบราฮิม: ในกรณีนี้มันฟุ่มเฟือย --- พฤติกรรมจะเหมือนกันทุกsuper()ประการถ้าคุณถอดออก โดยปกติฉันจะลพบุรีถ้ามันเป็นตัวเลือกเหมือนที่นี่
Chris Jester-Young

53

คู่ที่สั้นที่สุดที่ฉันสามารถหาได้มีดังต่อไปนี้โดยใช้ลอมบอก :

@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
    private F first;
    private S second;
}

มันมีประโยชน์ทั้งหมดของคำตอบจาก @arturh (ยกเว้นการเปรียบเทียบ) ก็มีhashCode, equals, toStringและแบบคงที่“คอนสตรัค” ซึ่งเป็น


Nifty! ชอบมัน!
Ahmet Ipkin


31

อีกวิธีในการใช้งานจับคู่กับ

  • เขตข้อมูลที่ไม่เปลี่ยนรูปแบบสาธารณะเช่นโครงสร้างข้อมูลอย่างง่าย
  • เทียบเคียง
  • แฮง่ายและเท่ากับ
  • โรงงานที่เรียบง่ายเพื่อให้คุณไม่ต้องให้ประเภท เช่น Pair.of ("hello", 1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    
        public final FIRST first;
        public final SECOND second;
    
        private Pair(FIRST first, SECOND second) {
            this.first = first;
            this.second = second;
        }
    
        public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first,
                SECOND second) {
            return new Pair<FIRST, SECOND>(first, second);
        }
    
        @Override
        public int compareTo(Pair<FIRST, SECOND> o) {
            int cmp = compare(first, o.first);
            return cmp == 0 ? compare(second, o.second) : cmp;
        }
    
        // todo move this to a helper class.
        private static int compare(Object o1, Object o2) {
            return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1
                    : ((Comparable) o1).compareTo(o2);
        }
    
        @Override
        public int hashCode() {
            return 31 * hashcode(first) + hashcode(second);
        }
    
        // todo move this to a helper class.
        private static int hashcode(Object o) {
            return o == null ? 0 : o.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Pair))
                return false;
            if (this == obj)
                return true;
            return equal(first, ((Pair) obj).first)
                    && equal(second, ((Pair) obj).second);
        }
    
        // todo move this to a helper class.
        private boolean equal(Object o1, Object o2) {
            return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
        }
    
        @Override
        public String toString() {
            return "(" + first + ", " + second + ')';
        }
    }

10
ofผมชอบวิธีการแบบคงที่โรงงาน มันเตือนคอลเลกชันที่ไม่เปลี่ยนรูปแบบของGoogle Guava
Jarek Przygódzki

7
คุณกำลังชี้o1ไปที่Comparableแม้ว่าจะไม่มีอะไรระบุว่าจริง ๆ แล้วจะใช้อินเทอร์เฟซนั้น หากเป็นความต้องการที่พารามิเตอร์ชนิดควรจะเป็นFIRST FIRST extends Comparable<?>
G_H

ฉันไม่ใช่คนจาวาดังนั้นโปรดยกโทษให้ฉันเพราะความเขลาของฉัน แต่คุณคิดว่าผู้ช่วยประเภทใดในความเห็นของสิ่งที่ต้องทำ

3
31 เป็นค่าคงที่ไม่ดีสำหรับ hashCode ตัวอย่างเช่นหากคุณใช้ HashMap ที่คีย์โดย Pair <Integer, Integer> สำหรับแผนที่ 2D คุณจะได้รับการชนหลายครั้ง ตัวอย่างเช่น (a * 65497) ^ b จะเหมาะกว่า
MichałZieliński

1
@MarioCarneiro ^ คือ xor ไม่ใช่กำลัง
MichałZieliński

27

วิธีการเกี่ยวกับhttp://www.javatuples.org/index.htmlฉันพบว่ามีประโยชน์มาก

javatuples เสนอคลาส tuple ให้คุณตั้งแต่หนึ่งถึงสิบองค์ประกอบ:

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Quartet<A,B,C,D> (4 elements)
Quintet<A,B,C,D,E> (5 elements)
Sextet<A,B,C,D,E,F> (6 elements)
Septet<A,B,C,D,E,F,G> (7 elements)
Octet<A,B,C,D,E,F,G,H> (8 elements)
Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)

6
ตลก แต่มีอย่างน้อย 5 คลาสมากกว่าที่ฉันจะจินตนาการได้
maaartinus

3
@maaartinus มากกว่า 10 อย่างที่ฉันอยากจะใช้
Boann

7
@Boann: ตกลงฉันแก้ไขอยู่เสมอ ฉันเคยใช้Pairและนึกได้ว่าอาจจะใช้Tripletทุกๆ 50 ปี ตอนนี้ฉันใช้ลอมบอกและสร้างชั้นเรียนขนาดเล็ก 4 บรรทัดทุกครั้งที่ฉันต้องการคู่ ดังนั้น "10 มากเกินไป" แน่นอน
maaartinus

5
เราต้องการBottom (0 element)ชั้นเรียนหรือไม่? :)
Earth Engine

2
ว้าวนี่น่าเกลียด ฉันรู้ว่าพวกเขากำลังพยายามทำให้ชัดเจน แต่ Tuple ที่มี params มากเกินไปอย่างใน C # น่าจะดีกว่า
arviman

12

ขึ้นอยู่กับสิ่งที่คุณต้องการใช้ เหตุผลทั่วไปในการทำเช่นนี้คือทำซ้ำบนแผนที่ซึ่งคุณทำสิ่งนี้ (Java 5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
  System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}

1
ผมไม่แน่ใจว่าชั้นเองจะช่วยในกรณีนี้ :)
Nikita Rybak

31
"เหตุผลทั่วไปในการทำเช่นนั้นก็คือการวนซ้ำแผนที่" จริงๆ?
Bennett McElwee

12

Android ให้Pairคลาส ( http://developer.android.com/reference/android/util/Pair.html ) ที่นี่การใช้งาน:

public class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        }
        Pair<?, ?> p = (Pair<?, ?>) o;
        return Objects.equal(p.first, first) && Objects.equal(p.second, second);
    }

    @Override
    public int hashCode() {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    }

    public static <A, B> Pair <A, B> create(A a, B b) {
        return new Pair<A, B>(a, b);
    }
}

1
Objects.equal(..)ต้องใช้ไลบรารี Guava
Markus L

3
เปลี่ยนเป็นObjects.equals(...)ที่อยู่ใน Java ตั้งแต่ 2011 (1.7)
AndrewF

9

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

อย่างไรก็ตามการล้างคำเตือนทั่วไปจากคำตอบของ @ PeterLawrey (java 1.7):

public class Pair<A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
        implements Comparable<Pair<A, B>> {

    public final A first;
    public final B second;

    private Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    public static <A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
            Pair<A, B> of(A first, B second) {
        return new Pair<A, B>(first, second);
    }

    @Override
    public int compareTo(Pair<A, B> o) {
        int cmp = o == null ? 1 : (this.first).compareTo(o.first);
        return cmp == 0 ? (this.second).compareTo(o.second) : cmp;
    }

    @Override
    public int hashCode() {
        return 31 * hashcode(first) + hashcode(second);
    }

    // TODO : move this to a helper class.
    private static int hashcode(Object o) {
        return o == null ? 0 : o.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        if (this == obj)
            return true;
        return equal(first, ((Pair<?, ?>) obj).first)
                && equal(second, ((Pair<?, ?>) obj).second);
    }

    // TODO : move this to a helper class.
    private boolean equal(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ')';
    }
}

เพิ่ม / แก้ไขการต้อนรับมาก :) Pair<?, ?>โดยเฉพาะอย่างยิ่งผมไม่แน่ใจว่าเกี่ยวกับการใช้ของฉัน

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่วากยสัมพันธ์นี้ดูให้แน่ใจว่าวัตถุนั้นใช้เปรียบเทียบได้และสำหรับคำอธิบายโดยละเอียดวิธีใช้max(Comparable a, Comparable b)ฟังก์ชันทั่วไปใน Java


ในฐานะที่เป็น Java Integers เป็น 32 บิตจะไม่คูณ hashcode แรกด้วย 31 หมายความว่ามันล้น? จะเป็นการดีกว่าไหมถ้าจะแสดงพิเศษหรือ
ด่าน

@Dan รู้สึกอิสระที่จะแก้ไขฉันย้ายออกไปจากจาวา :)
Mr_and_Mrs_D

5

ในความคิดของฉันไม่มีคู่ใน Java เพราะถ้าคุณต้องการเพิ่มฟังก์ชั่นพิเศษในคู่โดยตรง (เช่นที่เปรียบเทียบได้) คุณต้องผูกประเภทไว้ ใน C ++ เราไม่สนใจและหากประเภทที่เขียนคู่ไม่มีอยู่operator <แล้วpair::operator <ก็จะไม่รวบรวมเช่นกัน

ตัวอย่างของการเปรียบเทียบโดยไม่มีขอบเขต:

public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static int compare(Object l, Object r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : ((Comparable) (l)).compareTo(r);
        }
    }
}

/* ... */

Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
//Runtime error here instead of compile error!
System.out.println(a.compareTo(b));

ตัวอย่างของการเปรียบเทียบกับการตรวจสอบเวลาคอมไพล์ว่าอาร์กิวเมนต์ประเภทนั้นเปรียบได้หรือไม่:

public class Pair<
        F extends Comparable<? super F>, 
        S extends Comparable<? super S>
> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static <
            T extends Comparable<? super T>
    > int compare(T l, T r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : l.compareTo(r);
        }
    }
}

/* ... */

//Will not compile because Thread is not Comparable<? super Thread>
Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
System.out.println(a.compareTo(b));

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


5

JavaFX (ซึ่งมาพร้อมกับ Java 8) มีคลาส Pair <A, B>


1
การดำเนินการของ hashCode ใน javafx.util.Pairสามารถนำไปสู่การชนกันในกรณีเล็ก ๆ น้อย ๆ การใช้งานใน HashMap / HashTable จะยังคงใช้งานได้เนื่องจาก Java ตรวจสอบความเท่าเทียมกันของค่าเพิ่มเติมจากรหัสแฮช แต่เป็นสิ่งที่ต้องระวัง
sffc

นั่นคือการใช้งาน hashCode ที่เป็นมาตรฐานและแนะนำโดยทั่วไป ชนควรจะคาดหวังโดยใด ๆhashCode()รหัสที่โทร โปรดทราบว่า Java เองไม่ได้เรียกวิธีนี้ มันเป็นรหัสผู้ใช้รวมถึงห้องสมุด
AndrewF

5

ตามที่คนอื่น ๆ ระบุไว้แล้วมันขึ้นอยู่กับกรณีการใช้งานจริง ๆ ว่าคลาส Pair มีประโยชน์หรือไม่

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

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

เช่นเคยมันต้องใช้วิจารณญาณที่มีทักษะ

สำหรับคำถามที่สองของคุณฉันแนะนำคลาส Pair จากไลบรารี Apache Commons สิ่งเหล่านั้นอาจถูกพิจารณาว่าเป็นไลบรารีมาตรฐานเพิ่มเติมสำหรับ Java:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

คุณอาจต้องการดูที่ Apache Commons ' EqualsBuilder , HashCodeBuilderและToStringBuilderซึ่งทำให้การเขียนคลาสค่าสำหรับวัตถุธุรกิจของคุณง่ายขึ้น


URL ที่ปรับปรุงcommons.apache.org/lang/api-release/index.html?org/apache/...ตั้งแต่คอมมอน-lang3 คือออกจากรุ่นเบต้า นี่จะสั้นกว่าโซลูชันลอมบอกของฉันหากคุณใช้คอมมอนส์
แลง


5

ข่าวดีJavaFXมีคู่ค่าคีย์

เพียงแค่เพิ่ม JavaFX เป็นพึ่งพาและนำเข้าjavafx.util.Pair;

c++และใช้เป็นเพียงใน

Pair <Key, Value> 

เช่น

Pair <Integer, Integer> pr = new Pair<Integer, Integer>()

pr.get(key);// will return corresponding value

ข่าวร้ายก็คือว่าไม่ใช่ทุกคนที่ใช้ JavaFX
Michał Dobi Dobrzański

4

ส่วนต่อประสานMap.Entryมาใกล้คู่ c ++ ดูการใช้งานที่เป็นรูปธรรมเช่นAbstractMap.SimpleEntryและ AbstractMap.SimpleImmutableEntry รายการแรกคือ getKey () และที่สองคือ getValue ()


1
OP ทราบแล้วเกี่ยวกับตัวเลือกนี้และมีการพูดคุยกันตามความยาว
คีแกน


3

ตามลักษณะของภาษา Java ฉันคิดว่าผู้คนไม่จำเป็นต้องใช้จริง ๆPairอินเตอร์เฟสมักเป็นสิ่งที่พวกเขาต้องการ นี่คือตัวอย่าง:

interface Pair<L, R> {
    public L getL();
    public R getR();
}

ดังนั้นเมื่อผู้คนต้องการคืนค่าสองค่าพวกเขาสามารถทำสิ่งต่อไปนี้:

... //Calcuate the return value
final Integer v1 = result1;
final String v2 = result2;
return new Pair<Integer, String>(){
    Integer getL(){ return v1; }
    String getR(){ return v2; }
}

นี่เป็นโซลูชันที่มีน้ำหนักเบาและตอบคำถามที่ว่า "อะไรคือความหมายของPair<L,R>" คำตอบคือนี่คือการสร้างส่วนต่อประสานที่มีสองประเภท (อาจแตกต่างกัน) และมีวิธีการคืนค่าแต่ละรายการ มันขึ้นอยู่กับคุณที่จะเพิ่มความหมายเพิ่มเติมเข้าไป ตัวอย่างเช่นถ้าคุณกำลังใช้ตำแหน่งและต้องการที่จะแสดงให้เห็นในรหัสคุณสามารถกำหนดPositionXและPositionYที่มีเพื่อให้ขึ้นInteger Pair<PositionX,PositionY>หากมี JSR 308 คุณอาจใช้Pair<@PositionX Integer, @PositionY Ingeger>เพื่อทำให้ง่ายขึ้น

แก้ไข: สิ่งหนึ่งที่ฉันควรระบุไว้ที่นี่คือคำจำกัดความด้านบนเกี่ยวข้องกับชื่อพารามิเตอร์ประเภทและชื่อเมธอดอย่างชัดเจน นี่คือคำตอบของผู้โต้แย้งว่าข้อมูลPairขาดความหมาย ที่จริงแล้ววิธีการgetLหมายถึง "ให้ฉันองค์ประกอบที่สอดคล้องกับประเภทของพารามิเตอร์ประเภท L" ซึ่งหมายถึงบางสิ่งบางอย่าง

แก้ไข: นี่คือคลาสยูทิลิตี้ง่ายๆที่สามารถทำให้ชีวิตง่ายขึ้น:

class Pairs {
    static <L,R> Pair<L,R> makePair(final L l, final R r){
        return new Pair<L,R>(){
            public L getL() { return l; }
            public R getR() { return r; }   
        };
    }
}

การใช้งาน:

return Pairs.makePair(new Integer(100), "123");

สิ่งที่เกี่ยวกับequals, hashCodeและtoString?
sdgfsdh

นี่เป็นเพียงการนำไปใช้งานที่น้อยที่สุด หากคุณต้องการมากกว่านั้นคุณสามารถเขียนฟังก์ชันตัวช่วยบางอย่างเพื่อให้ง่ายขึ้น แต่คุณยังต้องเขียนโค้ด
Earth Engine

ในการดำเนินการtoStringคุณต้องมีความรู้เพิ่มเติมเกี่ยวกับความสัมพันธ์ระหว่างสองฟิลด์
Earth Engine

จุดของฉันคือการให้classอาจจะดีกว่าเพียงinterfaceเพราะมันสามารถใช้สิ่งเหล่านี้
sdgfsdh

3

แม้จะมีความคล้ายคลึงกันทางไวยากรณ์ Java และ C ++ มีกระบวนทัศน์ที่แตกต่างกันมาก การเขียน C ++ เช่น Java นั้นแย่ C ++ และการเขียน Java อย่าง C ++ นั้นไม่ดี Java

ด้วย IDE ที่ใช้การสะท้อนเช่น Eclipse การเขียนฟังก์ชันที่จำเป็นของคลาส "pair" นั้นรวดเร็วและง่ายดาย สร้างคลาสกำหนดสองฟิลด์ใช้ตัวเลือกเมนู "สร้าง XX" เพื่อเติมคลาสในเวลาไม่กี่วินาที บางทีคุณอาจต้องพิมพ์ "comparTo" อย่างรวดเร็วจริง ๆ ถ้าคุณต้องการอินเทอร์เฟซที่เปรียบเทียบได้

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


2

Pair จะเป็นสิ่งที่ดีในการเป็นหน่วยการก่อสร้างพื้นฐานสำหรับ generics ที่ซับซ้อนเช่นนี้มาจากรหัสของฉัน:

WeakHashMap<Pair<String, String>, String> map = ...

มันเหมือนกับ Haskell's Tuple


1
ตอนนี้ฉันสามารถพูดได้ว่าการใช้ Pair <A, B> ทำให้โค้ดมีข้อมูลน้อยลงและการใช้วัตถุพิเศษแทนการใช้ Pair นั้นดีกว่ามาก
Illarion Kovalchuk

1
ดีขึ้นหรือแย่ลง ลองนึกภาพคุณมีฟังก์ชั่นที่รวมสองข้อโต้แย้ง (เช่นการรวมกราฟเข้าด้วยกัน) และต้องการแคช ที่นี่Pairเหมาะสมที่สุดเนื่องจากไม่มีความหมายพิเศษ การมีชื่อที่ชัดเจนสำหรับแนวคิดที่ชัดเจนนั้นดี แต่การมองหาชื่อที่ "แรก" และ "ที่สอง" ทำงานได้ไม่ดี
maaartinus

2

Simple way Object [] - สามารถใช้เป็น tuple มิติใดก็ได้


2
ทุกขนาดใช่ แต่: ยุ่งยากในการสร้างและไม่ปลอดภัยสำหรับพิมพ์
Michael Piefel

2

สำหรับการเขียนโปรแกรมภาษาเช่น Java โครงสร้างข้อมูลทางเลือกที่ใช้โดยโปรแกรมเมอร์ส่วนใหญ่เพื่อแสดงคู่เช่นโครงสร้างข้อมูลเป็นสองอาร์เรย์และเข้าถึงข้อมูลผ่านดัชนีเดียวกัน

ตัวอย่าง: http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

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

ฉันชอบสิ่งนี้ในห้องสมุดของฉัน

public class Pair<First,Second>{.. }

2

คุณสามารถใช้ห้องสมุดค่าอัตโนมัติของ Google - https://github.com/google/auto/tree/master/value https://github.com/google/auto/tree/master/value

คุณสร้างคลาสนามธรรมขนาดเล็กมากและใส่คำอธิบายประกอบด้วย @AutoValue และตัวประมวลผลคำอธิบายประกอบจะสร้างคลาสที่เป็นรูปธรรมสำหรับคุณที่มีค่าความหมาย


2

นี่คือห้องสมุดบางแห่งที่มีสิ่งอันดับหลายระดับเพื่อความสะดวกของคุณ:

  • JavaTuples สิ่งอันดับจากระดับ 1-10 คือทั้งหมดที่มี
  • JavaSlang Tuples จากระดับ 0-8 และสารพัดหน้าที่อื่น ๆ อีกมากมาย
  • jOOλ สิ่งอันดับจากระดับ 0-16 และสารพัดหน้าที่อื่น ๆ (ข้อจำกัดความรับผิดชอบฉันทำงานกับ บริษัท ผู้ดูแล)
  • ฟังก์ชั่น Java Tuples จากระดับ 0-8 และสารพัดหน้าที่อื่น ๆ อีกมากมาย

ห้องสมุดอื่น ๆ ได้รับการกล่าวถึงอย่างน้อยมีPairtuple

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


2

Brian Goetz, Paul Sandoz และ Stuart Marks อธิบายว่าทำไมในช่วงเซสชัน QA ที่ Devoxx'14

การมีคลาสคู่ทั่วไปในไลบรารีมาตรฐานจะเปลี่ยนเป็นหนี้ทางเทคนิคเมื่อมีการแนะนำประเภทค่า

ดูเพิ่มเติม: Java SE 8 มีคู่หรือ Tuples หรือไม่



1

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

/**
 * The class <code>Pair</code> models a container for two objects wherein the
 * object order is of no consequence for equality and hashing. An example of
 * using Pair would be as the return type for a method that needs to return two
 * related objects. Another good use is as entries in a Set or keys in a Map
 * when only the unordered combination of two objects is of interest.<p>
 * The term "object" as being a one of a Pair can be loosely interpreted. A
 * Pair may have one or two <code>null</code> entries as values. Both values
 * may also be the same object.<p>
 * Mind that the order of the type parameters T and U is of no importance. A
 * Pair&lt;T, U> can still return <code>true</code> for method <code>equals</code>
 * called with a Pair&lt;U, T> argument.<p>
 * Instances of this class are immutable, but the provided values might not be.
 * This means the consistency of equality checks and the hash code is only as
 * strong as that of the value types.<p>
 */
public class Pair<T, U> implements Cloneable {

    /**
     * One of the two values, for the declared type T.
     */
    private final T object1;
    /**
     * One of the two values, for the declared type U.
     */
    private final U object2;
    private final boolean object1Null;
    private final boolean object2Null;
    private final boolean dualNull;

    /**
     * Constructs a new <code>Pair&lt;T, U&gt;</code> with T object1 and U object2 as
     * its values. The order of the arguments is of no consequence. One or both of
     * the values may be <code>null</code> and both values may be the same object.
     *
     * @param object1 T to serve as one value.
     * @param object2 U to serve as the other value.
     */
    public Pair(T object1, U object2) {

        this.object1 = object1;
        this.object2 = object2;
        object1Null = object1 == null;
        object2Null = object2 == null;
        dualNull = object1Null && object2Null;

    }

    /**
     * Gets the value of this Pair provided as the first argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public T getObject1() {

        return object1;

    }

    /**
     * Gets the value of this Pair provided as the second argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public U getObject2() {

        return object2;

    }

    /**
     * Returns a shallow copy of this Pair. The returned Pair is a new instance
     * created with the same values as this Pair. The values themselves are not
     * cloned.
     *
     * @return a clone of this Pair.
     */
    @Override
    public Pair<T, U> clone() {

        return new Pair<T, U>(object1, object2);

    }

    /**
     * Indicates whether some other object is "equal" to this one.
     * This Pair is considered equal to the object if and only if
     * <ul>
     * <li>the Object argument is not null,
     * <li>the Object argument has a runtime type Pair or a subclass,
     * </ul>
     * AND
     * <ul>
     * <li>the Object argument refers to this pair
     * <li>OR this pair's values are both null and the other pair's values are both null
     * <li>OR this pair has one null value and the other pair has one null value and
     * the remaining non-null values of both pairs are equal
     * <li>OR both pairs have no null values and have value tuples &lt;v1, v2> of
     * this pair and &lt;o1, o2> of the other pair so that at least one of the
     * following statements is true:
     * <ul>
     * <li>v1 equals o1 and v2 equals o2
     * <li>v1 equals o2 and v2 equals o1
     * </ul>
     * </ul>
     * In any other case (such as when this pair has two null parts but the other
     * only one) this method returns false.<p>
     * The type parameters that were used for the other pair are of no importance.
     * A Pair&lt;T, U> can return <code>true</code> for equality testing with
     * a Pair&lt;T, V> even if V is neither a super- nor subtype of U, should
     * the the value equality checks be positive or the U and V type values
     * are both <code>null</code>. Type erasure for parameter types at compile
     * time means that type checks are delegated to calls of the <code>equals</code>
     * methods on the values themselves.
     *
     * @param obj the reference object with which to compare.
     * @return true if the object is a Pair equal to this one.
     */
    @Override
    public boolean equals(Object obj) {

        if(obj == null)
            return false;

        if(this == obj)
            return true;

        if(!(obj instanceof Pair<?, ?>))
            return false;

        final Pair<?, ?> otherPair = (Pair<?, ?>)obj;

        if(dualNull)
            return otherPair.dualNull;

        //After this we're sure at least one part in this is not null

        if(otherPair.dualNull)
            return false;

        //After this we're sure at least one part in obj is not null

        if(object1Null) {
            if(otherPair.object1Null) //Yes: this and other both have non-null part2
                return object2.equals(otherPair.object2);
            else if(otherPair.object2Null) //Yes: this has non-null part2, other has non-null part1
                return object2.equals(otherPair.object1);
            else //Remaining case: other has no non-null parts
                return false;
        } else if(object2Null) {
            if(otherPair.object2Null) //Yes: this and other both have non-null part1
                return object1.equals(otherPair.object1);
            else if(otherPair.object1Null) //Yes: this has non-null part1, other has non-null part2
                return object1.equals(otherPair.object2);
            else //Remaining case: other has no non-null parts
                return false;
        } else {
            //Transitive and symmetric requirements of equals will make sure
            //checking the following cases are sufficient
            if(object1.equals(otherPair.object1))
                return object2.equals(otherPair.object2);
            else if(object1.equals(otherPair.object2))
                return object2.equals(otherPair.object1);
            else
                return false;
        }

    }

    /**
     * Returns a hash code value for the pair. This is calculated as the sum
     * of the hash codes for the two values, wherein a value that is <code>null</code>
     * contributes 0 to the sum. This implementation adheres to the contract for
     * <code>hashCode()</code> as specified for <code>Object()</code>. The returned
     * value hash code consistently remain the same for multiple invocations
     * during an execution of a Java application, unless at least one of the pair
     * values has its hash code changed. That would imply information used for 
     * equals in the changed value(s) has also changed, which would carry that
     * change onto this class' <code>equals</code> implementation.
     *
     * @return a hash code for this Pair.
     */
    @Override
    public int hashCode() {

        int hashCode = object1Null ? 0 : object1.hashCode();
        hashCode += (object2Null ? 0 : object2.hashCode());
        return hashCode;

    }

}

การใช้งานนี้ได้รับการทดสอบอย่างถูกต้องตามหน่วยและการใช้งานใน Set and Map นั้นได้รับการทดสอบแล้ว

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


3
จริงตรวจสอบที่ด้านล่างของแต่ละหน้า: "การมีส่วนร่วมของผู้ใช้ที่ได้รับอนุญาตภายใต้ cc-wiki"
amara

อาฉันไม่ได้สังเกตสิ่งนั้น ขอบคุณสำหรับหัวขึ้น. ในกรณีนั้นให้ใช้รหัสตามที่คุณเห็นว่าเหมาะสมภายใต้ใบอนุญาตนั้น
G_H

1
คำถามเกี่ยวกับคู่เทียบเท่า C ++ - ซึ่งได้รับคำสั่ง นอกจากนี้ฉันคิดว่าตราบใดที่มีการอ้างอิงถึงวัตถุของ Pair และสิ่งเหล่านั้นคือการใส่คู่ที่ไม่แน่นอนในคอลเลกชันอาจนำไปสู่พฤติกรรมที่ไม่ได้กำหนด
Mr_and_Mrs_D

1

หากใครต้องการตายง่ายและสะดวกในการใช้งานรุ่นที่ฉันทำของฉันที่ใช้ได้https://github.com/lfac-pt/Java-Pair นอกจากนี้ยังมีการปรับปรุงยินดีอย่างมาก!


1

com.sun.tools.javac.util.Pair เป็นการใช้งานอย่างง่ายของคู่ สามารถพบได้ใน jdk1.7.0_51 \ lib \ tools.jar

นอกเหนือจาก org.apache.commons.lang3.tuple.Pair ไม่ใช่เพียงส่วนต่อประสาน


2
ไม่มีใครควรใช้ API ภายในของ JDK
jpangamarca

0
public class Pair<K, V> {

    private final K element0;
    private final V element1;

    public static <K, V> Pair<K, V> createPair(K key, V value) {
        return new Pair<K, V>(key, value);
    }

    public Pair(K element0, V element1) {
        this.element0 = element0;
        this.element1 = element1;
    }

    public K getElement0() {
        return element0;
    }

    public V getElement1() {
        return element1;
    }

}

การใช้งาน:

Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();

ไม่เปลี่ยนรูปเพียงคู่เดียว!


โอ้ว้าว. อีกอัน? ลองใช้ของคุณกับ Generics ที่ซับซ้อนมากขึ้น - ในบางกรณีอาจไม่สามารถอนุมานประเภทที่เหมาะสมได้ นอกจากนี้ควรเป็นไปได้: Pair<Object, Object> pair = Pair.createPair("abc", "def")แต่ฉันคิดว่าต้องเขียนPair.createPair((Object)"abc", (Object)"def")ด้วยรหัสของคุณหรือไม่
มี QUIT - Anony-Mousse

คุณสามารถแทนที่วิธีคงโดยการนี้ @SuppressWarnings("unchecked") public static <K, V, X, Y> Pair<X, Y> createPair(K key, V value) { return new Pair<X, Y>((X) key, (Y) value); } แต่ผมไม่ทราบว่ามันเป็นวิธีที่ดี
Bastiflew

ไม่นั่นน่าจะเป็นสิ่งที่ทำให้สับสนมากยิ่งขึ้นเท่านั้น จากประสบการณ์ของฉันคอมไพเลอร์อย่างน้อยหนึ่งตัว (ลองใช้ java6, java7, javadoc และ eclipse java คอมไพเลอร์) จะบ่น แบบดั้งเดิมnew Pair<Object, Object>("abc", "def")นั้นน่าเชื่อถือที่สุดในการทดลองของฉัน
มี QUIT - Anony-Mousse
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.