Một tập hợp các cặp giá trị Java? (bộ dữ liệu?)


345

Tôi thích cách Java có Bản đồ nơi bạn có thể xác định loại của từng mục trong bản đồ, ví dụ <String, Integer>.

Thứ tôi đang tìm kiếm là một loại bộ sưu tập trong đó mỗi thành phần trong bộ sưu tập là một cặp giá trị. Mỗi giá trị trong cặp có thể có loại riêng (như ví dụ Chuỗi và Số nguyên ở trên), được xác định tại thời điểm khai báo.

Bộ sưu tập sẽ duy trì thứ tự đã cho và sẽ không coi một trong các giá trị là một khóa duy nhất (như trong bản đồ).

Về cơ bản tôi muốn có thể xác định ARRAY loại <String,Integer>hoặc bất kỳ loại nào khác.

Tôi nhận ra rằng tôi có thể tạo một lớp không có gì ngoài 2 biến trong đó, nhưng điều đó dường như quá dài dòng.

Tôi cũng nhận ra rằng tôi có thể sử dụng một mảng 2D, nhưng do các loại khác nhau mà tôi cần sử dụng, tôi phải biến chúng thành các mảng của ĐỐI TƯỢNG, và sau đó tôi phải bỏ qua mọi lúc.

Tôi chỉ cần lưu trữ các cặp trong bộ sưu tập, vì vậy tôi chỉ cần hai giá trị cho mỗi mục nhập. Có một cái gì đó như thế này tồn tại mà không đi theo lộ trình lớp học? Cảm ơn!


tôi tự hỏi Guava có thể có một lớp học cho điều này cũng.
Sikorski

Quả ổi khá chống Pair, và những người ở Google đã đi xa hơn để tạo ra một sự thay thế tốt hơn nhiều - Tự động / Giá trị . Nó cho phép bạn dễ dàng tạo được gõ lớp kiểu giá trị, với ngữ nghĩa bình đẳng / hashCode thích hợp. Bạn sẽ không bao giờ cần một Pairloại nữa!
dimo414

Câu trả lời:


255

Lớp Pair là một trong những ví dụ chung về "gimme" đủ dễ để bạn tự viết. Ví dụ, ngoài đỉnh đầu của tôi:

public class Pair<L,R> {

  private final L left;
  private final R right;

  public Pair(L left, R right) {
    assert left != null;
    assert right != null;

    this.left = left;
    this.right = right;
  }

  public L getLeft() { return left; }
  public R getRight() { return right; }

  @Override
  public int hashCode() { return left.hashCode() ^ right.hashCode(); }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Pair)) return false;
    Pair pairo = (Pair) o;
    return this.left.equals(pairo.getLeft()) &&
           this.right.equals(pairo.getRight());
  }

}

Và vâng, điều này tồn tại ở nhiều nơi trên Net, với mức độ hoàn thiện và tính năng khác nhau. (Ví dụ của tôi ở trên được dự định là bất biến.)


17
Tôi thích điều này, nhưng bạn nghĩ gì về việc làm cho các trường bên trái và bên phải công khai? Rõ ràng là lớp Pair sẽ không bao giờ có bất kỳ logic nào được liên kết và tất cả các máy khách sẽ cần quyền truy cập vào 'trái' và 'phải', vậy tại sao không làm cho nó dễ dàng?
Lập trình viên ngoài vòng pháp luật

43
Ừm ... không, nó sẽ không. Các trường được đánh dấu là cuối cùng, vì vậy chúng không thể được chỉ định lại. Và nó không an toàn vì 'trái' và 'phải' có thể thay đổi. Trừ khi getLeft () / getRight () trả lại các bản sao phòng thủ (vô dụng trong trường hợp này), tôi không thấy vấn đề lớn là gì.
Lập trình viên ngoài vòng pháp luật

17
Lưu ý rằng hashCode () như hiện tại sẽ cho cùng một giá trị nếu trái và phải được hoán đổi. Có lẽ:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze

68
Vì vậy, nếu tôi hiểu chính xác, việc đưa ra một lớp cặp đơn giản sẽ gây ra lỗi cú pháp, phương thức hashCode phụ, ngoại lệ con trỏ null, không có phương thức so sánh, câu hỏi thiết kế ... và mọi người vẫn ủng hộ việc triển khai lớp này trong khi nó tồn tại trong commons Apache. Xin vui lòng, chỉ cần sao chép mã nếu bạn không muốn bao gồm JAR nhưng ngừng phát minh lại bánh xe!
cquezel

38
Bạn có ý nghĩa gì "đủ dễ để tự viết"? Đó là kỹ thuật phần mềm khủng khiếp . Các lớp bộ điều hợp N ^ 2 để chuyển đổi giữa MyPair và Ai đóElsesPair cũng được coi là đủ dễ dàng để tự viết?
djechlin

299

Tóm tắtMap.SimpleEntry

Dễ dàng bạn đang tìm kiếm điều này:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

Làm thế nào bạn có thể điền vào nó?

java.util.Map.Entry<String,Integer> pair1=new java.util.AbstractMap.SimpleEntry<>("Not Unique key1",1);
java.util.Map.Entry<String,Integer> pair2=new java.util.AbstractMap.SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);

Điều này đơn giản hóa để:

Entry<String,Integer> pair1=new SimpleEntry<>("Not Unique key1",1);
Entry<String,Integer> pair2=new SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);

Và, với sự trợ giúp của một createEntryphương pháp, có thể giảm thêm mức độ chi tiết để:

pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));

ArrayListkhông phải là cuối cùng, nó có thể được phân lớp để hiển thị một ofphương thức (và createEntryphương thức đã nói ở trên ), dẫn đến việc tóm tắt cú pháp:

TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);

11
FYI: Điều đó được đề xuất SimpleEntrycó một setValuephương pháp bỏ qua lớp anh chị em là bất biến. Như vậy tên SimpleImmutableEntry.
Basil Bourque

3
Điều này tốt hơn là tự thực hiện. Thực tế là nó dễ thực hiện không phải là một cái cớ để thực hiện nó mỗi lần.
pevogam

6
Tôi sẽ không coi đó là "tiêu chuẩn." Entrycó nghĩa là một cặp khóa-giá trị và điều đó tốt cho điều đó. Nhưng nó có điểm yếu là một tuple thực sự. Ví dụ, việc hashcodethực hiện SimpleEntrychỉ xors mã của các phần tử, do đó <a,b>băm đến cùng giá trị như <b,a>. Việc triển khai bộ dữ liệu thường sắp xếp theo từ vựng, nhưng SimpleEntry không thực hiện Comparable. Vì vậy, hãy cẩn thận ở ngoài đó ...
Gene

166

Java 9+

Trong Java 9, bạn có thể chỉ cần viết: Map.entry(key, value) để tạo một cặp bất biến.

Lưu ý: phương pháp này không cho phép các khóa hoặc giá trị là null. Ví dụ: nếu bạn muốn cho phép giá trị null, bạn muốn thay đổi giá trị này thành : Map.entry(key, Optional.ofNullable(value)).


Java 8+

Trong Java 8, bạn có thể sử dụng mục đích chung hơn javafx.util.Pairđể tạo một cặp tuần tự, bất biến. Lớp này không cho phép khóa null và giá trị null. (Trong Java 9, lớp này được bao gồm trong javafx.basemô-đun). EDIT: Kể từ Java 11, JavaFX đã được tách rời khỏi JDK, do đó, bạn cần thêm phần tạo tác maven org.openjfx: javafx-base.


Java 6+

Trong Java 6 trở lên, bạn có thể sử dụng nhiều chi tiết hơn AbstractMap.SimpleImmutableEntrycho một cặp không thay đổi hoặc AbstractMap.SimpleEntrycho một cặp có giá trị có thể thay đổi. Các lớp này cũng cho phép các khóa null và giá trị null và được tuần tự hóa.


Android

Nếu bạn đang viết cho Android, chỉ cần sử dụng Pair.create(key, value)để tạo một cặp bất biến.


Cộng đồng Apache

Apache Commons Langcung cấp sự hữu ích Pair.of(key, value)để tạo ra một cặp bất biến, có thể so sánh, tuần tự hóa.


Bộ sưu tập Eclipse

Nếu bạn đang sử dụng các cặp có chứa nguyên thủy, Bộ sưu tập Eclipse cung cấp một số lớp cặp nguyên thủy rất hiệu quả sẽ tránh được tất cả các hộp tự động không hiệu quả và tự động mở hộp.

Chẳng hạn, bạn có thể sử dụng PrimitiveTuples.pair(int, int)để tạo một IntIntPairhoặc PrimitiveTuples.pair(float, long)để tạo một FloatLongPair.


Dự án Lombok

Sử dụng Project Lombok , bạn có thể tạo một lớp cặp bất biến chỉ bằng cách viết:

@Value
public class Pair<K, V> {
    K key;
    V value;
}

Lombok sẽ điền vào các nhà xây dựng, thu khí, equals(), hashCode(), và toString()phương pháp để bạn tự động trong bytecode tạo ra. Nếu bạn muốn một phương thức nhà máy tĩnh thay vì hàm tạo, ví dụ: a Pair.of(k, v), chỉ cần thay đổi chú thích thành : @Value(staticConstructor = "of").


Nếu không thì

Nếu không có giải pháp nào ở trên làm nổi thuyền của bạn, bạn chỉ cần sao chép và dán mã sau đây (không giống như lớp được liệt kê trong câu trả lời được chấp nhận, bảo vệ chống lại NullPulumExceptions):

import java.util.Objects;

public class Pair<K, V> {

    public final K key;
    public final V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public boolean equals(Object o) {
        return o instanceof Pair && Objects.equals(key, ((Pair<?,?>)o).key) && Objects.equals(value, ((Pair<?,?>)o).value);
    }

    public int hashCode() {
        return 31 * Objects.hashCode(key) + Objects.hashCode(value);
    }

    public String toString() {
        return key + "=" + value;
    }
}

3
Tuy nhiên, JavaFX chỉ dành cho máy tính để bàn, thêm một phụ thuộc không cần thiết cho các môi trường không phải máy tính để bàn (ví dụ: máy chủ).
foo

2
Bộ sưu tập Eclipse cũng có một Pairgiao diện cho các đối tượng, có thể được khởi tạo bằng cách gọi Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/ khăn
Donald Raab

2
Cảm giác này giống như câu trả lời thực sự của Java. TẤT CẢ các triển khai có sẵn. Rất kỹ lưỡng, cảm ơn nhiều!
Mike

Xin chào @ Tôi đang sử dụng một danh sách List<Pair<Integer, String> listOfTuple. Làm thế nào để tôi sử dụng listOfTuple.add()? Nếu tôi làm - listOfTuple.add(Pair<someInteger, someString>);, điều này sẽ không hoạt động. Cảm ơn trước.
Me_developer

phiên bản Android không có sẵn trong thử nghiệm đơn vị, điều này làm cho nó không thực sự hữu ích ...
OznOg


31

Apache chung3 có lớp Pair và một vài thư viện khác được đề cập trong luồng này Tương đương với cặp C ++ <L, R> trong Java là gì?

Ví dụ phù hợp với yêu cầu từ câu hỏi ban đầu của bạn:

List<Pair<String, Integer>> myPairs = new ArrayList<Pair<String, Integer>>();
myPairs.add(Pair.of("val1", 11));
myPairs.add(Pair.of("val2", 17));

//...

for(Pair<String, Integer> pair : myPairs) {
  //following two lines are equivalent... whichever is easier for you...
  System.out.println(pair.getLeft() + ": " + pair.getRight());
  System.out.println(pair.getKey() + ": " + pair.getValue());
}

1
Đây là tùy chọn đơn giản nhất nếu Apache Commons có sẵn.
Kip


15

Còn lớp "Apache Commons Lang 3" Pairvà các lớp con tương đối thì sao?

    import org.apache.commons.lang3.tuple.ImmutablePair;
    import org.apache.commons.lang3.tuple.Pair;
    ...
    @SuppressWarnings("unchecked")
    Pair<String, Integer>[] arr = new ImmutablePair[]{
            ImmutablePair.of("A", 1),
            ImmutablePair.of("B", 2)};

    // both access the 'left' part
    String key = arr[0].getKey();
    String left = arr[0].getLeft();

    // both access the 'right' part
    Integer value = arr[0].getValue();
    Integer right = arr[0].getRight();

ImmutablePairlà một lớp con cụ thể không cho phép các giá trị trong cặp được sửa đổi, nhưng có những cách triển khai khác với ngữ nghĩa khác nhau. Đây là tọa độ Maven, nếu bạn cần chúng.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

11

Bạn có thể viết một lớp Cặp <A, B> chung và sử dụng lớp này trong một mảng hoặc danh sách. Có, bạn phải viết một lớp, nhưng bạn có thể sử dụng lại cùng một lớp cho tất cả các loại, vì vậy bạn chỉ phải thực hiện một lần.


Tôi muốn thấy một ví dụ về điều đó!
DivideByHero

1
Dan, điều tồi tệ về điều đó là không thể chấp nhận, ví dụ: chỉ cặp <Chuỗi, Số nguyên> vì loại xóa, không? Nhưng Java cũng vậy ...
Julian Weiss


Tôi không nghĩ rằng nó sẽ hoạt động cho các mảng đơn giản, nhưng nó chắc chắn sẽ hoạt động cho các Bộ sưu tập khác.
Lập trình viên ngoài vòng pháp luật

@Outlaw Lập trình viên: Điểm hay, bạn có thể sử dụng nó với mảng, nhưng thật xấu khi bạn phải sử dụng hack để tạo mảng chung.
Dan Dyer

7

Mở rộng trên các câu trả lời khác, một cặp bất biến chung nên có một phương thức tĩnh để tránh làm lộn xộn mã của bạn với lệnh gọi đến hàm tạo:

class Pair<L,R> {
      final L left;
      final R right;

      public Pair(L left, R right) {
        this.left = left;
        this.right = right;
      }

      static <L,R> Pair<L,R> of(L left, R right){
          return new Pair<L,R>(left, right);
      }
}

nếu bạn đặt tên cho phương thức tĩnh là "của" hoặc "cặpOf" thì mã sẽ thành thạo khi bạn có thể viết:

    list.add(Pair.of(x,y)); // my preference
    list.add(pairOf(x,y)); // use with import static x.y.Pair.pairOf

thật xấu hổ khi các thư viện java cốt lõi quá thưa thớt về những thứ như vậy mà bạn phải sử dụng commons-lang hoặc các bên thứ 3 khác để làm những thứ cơ bản như vậy. còn một lý do nữa để nâng cấp lên scala ...


6

Tôi sẽ hỏi nếu bạn không muốn chỉ sử dụng một List<Pair<T, U>>? nhưng sau đó, tất nhiên, JDK không có lớp Pair <>. Nhưng một Google nhanh chóng tìm thấy một cái trên cả Wikipediaforum.sun.com . Chúc mừng


6

Giải pháp ưa thích như bạn đã mô tả đó là Danh sách các cặp (tức là Danh sách).

Để thực hiện điều này, bạn sẽ tạo một lớp Pair để sử dụng trong bộ sưu tập của bạn. Đây là một lớp tiện ích hữu ích để thêm vào cơ sở mã của bạn.

Lớp gần nhất trong Sun JDK cung cấp chức năng tương tự như lớp Cặp điển hình là AbstractMap.SimpleEntry. Bạn có thể sử dụng lớp này thay vì tạo lớp Cặp của riêng mình, mặc dù bạn sẽ phải sống với một số hạn chế khó xử và tôi nghĩ hầu hết mọi người sẽ nhăn mặt vì đây không thực sự là vai trò dự định của SimpleEntry. Ví dụ SimpleEntry không có phương thức "setKey ()" và không có hàm tạo mặc định, vì vậy bạn có thể thấy nó quá giới hạn.

Hãy nhớ rằng Bộ sưu tập được thiết kế để chứa các yếu tố của một loại duy nhất. Các giao diện tiện ích liên quan như Bản đồ không thực sự là Bộ sưu tập (tức là Bản đồ không triển khai giao diện Bộ sưu tập). Một cặp cũng sẽ không thực hiện giao diện Bộ sưu tập nhưng rõ ràng là một lớp hữu ích trong việc xây dựng các cấu trúc dữ liệu lớn hơn.


Tôi thấy giải pháp này tốt hơn cách "chiến thắng" với Cặp chung <K, V>. Nó làm mọi thứ được yêu cầu và đi ra khỏi hộp. Tôi sử dụng cái này để thực hiện.
Sauer

5

Điều này dựa trên mã của JavaHelp4u.

Ít dài dòng hơn và chỉ ra cách làm trong một dòng và cách lặp lại mọi thứ.

//======>  Imports
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

//======>  Single Entry
SimpleEntry<String, String> myEntry = new SimpleEntry<String, String>("ID", "Text");
System.out.println("key: " + myEntry.getKey() + "    value:" + myEntry.getValue());
System.out.println();

//======>  List of Entries
List<Entry<String,String>> pairList = new ArrayList<>();

//-- Specify manually
Entry<String,String> firstButton = new SimpleEntry<String, String>("Red ", "Way out");
pairList.add(firstButton);

//-- one liner:
pairList.add(new SimpleEntry<String,String>("Gray", "Alternate route"));  //Ananomous add.

//-- Iterate over Entry array:
for (Entry<String, String> entr : pairList) {
    System.out.println("Button: " + entr.getKey() + "    Label: " + entr.getValue());
}


4

chỉ cần tạo một lớp như

class tuples 
{ 
int x;
int y;
} 

sau đó tạo Danh sách các đối tượng này của bộ dữ liệu

List<tuples> list = new ArrayList<tuples>();

do đó bạn cũng có thể thực hiện các cấu trúc dữ liệu mới khác theo cách tương tự.


4

Ý tôi là, mặc dù không có Pairlớp nào trong Java, nhưng có một thứ khá giống nhau:Map.Entry

Map.Entry Tài liệu

Đây là (đơn giản hóa khá nhiều) những gì HashMap, hoặc thực sự là bất kỳ Mapcửa hàng.

Bạn có thể tạo một thể hiện Maplưu trữ các giá trị của bạn trong đó và lấy mục nhập. Bạn sẽ kết thúc với một Set<Map.Entry<K,V>>cách hiệu quả là những gì bạn muốn.

Vì thế:

public static void main(String []args)
{    
    HashMap<String, Integer> values = new HashMap<String,Integer>();
    values.put("A", 235);//your custom data, the types may be different
    //more data insertions....
    Set<Map.Entry<String,Integer>> list = values.entrySet();//your list 
    //do as you may with it
}

Tùy chọn này có vấn đề về các phím duy nhất. Giả sử bạn có các thuộc tính của người (ví dụ {(person1, "mắt xanh"), (person1, "tóc đỏ", (person2, "thiển cận"), (person2, "suy nghĩ nhanh")}) bạn sẽ không thể lưu trữ chúng thành các cặp trong bản đồ, vì mỗi người là chìa khóa của bản đồ và sẽ chỉ cho phép một thuộc tính cho mỗi người.
manuelvigarcia


0

Còn com.sun.tools.javac.util.Pair thì sao?


7
Loại này nằm trong tools.jar - một phần của việc triển khai "Mặt trời" của trình biên dịch javac. Nó chỉ được phân phối như một phần của JDK, có thể không có mặt trong các triển khai của các nhà cung cấp khác và không chắc là một phần của API công khai.
McDowell

0

Điều đầu tiên tôi nghĩ khi nói về các cặp khóa / giá trị là Lớp Thuộc tính nơi bạn có thể lưu và tải các mục vào một luồng / tệp.


0

Trong dự án Lò phản ứng (io.projectreactor: lõi lò phản ứng) có hỗ trợ nâng cao cho n-Tuples:

Tuple2<String, Integer> t = Tuples.of("string", 1)

Ở đó bạn có thể nhận t.getT1(), t.getT2(), ...Đặc biệt với Stream hoặc Flux, bạn thậm chí có thể ánh xạ các phần tử tuple:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
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.