Saya bermain-main dengan operasi fungsional yang malas di Java SE 8, dan saya ingin map
indeks i
untuk pasangan / tuple (i, value[i])
, kemudian filter
berdasarkan pada value[i]
elemen kedua , dan akhirnya hanya output indeks.
Haruskah saya tetap menderita ini: Apa yang setara dengan C ++ Pair <L, R> di Jawa? di era baru lambda dan aliran yang berani?
Pembaruan: Saya menyajikan contoh yang agak disederhanakan, yang memiliki solusi rapi yang ditawarkan oleh @dkatzel di salah satu jawaban di bawah ini. Namun, itu tidak menyamaratakan. Karena itu, izinkan saya menambahkan contoh yang lebih umum:
package com.example.test;
import java.util.ArrayList;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
boolean [][] directed_acyclic_graph = new boolean[][]{
{false, true, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, true, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, true},
{false, false, false, false, false, false}
};
System.out.println(
IntStream.range(0, directed_acyclic_graph.length)
.parallel()
.mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length)
.filter(j -> directed_acyclic_graph[j][i])
.count()
)
.filter(n -> n == 0)
.collect(() -> new ArrayList<Long>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
);
}
}
Ini memberikan output yang salah[0, 0, 0]
yang sesuai dengan jumlah untuk tiga kolom yang semuanya false
. Yang saya butuhkan adalah indeks dari tiga kolom ini. Output yang benar seharusnya [0, 2, 4]
. Bagaimana saya bisa mendapatkan hasil ini?
sumber
AbstractMap.SimpleImmutableEntry<K,V>
selama bertahun-tahun ... Tapi bagaimanapun, bukannya pemetaani
untuk(i, value[i])
hanya untuk menyaring olehvalue[i]
dan pemetaan kembali kei
: mengapa tidak hanya filter byvalue[i]
di tempat pertama, tanpa pemetaan?i
dalam arus. Saya juga perluvalue[i]
untuk kriteria. Itu sebabnya saya perlu(i, value[i])
[0, 2, 4]
?Jawaban:
UPDATE: Jawaban ini sebagai tanggapan terhadap pertanyaan awal, Apakah Java SE 8 memiliki Pasangan atau Tuple? (Dan secara implisit, jika tidak, mengapa tidak?) OP telah memperbarui pertanyaan dengan contoh yang lebih lengkap, tetapi sepertinya itu dapat diselesaikan tanpa menggunakan struktur Pair. [Catatan dari OP: inilah jawaban yang benar .]
Jawaban singkatnya adalah tidak. Anda harus menggulung sendiri atau membawa salah satu dari beberapa perpustakaan yang mengimplementasikannya.
Memiliki
Pair
kelas di Jawa SE diusulkan dan ditolak setidaknya sekali. Lihat utas diskusi ini di salah satu milis OpenJDK. Pengorbanannya tidak jelas. Di satu sisi, ada banyak implementasi Pair di perpustakaan lain dan dalam kode aplikasi. Itu menunjukkan kebutuhan, dan menambahkan kelas seperti itu ke Java SE akan meningkatkan penggunaan kembali dan berbagi. Di sisi lain, memiliki kelas Pair menambah godaan untuk menciptakan struktur data yang rumit dari Pasangan dan koleksi tanpa membuat jenis dan abstraksi yang diperlukan. (Itu adalah parafrase dari pesan Kevin Bourillion dari utas itu.)Saya sarankan semua orang membaca seluruh utas email itu. Ini sangat berwawasan dan tidak memiliki kerusakan. Cukup meyakinkan. Ketika mulai, saya berpikir, "Ya, seharusnya ada kelas Pair di Java SE" tetapi pada saat thread mencapai akhirnya saya telah berubah pikiran.
Perhatikan bahwa JavaFX memiliki kelas javafx.util.Pair . API JavaFX berevolusi secara terpisah dari Java SE API.
Seperti yang dapat dilihat dari pertanyaan terkait Apa persamaan dari C ++ Pair in Java? ada ruang desain yang cukup besar di sekitar apa yang tampaknya seperti API sederhana. Haruskah benda tidak berubah? Haruskah mereka serial? Haruskah mereka sebanding? Haruskah kelasnya final atau tidak? Haruskah kedua elemen dipesan? Haruskah itu antarmuka atau kelas? Mengapa berhenti berpasangan? Mengapa tidak tiga kali lipat, paha depan, atau N-tupel?
Dan tentu saja ada penamaan yang tak terhindarkan bikeshed untuk elemen:
Satu masalah besar yang hampir tidak disebutkan adalah hubungan Pasangan dengan primitif. Jika Anda memiliki
(int x, int y)
datum yang mewakili titik dalam ruang 2D, mewakili ini sebagaiPair<Integer, Integer>
mengkonsumsi tiga objek, bukan dua kata 32-bit. Selanjutnya, benda-benda ini harus berada di heap dan akan dikenakan overhead GC.Tampak jelas bahwa, seperti Streams, penting untuk ada spesialisasi primitif untuk Pasangan. Apakah kita ingin melihat:
Bahkan sebuah
IntIntPair
masih akan membutuhkan satu objek di heap.Ini, tentu saja, mengingatkan pada proliferasi antarmuka fungsional dalam
java.util.function
paket di Java SE 8. Jika Anda tidak ingin API kembung, yang mana yang akan Anda tinggalkan? Anda juga bisa berpendapat bahwa ini tidak cukup, dan bahwa spesialisasi untuk, katakanlah,Boolean
harus ditambahkan juga.Perasaan saya adalah bahwa jika Java telah menambahkan kelas Pair sejak lama, itu akan menjadi sederhana, atau bahkan sederhana, dan itu tidak akan memuaskan banyak kasus penggunaan yang kita bayangkan sekarang. Pertimbangkan bahwa jika Pair telah ditambahkan dalam kerangka waktu JDK 1.0, itu mungkin akan bisa berubah! (Lihatlah java.util.Date.) Apakah orang akan senang dengan itu? Dugaan saya adalah bahwa jika ada kelas Pair di Jawa, itu akan menjadi semacam-agak-tidak-benar-berguna dan semua orang masih akan bergulir sendiri untuk memenuhi kebutuhan mereka, akan ada berbagai implementasi Pair dan Tuple di perpustakaan eksternal, dan orang-orang masih akan berdebat / berdiskusi tentang cara memperbaiki kelas Pasangan Jawa. Dengan kata lain, jenis di tempat yang sama kita di hari ini.
Sementara itu, beberapa pekerjaan sedang berlangsung untuk mengatasi masalah mendasar, yang merupakan dukungan yang lebih baik dalam JVM (dan akhirnya bahasa Jawa) untuk tipe nilai . Lihat dokumen Status Nilai ini . Ini adalah pekerjaan pendahuluan dan spekulatif, dan hanya membahas masalah dari perspektif JVM, tetapi sudah ada cukup banyak pemikiran di baliknya. Tentu saja tidak ada jaminan bahwa ini akan masuk ke Java 9, atau pernah masuk ke mana saja, tetapi hal itu menunjukkan arah pemikiran saat ini tentang topik ini.
sumber
Pair<T,U>
. Karena obat generik harus dari jenis referensi. Primitif apa pun akan dikotak ketika disimpan. Untuk menyimpan primitif Anda benar-benar membutuhkan kelas yang berbeda.valueOf
seharusnya menjadi satu-satunya cara untuk mendapatkan contoh kotak. Tetapi itu sudah ada di sana sejak Java 1.0 dan mungkin tidak layak untuk dicoba pada titik ini.Pair
atauTuple
kelas dengan metode pabrik yang menciptakan kelas spesialisasi yang diperlukan (dengan penyimpanan yang dioptimalkan) secara transparan di latar belakang. Pada akhirnya, lambdas melakukan hal itu: mereka dapat menangkap sejumlah variabel tipe arbitrer. Dan sekarang gambar dukungan bahasa yang memungkinkan untuk membuat kelas tuple yang sesuai saat runtime dipicu olehinvokedynamic
instruksi ...invokedynamic
pabrik berbasis yang mirip dengan pembuatan lambda perkuatan seperti itu di kemudian hari tidak akan menjadi masalah. Omong-omong, lambda tidak memiliki identitas juga. Seperti yang dinyatakan secara eksplisit, identitas yang mungkin Anda rasakan hari ini adalah artefak dari implementasi saat ini.Anda dapat melihat kelas bawaan ini:
AbstractMap.SimpleEntry
AbstractMap.SimpleImmutableEntry
sumber
SimpleImmutableEntry
hanya menjamin bahwa referensi yang disimpan diEntry
tidak berubah, bukan bidang yang ditautkankey
danvalue
objek (atau objek yang ditautkan) tidak berubah.Sayangnya, Java 8 tidak memperkenalkan pasangan atau tupel. Anda selalu dapat menggunakan org.apache.commons.lang3.tuple tentu saja (yang secara pribadi saya gunakan dalam kombinasi dengan Java 8) atau Anda dapat membuat pembungkus sendiri. Atau gunakan Maps. Atau hal-hal seperti itu, seperti dijelaskan dalam jawaban yang diterima untuk pertanyaan yang Anda tautkan.
PEMBARUAN: JDK 14 memperkenalkan catatan sebagai fitur pratinjau. Ini bukan tupel, tetapi dapat digunakan untuk menyimpan banyak masalah yang sama. Dalam contoh spesifik Anda dari atas, itu bisa terlihat seperti ini:
Ketika dikompilasi dan dijalankan dengan JDK 14 (pada saat penulisan, ini merupakan akses awal) menggunakan
--enable-preview
flag, Anda mendapatkan hasil berikut:sumber
Tampaknya contoh lengkap dapat diselesaikan tanpa menggunakan segala jenis struktur Pair. Kuncinya adalah menyaring indeks kolom, dengan predikat memeriksa seluruh kolom, alih-alih memetakan indeks kolom ke jumlah
false
entri dalam kolom itu.Kode yang melakukan ini ada di sini:
Ini menghasilkan output
[0, 2, 4]
yang saya pikir hasil yang benar diminta oleh OP.Perhatikan juga
boxed()
operasi yang mengotakkanint
nilai ke dalamInteger
objek. Ini memungkinkan seseorang untuk menggunakantoList()
kolektor yang sudah ada alih-alih harus menulis fungsi kolektor yang melakukan tinju sendiri.sumber
true
). Oleh karena itu, saya akan menerima jawaban Anda yang lain sebagai benar, tetapi juga tunjukkan yang ini! Terima kasih banyak :)Vavr (sebelumnya disebut Javaslang) ( http://www.vavr.io ) juga menyediakan tupel (hingga ukuran 8). Inilah javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .
Ini adalah contoh sederhana:
Mengapa JDK sendiri tidak datang dengan jenis tuple yang sederhana sampai sekarang adalah misteri bagi saya. Menulis kelas bungkus tampaknya menjadi bisnis setiap hari.
sumber
Sejak Java 9, Anda dapat membuat instance yang
Map.Entry
lebih mudah daripada sebelumnya:Map.entry
mengembalikan yang tidak dapat dimodifikasiEntry
dan melarang nulls.sumber
Karena Anda hanya peduli pada indeks, Anda tidak perlu memetakan ke tuple sama sekali. Mengapa tidak menulis filter yang menggunakan elemen pencarian di array Anda?
sumber
Iya.
Map.Entry
dapat digunakan sebagaiPair
.Sayangnya itu tidak membantu dengan stream Java 8 karena masalahnya adalah bahwa meskipun lambdas dapat mengambil beberapa argumen, bahasa Java hanya memungkinkan untuk mengembalikan nilai tunggal (objek atau tipe primitif). Ini menyiratkan bahwa setiap kali Anda memiliki aliran Anda berakhir dengan dilewatkan satu objek dari operasi sebelumnya. Ini adalah kekurangan dalam bahasa Java, karena jika beberapa nilai balik didukung DAN stream mendukung mereka, kita bisa memiliki tugas non-sepele yang jauh lebih baik dilakukan oleh stream.
Sampai saat itu, hanya ada sedikit kegunaan.
EDIT 2018-02-12: Saat mengerjakan proyek saya menulis kelas pembantu yang membantu menangani kasus khusus memiliki pengidentifikasi sebelumnya dalam aliran yang Anda butuhkan di lain waktu tetapi bagian aliran di antara tidak tahu tentang itu. Sampai saya sempat merilis sendiri, itu tersedia di IdValue.java dengan unit test di IdValueTest.java
sumber
Eclipse Collections memiliki
Pair
dan semua kombinasi Pasangan primitif / objek (untuk semua delapan primitif).The
Tuples
pabrik dapat membuat contohPair
, danPrimitiveTuples
pabrik dapat digunakan untuk membuat semua kombinasi dari primitif pasang / objek.Kami menambahkan ini sebelum Java 8 dirilis. Mereka berguna untuk mengimplementasikan key / value Iterators untuk peta primitif kami, yang kami juga dukung di semua kombinasi primitif / objek.
Jika Anda ingin menambahkan overhead perpustakaan tambahan, Anda dapat menggunakan solusi yang diterima Stuart dan mengumpulkan hasilnya menjadi primitif
IntList
untuk menghindari tinju. Kami menambahkan metode baru di Eclipse Collections 9.0 untuk memungkinkanInt/Long/Double
koleksi dibuat dariInt/Long/Double
Streams.Catatan: Saya pengendara untuk Eclipse Collections.
sumber