Tương đương với cặp C ++ <L, R> trong Java là gì?


670

Có một lý do chính đáng tại sao không có Pair<L,R>trong Java? Điều gì sẽ tương đương với cấu trúc C ++ này? Tôi thà tránh thực hiện lại của riêng tôi.

Có vẻ như 1.6 đang cung cấp một cái gì đó tương tự ( AbstractMap.SimpleEntry<K,V>), nhưng điều này có vẻ khá phức tạp.


7
Tại sao lại AbstractMap.SimpleEntryphức tạp?
RèmDog

27
Bởi vì tên, tùy ý đặt tên một khóa và một giá trị.
Enerccio


2
@sffc JavaFX không có trên bất kỳ đường dẫn mặc định nào trong JDK7, sử dụng nó yêu cầu các thư viện thời gian chạy JFX được thêm thủ công.
Dây Rehn

3
@Enerccio: Vì vậy, bạn thực sự nói rằng "thứ nhất" và "thứ hai" không phải là tùy ý, trong khi "khóa" và "giá trị" - là? Sau đó, đây là một lý do tốt cho việc không có lớp như vậy trong SDK. Sẽ có một cuộc tranh cãi muôn thuở về việc đặt tên "đúng".
fdreger

Câu trả lời:


400

Trong một chủ đề trêncomp.lang.java.help , Hunter Gratzner đưa ra một số lập luận chống lại sự hiện diện của một Paircấu trúc trong Java. Đối số chính là một lớp Pairkhông truyền đạt bất kỳ ngữ nghĩa nào về mối quan hệ giữa hai giá trị (làm thế nào để bạn biết "thứ nhất" và "thứ hai" nghĩa là gì?).

Một cách thực hành tốt hơn là viết một lớp rất đơn giản, giống như bài mà Mike đề xuất, cho mỗi ứng dụng mà bạn đã thực hiện trong Pairlớp. Map.Entrylà một ví dụ về một cặp mang ý nghĩa của nó trong tên của nó.

Tóm lại, theo tôi, tốt hơn là nên có một lớp Position(x,y), một lớp Range(begin,end)và một lớp Entry(key,value)chứ không phải là chung chung Pair(first,second)mà không cho tôi biết bất cứ điều gì về những gì nó phải làm.


143
Gratzner đang chia tóc. Chúng tôi khá vui khi trả lại một giá trị dưới dạng một lớp nguyên thủy hoặc tích hợp mà không gói gọn nó trong một lớp. Nếu chúng ta trả lại một bộ gồm hàng tá phần tử thì không ai có thể không đồng ý nó nên có lớp riêng. Một nơi nào đó ở giữa là một đường phân chia (mờ). Tôi nghĩ bộ não thằn lằn của chúng ta có thể đối phó với Cặp đủ dễ dàng.
Ian

25
Tôi đồng ý với Ian. Java cho phép bạn trả về int; nó không bắt buộc bạn phải tạo bí danh cho int mỗi khi bạn sử dụng nó. Các cặp không khác nhau lắm.
Clément

5
Nếu chúng ta có thể giải nén một cặp trực tiếp đến các biến cục bộ của bạn hoặc chuyển tiếp nó đến một phương thức có hai đối số, thì Pair sẽ là một lớp hữu ích. Vì chúng ta không thể giải nén nó như thế này, tạo ra một lớp có ý nghĩa và giữ các giá trị cùng nhau trông không quá tệ. Và, nếu bạn thực sự muốn một cặp bất chấp những hạn chế, luôn có Object [2] + phôi :-)
marcus

Vấn đề là nếu bạn không đồng ý với Gratzner, thì sẽ có triển khai Ghép đôi ở một số nơi. Cả Commons và Guava đều có IIRC. Sử dụng chúng. Nhưng để đặt một cái gì đó vào các thư viện Java chính có nghĩa là đó là Cách làm việc cao quý và được chấp thuận (có viết hoa) và vì mọi người không đồng ý với nó, chúng ta không nên đặt nó ở đó. Có đủ hành trình trong các lib cũ như vậy, chúng ta không cần phải đặt nhiều hơn ở đó.
Haakon Løtveit

1
@Dragas Khi tôi cần một cặp giá trị thì đó không phải là ... nghiêm túc sao?
idclev 463035818

156

Đây là Java. Bạn phải tạo lớp Cặp được tùy chỉnh của riêng bạn với tên trường mô tả và tên trường, và đừng bận tâm rằng bạn sẽ phát minh lại bánh xe bằng cách viết hàm hashCode () / bằng () hoặc thực hiện lặp đi lặp lại.


61
Điều đó không trả lời câu hỏi "tại sao". (Trừ khi bạn coi 'đây là java' một câu trả lời)
Nikita Rybak

127
+1 để chế nhạo tính dài dòng của Java. -1 vì không thực sự trả lời câu hỏi.
Bennett McElwee

19
Trình giả lập Java sẽ ổn nếu bạn đã chỉ vào Apache Commong Lang, trong đó có một lớp Pair.
haylem

6
Hoặc bạn chỉ có thể sử dụngSimpleImmutableEntry
RèmDog

33
Câu đầu tiên KHÔNG trả lời câu hỏi "tại sao?". Đây là Java và đó là nó.
masterziv

103

Lớp cặp tương thích HashMap:

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
Bạn có thể muốn xóa các setters, và thực hiện trận chung kết đầu tiên và thứ hai, do đó làm cho cặp đôi không thay đổi. (Nếu ai đó thay đổi các thành phần sau khi sử dụng chúng làm khóa băm, những điều kỳ lạ sẽ xảy ra).
Thilo

21
return "(" + first.toString () + "," + second.toString () + ")" trong phương thức toString () có thể ném NullPulumExceptions. Điều này tốt hơn: return "(" + first + "," + second + ")";
Juha Syrjälä

6
Ngoài ra, hãy đánh dấu cặp là "cuối cùng" hoặc thay đổi dòng bằng đầu tiên thành 'if (other! = Null && this.getClass () == other.getClass ())'
sargas

8
Xin lỗi vì câu hỏi không ngẫu nhiên, nhưng tại sao bạn lại có lệnh gọi super () trong hàm tạo?
Ibrahim

6
@Ibrahim: Trong trường hợp này, nó không cần thiết --- hành vi hoàn toàn giống nhau nếu bạn lấy super()ra. Thông thường tôi sẽ tắt nó nếu nó là tùy chọn, giống như nó ở đây.
Chris Jester-Young

53

Cặp ngắn nhất mà tôi có thể nghĩ ra là như sau, sử dụng Lombok :

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

Nó có tất cả những lợi ích của các câu trả lời từ @arturh (trừ khả năng so sánh), nó có hashCode, equals, toStringvà một tĩnh “constructor”.


Tiện lợi! Thích nó!
Ahmet Ipkin


31

Một cách khác để thực hiện Ghép đôi với.

  • Các lĩnh vực bất biến công cộng, tức là cấu trúc dữ liệu đơn giản.
  • So sánh.
  • Băm đơn giản và bằng.
  • Nhà máy đơn giản để bạn không phải cung cấp các loại. ví dụ: Pair.of ("xin chào", 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
Tôi thích phương pháp tĩnh nhà máy of. Nó nhắc nhở Google Guava bộ sưu tập bất biến.
Jarek Przygódzki

7
Bạn đang tại một số điểm casting o1để Comparable, mặc dù không có gì cho thấy nó sẽ thực sự thực hiện giao diện đó. Nếu đó là một yêu cầu, FIRSTtham số loại nên được FIRST extends Comparable<?>.
G_H

Tôi không phải là một người java, vì vậy xin vui lòng tha thứ cho tôi vì sự thiếu hiểu biết của tôi, nhưng bạn đã nghĩ đến loại lớp trợ giúp nào trong các bình luận TODO?

3
31 là một hằng số xấu cho hashCode. Ví dụ: nếu bạn sử dụng HashMap được khóa bởi Pair <Integer, Integer> cho bản đồ 2D, bạn sẽ nhận được nhiều va chạm. Ví dụ: (a * 65497) ^ b sẽ phù hợp hơn.
Michał Zieliński

1
@MarioCarneiro ^ là xor, không phải quyền lực
Michał Zieliński

27

Làm thế nào về http://www.javatuples.org/index.html Tôi đã thấy nó rất hữu ích.

Các javatuples cung cấp cho bạn các lớp tuple từ một đến mười yếu tố:

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
Hài hước, nhưng có ít nhất 5 lớp nhiều hơn tôi có thể tưởng tượng để sử dụng.
maaartinus

3
@maaartinus Ít nhất 10 nhiều hơn tôi sẽ sử dụng.
Boann

7
@Boann: OK, tôi ở lại sửa. Tôi đã từng sử dụng Pairvà có thể tưởng tượng sử dụng Tripletcó thể cứ sau 50 năm. Bây giờ tôi sử dụng Lombok và tạo một lớp 4 dòng nhỏ mỗi khi tôi cần một cặp. Vì vậy, "10 quá nhiều" là chính xác.
maaartinus

5
Chúng ta có cần một Bottom (0 element)lớp học không? :)
Động cơ Trái đất

2
Wow điều này thật xấu xí. Tôi biết họ đang cố gắng làm cho nó rõ ràng, nhưng một Tuple với các thông số quá tải như trong C # sẽ đẹp hơn.
arviman

12

Nó phụ thuộc vào những gì bạn muốn sử dụng nó cho. Lý do điển hình để làm như vậy là lặp đi lặp lại trên các bản đồ mà bạn chỉ cần làm điều này (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
Tôi không chắc chắn một lớp tùy chỉnh sẽ giúp ích trong trường hợp này :)
Nikita Rybak

31
"Lý do điển hình để làm như vậy là lặp đi lặp lại trên bản đồ". Có thật không?
Bennett McElwee

12

android cung cấp Pairlớp ( http://developer.android.com/reference/android/util/Pair.html ), tại đây triển khai:

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(..)yêu cầu thư viện ổi.
Markus L

3
Thay đổi nó thành Objects.equals(...)Java đã có từ năm 2011 (1.7).
AndrewF

9

Vấn đề lớn nhất có lẽ là người ta không thể đảm bảo tính bất biến trên A và B (xem Cách đảm bảo rằng các tham số loại là bất biến ) vì vậy hashCode()có thể cho kết quả không nhất quán cho cùng một cặp sau khi được chèn vào bộ sưu tập (ví dụ: điều này sẽ cho hành vi không xác định , xem Xác định bằng với các trường có thể thay đổi ). Đối với một lớp Cặp cụ thể (không chung chung), lập trình viên có thể đảm bảo tính bất biến bằng cách cẩn thận chọn A và B là bất biến.

Dù sao, xóa các cảnh báo chung từ câu trả lời của @ 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 + ')';
    }
}

Bổ sung / chỉnh sửa rất được hoan nghênh :) Đặc biệt tôi không chắc chắn về việc sử dụng của tôi Pair<?, ?>.

Để biết thêm thông tin về lý do tại sao cú pháp này, hãy xem Đảm bảo rằng các đối tượng triển khai Có thể so sánh và để được giải thích chi tiết Làm thế nào để triển khai một max(Comparable a, Comparable b)hàm chung trong Java?


Vì số nguyên Java là 32 bit, sẽ không nhân mã băm đầu tiên với 31 có nghĩa là nó tràn? Nó sẽ không tốt hơn để thực hiện một OR độc quyền?
Dan

@Dan cảm thấy thoải mái khi chỉnh sửa Tôi đã chuyển khỏi java :)
Mr_and_Mrs_D

5

Theo tôi, không có Cặp nào trong Java bởi vì, nếu bạn muốn thêm chức năng bổ sung trực tiếp vào cặp (ví dụ: Có thể so sánh), bạn phải ràng buộc các loại. Trong C ++, chúng tôi không quan tâm và nếu các kiểu soạn một cặp không có operator <, thì pair::operator <nó cũng sẽ không được biên dịch.

Một ví dụ về So sánh không có giới hạn:

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));

Một ví dụ về So sánh với kiểm tra thời gian biên dịch để xem các đối số loại có thể so sánh được không:

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));

Điều này là tốt, nhưng lần này bạn không thể sử dụng các loại không thể so sánh làm đối số loại trong Cặp. Người ta có thể sử dụng nhiều Bộ so sánh cho Cặp trong một số lớp tiện ích, nhưng người C ++ có thể không nhận được. Một cách khác là viết nhiều lớp trong một hệ thống phân cấp kiểu với các giới hạn khác nhau trên các đối số kiểu, nhưng có quá nhiều giới hạn có thể và sự kết hợp của chúng ...


5

JavaFX (đi kèm với Java 8) có lớp Ghép <A, B>


1
Việc triển khai hashCode trong javafx.util.Pair có thể dẫn đến va chạm trong các trường hợp tầm thường. Việc sử dụng nó trong HashMap / HashTable vẫn sẽ hoạt động do Java kiểm tra sự bằng nhau của các giá trị ngoài mã băm, nhưng đó là điều cần chú ý.
sffc

Đó là một triển khai hashCode rất chuẩn và thường được đề xuất. Va chạm nên được dự kiến ​​bởi bất kỳ mã nào gọi hashCode(). Lưu ý rằng chính Java không gọi phương thức này. Nó dành cho mã người dùng, bao gồm các thư viện.
AndrewF

5

Như nhiều người khác đã nêu, nó thực sự phụ thuộc vào trường hợp sử dụng nếu một lớp Pair có hữu ích hay không.

Tôi nghĩ đối với một hàm trợ giúp riêng thì việc sử dụng lớp Pair là hoàn toàn hợp pháp nếu điều đó làm cho mã của bạn dễ đọc hơn và không đáng để tạo ra một lớp giá trị khác với tất cả mã tấm nồi hơi của nó.

Mặt khác, nếu mức độ trừu tượng của bạn yêu cầu bạn ghi lại rõ ràng ngữ nghĩa của lớp có chứa hai đối tượng hoặc giá trị, thì bạn nên viết một lớp cho nó. Thông thường đó là trường hợp nếu dữ liệu là một đối tượng kinh doanh.

Như mọi khi, nó đòi hỏi sự phán đoán lành nghề.

Đối với câu hỏi thứ hai của bạn, tôi đề xuất lớp Pair từ các thư viện Apache Commons. Chúng có thể được coi là thư viện chuẩn mở rộng cho Java:

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

Bạn cũng có thể muốn xem EqualsBuilder , HashCodeBuilderToStringBuilder của Apache Commons để đơn giản hóa việc viết các lớp giá trị cho các đối tượng kinh doanh của bạn.


URL được cập nhật là commons.apache.org/lang/api-release/index.html?org/apache/ mẹo vì commons-lang3 đã hết beta. Điều này thậm chí còn ngắn hơn giải pháp Lombok của riêng tôi nếu bạn đã sử dụng commons-lang 3.
Michael Piefel


5

Tin tốt JavaFXcó một cặp giá trị quan trọng.

chỉ cần thêm javafx làm phụ thuộc và nhập javafx.util.Pair;

và sử dụng đơn giản như trong c++.

Pair <Key, Value> 

ví dụ

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

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

Tin xấu là không phải ai cũng sử dụng JavaFX
Michał Dobi Dobrzański

4

Giao diện Map.Entry khá gần với cặp c ++. Nhìn vào cách triển khai cụ thể, như AbstractMap.SimpleEntry và AbstractMap.SimpleImmutableEntry Mục đầu tiên là getKey () và thứ hai là getValue ().


1
OP đã biết về tùy chọn này và nó đã được thảo luận rất lâu.
Keegan


3

Theo bản chất của ngôn ngữ Java, tôi cho rằng mọi người không thực sự yêu cầu Pair, giao diện thường là thứ họ cần. Đây là một ví dụ:

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

Vì vậy, khi mọi người muốn trả về hai giá trị, họ có thể thực hiện như sau:

... //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; }
}

Đây là một giải pháp khá nhẹ và nó trả lời cho câu hỏi "ngữ nghĩa của a là Pair<L,R>gì?". Câu trả lời là, đây là một bản dựng giao diện với hai loại (có thể khác nhau) và nó có các phương thức để trả về từng loại. Tùy thuộc vào bạn để thêm ngữ nghĩa cho nó. Ví dụ: nếu bạn đang sử dụng Position và THỰC SỰ muốn chỉ ra nó trong mã của bạn, bạn có thể xác định PositionXPositionYcó chứa Integer, để tạo nên một Pair<PositionX,PositionY>. Nếu JSR 308 có sẵn, bạn cũng có thể sử dụng Pair<@PositionX Integer, @PositionY Ingeger>để đơn giản hóa điều đó.

EDIT: Một điều tôi nên chỉ ra ở đây là định nghĩa trên liên quan rõ ràng đến tên tham số loại và tên phương thức. Đây là một câu trả lời cho những người lập luận rằng Pairthiếu thông tin ngữ nghĩa. Trên thực tế, phương thức này getLcó nghĩa là "đưa cho tôi phần tử tương ứng với loại tham số loại L", nghĩa là gì đó.

EDIT: Đây là một lớp tiện ích đơn giản có thể làm cho cuộc sống dễ dàng hơn:

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; }   
        };
    }
}

sử dụng:

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

Thế còn equals, hashCodetoString?
sdgfsdh

tốt, đây chỉ là một thực hiện tối thiểu. Nếu bạn cần nhiều hơn thế, bạn có thể viết một số hàm trợ giúp để làm cho nó dễ dàng hơn, nhưng bạn vẫn cần phải viết mã.
Động cơ Trái đất

Để thực hiện, toStringbạn cần có thêm kiến ​​thức về mối quan hệ giữa hai lĩnh vực.
Động cơ Trái đất

Quan điểm của tôi là cung cấp một thứ classcó thể tốt hơn chỉ interfacevì nó có thể thực hiện những điều này.
sdgfsdh

3

Mặc dù giống nhau về mặt cú pháp, Java và C ++ có mô hình rất khác nhau. Viết C ++ như Java là C ++ xấu và viết Java như C ++ là Java xấu.

Với IDE dựa trên phản chiếu như Eclipse, việc viết chức năng nhất thiết của lớp "cặp" là nhanh chóng và đơn giản. Tạo lớp, xác định hai trường, sử dụng các tùy chọn menu "Tạo XX" khác nhau để điền vào lớp trong vài giây. Có lẽ bạn phải gõ "so sánh" thật nhanh nếu bạn muốn giao diện So sánh.

Với các tùy chọn khai báo / định nghĩa riêng biệt trong ngôn ngữ, trình tạo mã C ++ không tốt lắm, do đó, việc viết tay các lớp tiện ích ít tốn thời gian hơn. Vì cặp này là một mẫu, bạn không phải trả tiền cho các chức năng bạn không sử dụng và cơ sở typedef cho phép gán các kiểu chữ có ý nghĩa cho mã, do đó, sự phản đối về "không có ngữ nghĩa" không thực sự theo kịp.


2

Cặp sẽ là một công cụ tốt, là một đơn vị xây dựng cơ bản cho một tổng quát phức tạp, ví dụ, đây là từ mã của tôi:

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

Nó cũng giống như Tuple của Haskell


1
Bây giờ tôi có thể nói rằng, việc sử dụng Cặp <A, B> làm cho mã ít thông tin hơn và triển khai các đối tượng đặc biệt thay vì sử dụng Cặp tốt hơn nhiều
Illarion Kovalchuk

1
Tốt hơn hoặc xấu hơn. Hãy tưởng tượng bạn có một hàm kết hợp hai đối số của nó (ví dụ: hợp nhất các biểu đồ thành một) và cần lưu trữ nó. Ở đây, Pairlà tối ưu vì không có ngữ nghĩa đặc biệt. Có một tên rõ ràng cho một khái niệm rõ ràng là tốt, nhưng tìm kiếm một tên trong đó "thứ nhất" và "thứ hai" hoạt động tốt là không.
maaartinus

2

Đối tượng cách đơn giản [] - có thể được sử dụng như một tuple giảm bớt anу


2
Bất kỳ kích thước, có. Nhưng: Cồng kềnh để tạo, và không an toàn.
Michael Piefel

2

Đối với các ngôn ngữ lập trình như Java, cấu trúc dữ liệu thay thế được hầu hết các lập trình viên sử dụng để biểu diễn cặp như cấu trúc dữ liệu là hai mảng và dữ liệu được truy cập thông qua cùng một chỉ mục

thí dụ: http://www-igm.univ-mlv.fr/~lecroq/opes/node8.html#SECTION0080

Điều này không lý tưởng vì dữ liệu nên được liên kết với nhau, nhưng cũng khá rẻ. Ngoài ra, nếu trường hợp sử dụng của bạn yêu cầu lưu trữ tọa độ thì tốt hơn là xây dựng cấu trúc dữ liệu của riêng bạn.

Tôi có một cái gì đó như thế này trong thư viện của tôi

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

2

Bạn có thể sử dụng thư viện AutoValue của Google - https://github.com/google/auto/tree/master/value .

Bạn tạo một lớp trừu tượng rất nhỏ và chú thích nó bằng @AutoValue và bộ xử lý chú thích tạo ra một lớp cụ thể cho bạn có giá trị ngữ nghĩa.


2

Dưới đây là một số thư viện có nhiều mức độ để thuận tiện cho bạn:

  • JavaTuples . Tuples từ độ 1-10 là tất cả những gì nó có.
  • JavaSlang . Tuples từ độ 0-8 và rất nhiều tính năng chức năng khác.
  • CHÀO MỪNG . Tuples từ độ 0-16 và một số tính năng chức năng khác. (Khước từ, tôi làm việc cho công ty bảo trì)
  • Chức năng Java . Tuples từ độ 0-8 và rất nhiều tính năng chức năng khác.

Các thư viện khác đã được đề cập để chứa ít nhất Pair bộ dữ liệu.

Cụ thể, trong bối cảnh lập trình chức năng sử dụng nhiều kiểu gõ cấu trúc, thay vì gõ danh nghĩa ( như được ủng hộ trong câu trả lời được chấp nhận ), các thư viện và bộ dữ liệu của chúng rất tiện dụng.



2

một triển khai lombok terse khác

import lombok.Value;

@Value(staticConstructor = "of")
public class Pair<F, S> {
    private final F first;
    private final S second;
}

1

Tôi nhận thấy tất cả các cài đặt Cặp được đặt rải rác ở đây thuộc tính có nghĩa là thứ tự của hai giá trị. Khi tôi nghĩ về một cặp, tôi nghĩ về sự kết hợp của hai món đồ trong đó thứ tự của hai thứ không quan trọng. Đây là việc tôi thực hiện một cặp không có thứ tự, có hashCodeequalsghi đè để đảm bảo hành vi mong muốn trong các bộ sưu tập. Cũng có thể nhân bản.

/**
 * 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;

    }

}

Việc triển khai này đã được kiểm tra đúng đơn vị và việc sử dụng trong Tập hợp và Bản đồ đã được thử.

Lưu ý rằng tôi không yêu cầu phát hành này trong phạm vi công cộng. Đây là mã tôi vừa viết để sử dụng trong một ứng dụng, vì vậy nếu bạn sẽ sử dụng nó, xin vui lòng không tạo một bản sao trực tiếp và gây rối với các nhận xét và tên một chút. Bắt trôi của tôi?


3
trên thực tế, hãy kiểm tra phần dưới cùng của mỗi trang: "đóng góp của người dùng được cấp phép theo cc-wiki"
amara

Ah, tôi đã không nhận thấy điều đó. Cảm ơn cho những người đứng đầu lên. Trong trường hợp đó, sử dụng mã khi bạn thấy phù hợp với giấy phép đó.
G_H

1
Câu hỏi là về một cặp tương đương C ++ - được đặt hàng. Ngoài ra tôi nghĩ rằng miễn là người ta có tham chiếu đến đối tượng của Cặp và đó là các cặp chèn có thể thay đổi trong các bộ sưu tập có thể dẫn đến hành vi không xác định.
Mr_and_Mrs_D


1

com.sun.tools.javac.util.Pair là một triển khai đơn giản của một cặp. Nó có thể được tìm thấy trong jdk1.7.0_51 \ lib \ tools.jar.

Khác với org.apache.commons.lang3.tuple.Pair, nó không chỉ là một giao diện.


2
Mặc dù vậy, không ai nên sử dụng API nội bộ 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;
    }

}

sử dụng :

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

Bất biến, chỉ có một đôi!


Tuyệt vời. Một số khác? Hãy thử sử dụng của bạn với Generics phức tạp hơn - tại một số điểm, nó sẽ không thể suy ra các loại thích hợp. Thêm vào đó, những điều sau đây nên có thể: Pair<Object, Object> pair = Pair.createPair("abc", "def")nhưng tôi nghĩ người ta cần viết Pair.createPair((Object)"abc", (Object)"def")bằng mã của bạn?
Có QUIT - Anony-Mousse

bạn có thể thay thế phương thức tĩnh bằng cách này: @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); } nhưng tôi không biết liệu đó có phải là một cách thực hành hay không
Bastiflew

Không, điều đó có khả năng sẽ chỉ làm hỏng mọi thứ nhiều hơn. Theo kinh nghiệm của tôi, ít nhất một trong số các trình biên dịch (thử java6, java7, javadoc và trình biên dịch java nhật thực) sẽ phàn nàn. Truyền thống new Pair<Object, Object>("abc", "def")là đáng tin cậy nhất trong các thí nghiệm của tôi.
Có QUIT - Anony-Mousse
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.