Bagaimana cara menghapus elemen berulang dari ArrayList?


Jawaban:


991

Jika Anda tidak ingin duplikat dalam Collection, Anda harus mempertimbangkan mengapa Anda menggunakan Collectionyang memungkinkan duplikat. Cara termudah untuk menghapus elemen berulang adalah dengan menambahkan konten ke Set(yang tidak akan memungkinkan duplikat) dan kemudian menambahkan Setkembali ke ArrayList:

Set<String> set = new HashSet<>(yourList);
yourList.clear();
yourList.addAll(set);

Tentu saja, ini menghancurkan urutan elemen dalam ArrayList.


260
Lihat juga LinkedHashSet, jika Anda ingin mempertahankan pesanan.
voli

3
@ Chetan menemukan semua duplikat dari ArrayList di O (n), yang penting untuk didefinisikan dengan benar sama dengan metode pada objek yang Anda miliki dalam daftar (tidak ada masalah untuk angka): public Set<Object> findDuplicates(List<Object> list) { Set<Object> items = new HashSet<Object>(); Set<Object> duplicates = new HashSet<Object>(); for (Object item : list) { if (items.contains(item)) { duplicates.add(item); } else { items.add(item); } } return duplicates; }
Ondrej Bozek

4
Praktik yang baik adalah mendefinisikan variabel menggunakan tipe antarmuka Listdan Set(bukan tipe implementasi ArrayListdan HashSetseperti dalam contoh Anda).
Jonik

33
Anda dapat membersihkan ini dengan menggunakan new HashSet(al)alih-alih menginisialisasi untuk mengosongkan dan memanggil addAll.
ashes999

1
dapatkah saya menambahkan aturan untuk menetapkan apa yang duplikat kepada saya? Sebagai contoh: ketika saya Objectmemiliki beberapa nilai jika dua dari mereka mengulang saya menganggapnya sebagai duplikat (nilai-nilai lain dapat berbeda) dan digunakan Set?
jean d'arme

290

Meskipun mengonversikan ArrayListke yang HashSetefektif menghapus duplikat, jika Anda perlu mempertahankan urutan penyisipan, saya lebih suka menyarankan Anda untuk menggunakan varian ini

// list is some List of Strings
Set<String> s = new LinkedHashSet<>(list);

Kemudian, jika Anda perlu mendapatkan kembali Listreferensi, Anda dapat menggunakan lagi konstruktor konversi.


10
Apakah LinkedHashSet membuat jaminan untuk yang mana dari beberapa duplikat disimpan dari daftar? Misalnya, jika posisi 1, 3, dan 5 adalah duplikat dalam daftar asli, dapatkah kita berasumsi bahwa proses ini akan menghapus 3 dan 5? Atau mungkin menghapus 1 dan 3? Terima kasih.
Matt Briançon

16
@ Matt: ya, itu memang menjamin hal itu. The docs mengatakan: "linked list ini mendefinisikan urutan iterasi, yang merupakan urutan elemen yang dimasukkan ke dalam set (penyisipan-order) Perhatikan bahwa urutan penyisipan tidak terpengaruh jika suatu unsur adalah re-dimasukkan ke dalam set.."
abahgat

Sangat menarik. Saya punya situasi berbeda di sini. Saya tidak mencoba untuk mengurutkan String tetapi objek lain yang disebut AwardYearSource. Kelas ini memiliki atribut int yang disebut tahun. Jadi saya ingin menghapus duplikat berdasarkan tahun. yaitu jika ada tahun 2010 yang disebutkan lebih dari sekali, saya ingin menghapus objek AwardYearSource. Bagaimana saya bisa melakukan itu?
WowBow

@WowBow Misalnya Anda dapat mendefinisikan objek Wrapper yang memegang AwardYearSource. Dan tentukan objek Wrapper ini sama dengan metode berdasarkan bidang tahun AwardYearSources. Kemudian Anda bisa menggunakan Set dengan objek Wrapper ini.
Ondrej Bozek

@WowBow atau implementasikan Comparable / Comparator
shrini1000

134

Di Jawa 8:

List<String> deduped = list.stream().distinct().collect(Collectors.toList());

Harap perhatikan bahwa kontrak hashCode-equals untuk anggota daftar harus dihormati agar penyaringan berfungsi dengan baik.


1
Bagaimana saya melakukan ini untuk membedakan huruf besar-kecil?
StackFlowed

@StackFlowed Jika Anda tidak perlu untuk melestarikan urutan daftar yang Anda bisa addAlluntuk new TreeSet<String>(String.CASE_INSENSITIVE_ORDER). Elemen pertama yang ditambahkan akan tetap di set jadi jika daftar Anda berisi "Dog" dan "dog" (dalam urutan itu) TreeSetakan berisi "Dog". Jika pesanan harus dipertahankan maka sebelum baris dalam jawaban dimasukkan list.replaceAll(String::toUpperCase);.
Paul

1
Saya mendapatkan kesalahan ini: jenis yang tidak kompatibel: Daftar <Object> tidak dapat dikonversi ke Daftar <String>
Samir

Ini adalah solusi sederhana secara umum tetapi bagaimana Anda menghapus duplikat dari Arraylist of int []?
Nooby Programmer

56

Misalkan kita memiliki daftar Stringseperti:

List<String> strList = new ArrayList<>(5);
// insert up to five items to list.        

Kemudian kita dapat menghapus elemen duplikat dengan berbagai cara.

Sebelum ke Jawa 8

List<String> deDupStringList = new ArrayList<>(new HashSet<>(strList));

Catatan: Jika kita ingin menjaga ketertiban penyisipan maka kita perlu untuk menggunakan LinkedHashSetdi tempatHashSet

Menggunakan Jambu Biji

List<String> deDupStringList2 = Lists.newArrayList(Sets.newHashSet(strList));

Menggunakan Java 8

List<String> deDupStringList3 = strList.stream().distinct().collect(Collectors.toList());

Catatan: Jika kami ingin mengumpulkan hasilnya dalam implementasi daftar tertentu misalnyaLinkedList maka kami dapat mengubah contoh di atas sebagai:

List<String> deDupStringList3 = strList.stream().distinct()
                 .collect(Collectors.toCollection(LinkedList::new));

Kita dapat menggunakan parallelStreamjuga dalam kode di atas tetapi mungkin tidak memberikan manfaat kinerja yang diharapkan. Lihat pertanyaan ini untuk lebih lanjut.


Yah, ketika saya mengetik komentar saya sebelumnya, saya berada dalam kesan yang parallel streamsakan selalu memberikan kinerja yang lebih baik. Tapi itu hanya mitos. Saya kemudian mengetahui bahwa ada beberapa skenario di mana aliran paralel harus digunakan. Dalam skenario ini, aliran paralel tidak akan memberikan kinerja yang lebih baik. dan ya aliran paralel mungkin tidak memberikan hasil yang diinginkan beberapa kasus. List<String> deDupStringList3 = stringList.stream().map(String::toLowerCase).distinct().collect(Collectors.toList());harus menjadi solusi yang cocok dalam kasus ini
Diablo

53

Jika Anda tidak ingin duplikat, gunakan Set daripada a List. Untuk mengonversi a Listmenjadi SetAnda dapat menggunakan kode berikut:

// list is some List of Strings
Set<String> s = new HashSet<String>(list);

Jika benar - benar diperlukan, Anda dapat menggunakan konstruksi yang sama untuk mengubah Setkembali menjadi a List.


Demikian pula di bagian bawah utas, saya telah memberikan jawaban di mana saya menggunakan Set for Custom Object. Dalam kasus jika ada orang yang memiliki objek khusus seperti "Kontak" atau "Siswa" dapat menggunakan jawaban yang berfungsi baik untuk saya.
Muhammad Adil

Masalahnya muncul ketika Anda harus secara khusus mengakses suatu elemen. Misalnya saat mengikat objek ke tampilan item daftar di Android, Anda diberikan indeksnya. Jadi Settidak bisa digunakan di sini.
TheRealChx101

Bagaimana saya bisa menyetujui ini ketika daftar adalah daftar objek
jvargas

28

Anda juga dapat melakukannya dengan cara ini, dan menjaga ketertiban:

// delete duplicates (if any) from 'myArrayList'
myArrayList = new ArrayList<String>(new LinkedHashSet<String>(myArrayList));

Saya pikir ini adalah cara terbaik untuk menghapus duplikat di ArrayList. Sangat direkomendasikan. @Nenad terima kasih atas jawabannya.
ByWaleed

25

Java 8 stream menyediakan cara yang sangat sederhana untuk menghapus elemen duplikat dari daftar. Menggunakan metode yang berbeda. Jika kami memiliki daftar kota dan kami ingin menghapus duplikat dari daftar itu dapat dilakukan dalam satu baris -

 List<String> cityList = new ArrayList<>();
 cityList.add("Delhi");
 cityList.add("Mumbai");
 cityList.add("Bangalore");
 cityList.add("Chennai");
 cityList.add("Kolkata");
 cityList.add("Mumbai");

 cityList = cityList.stream().distinct().collect(Collectors.toList());

Cara menghapus elemen duplikat dari daftar array


25

Berikut cara yang tidak memengaruhi pemesanan daftar Anda:

ArrayList l1 = new ArrayList();
ArrayList l2 = new ArrayList();

Iterator iterator = l1.iterator();

while (iterator.hasNext()) {
    YourClass o = (YourClass) iterator.next();
    if(!l2.contains(o)) l2.add(o);
}

l1 adalah daftar asli, dan l2 adalah daftar tanpa item berulang (Pastikan YourClass memiliki metode yang sama dengan apa yang ingin Anda perjuangkan untuk kesetaraan)


Jawaban ini tidak memiliki dua hal: 1) Tidak menggunakan generik, tetapi tipe mentah ( ArrayList<T>harus digunakan sebagai ganti ArrayList) 2) Pembuatan iterator eksplisit dapat dihindari dengan menggunakan a for (T current : l1) { ... }. Bahkan jika Anda ingin menggunakan secara Iteratoreksplisit, iteradorsalah eja.
RAnders00

4
Dan implementasi ini berjalan dalam waktu kuadratik, dibandingkan dengan implementasi hash set yang terhubung berjalan dalam waktu linier. (yaitu ini membutuhkan 10 kali lebih lama pada daftar dengan 10 elemen, 10.000 kali lebih lama pada daftar dengan 10.000 elemen. Implementasi JDK 6 untuk ArrayList.contains , imp JDK8 adalah sama.)
Patrick M

21

Dimungkinkan untuk menghapus duplikat dari arraylist tanpa menggunakan HashSet atau satu arraylist lagi .

Coba kode ini ..

    ArrayList<String> lst = new ArrayList<String>();
    lst.add("ABC");
    lst.add("ABC");
    lst.add("ABCD");
    lst.add("ABCD");
    lst.add("ABCE");

    System.out.println("Duplicates List "+lst);

    Object[] st = lst.toArray();
      for (Object s : st) {
        if (lst.indexOf(s) != lst.lastIndexOf(s)) {
            lst.remove(lst.lastIndexOf(s));
         }
      }

    System.out.println("Distinct List "+lst);

Output adalah

Duplicates List [ABC, ABC, ABCD, ABCD, ABCE]
Distinct List [ABC, ABCD, ABCE]

Ini lambat dan Anda mungkin mendapatkan ConcurrentModificationException.
maaartinus

@maaartinus Sudahkah Anda mencoba kode itu? Itu tidak akan menghasilkan pengecualian. Juga cukup cepat. Saya mencoba kode sebelum memposting.
CarlJohn

4
Anda benar, itu tidak seperti Anda mengulangi array, bukan daftar. Namun, ini lambat sekali. Cobalah dengan jutaan elemen. Bandingkan dengan ImmutableSet.copyOf(lst).toList().
maaartinus

menjawab pertanyaan yang saya tanyakan dalam wawancara .. Bagaimana menghapus nilai berulang dari ArrayList tanpa menggunakan Sets. Terima kasih
Aniket Paul

Secara internal, indexOfiterasi lstmenggunakan for for loop.
Patrick M


19

ini bisa menyelesaikan masalah:

private List<SomeClass> clearListFromDuplicateFirstName(List<SomeClass> list1) {

     Map<String, SomeClass> cleanMap = new LinkedHashMap<String, SomeClass>();
     for (int i = 0; i < list1.size(); i++) {
         cleanMap.put(list1.get(i).getFirstName(), list1.get(i));
     }
     List<SomeClass> list = new ArrayList<SomeClass>(cleanMap.values());
     return list;
}

1
Saya lebih menyukai solusi ini.
Tushar Gogna

12

Mungkin agak berlebihan, tetapi saya menikmati masalah terisolasi semacam ini. :)

Kode ini menggunakan Set sementara (untuk pemeriksaan keunikan) tetapi menghapus elemen langsung di dalam daftar asli. Karena penghapusan elemen di dalam ArrayList dapat menyebabkan sejumlah besar penyalinan array, metode penghapusan (int) dihindari.

public static <T> void removeDuplicates(ArrayList<T> list) {
    int size = list.size();
    int out = 0;
    {
        final Set<T> encountered = new HashSet<T>();
        for (int in = 0; in < size; in++) {
            final T t = list.get(in);
            final boolean first = encountered.add(t);
            if (first) {
                list.set(out++, t);
            }
        }
    }
    while (out < size) {
        list.remove(--size);
    }
}

Sementara kami melakukannya, ini adalah versi untuk LinkedList (jauh lebih bagus!):

public static <T> void removeDuplicates(LinkedList<T> list) {
    final Set<T> encountered = new HashSet<T>();
    for (Iterator<T> iter = list.iterator(); iter.hasNext(); ) {
        final T t = iter.next();
        final boolean first = encountered.add(t);
        if (!first) {
            iter.remove();
        }
    }
}

Gunakan antarmuka penanda untuk menyajikan solusi terpadu untuk Daftar:

public static <T> void removeDuplicates(List<T> list) {
    if (list instanceof RandomAccess) {
        // use first version here
    } else {
        // use other version here
    }
}

EDIT: Saya kira barang generik tidak benar-benar menambah nilai apa pun di sini .. :)


1
Mengapa menggunakan ArrayList di parameter? Kenapa tidak Daftar saja? Apakah itu tidak akan berhasil?
Shervin Asgari

Daftar akan benar-benar berfungsi sebagai parameter untuk metode pertama yang terdaftar. Namun metode ini dioptimalkan untuk digunakan dengan daftar akses acak seperti ArrayList, jadi jika LinkedList dilewatkan, Anda akan mendapatkan kinerja yang buruk. Misalnya, pengaturan elemen n: th dalam LinkedList membutuhkan waktu O (n), sedangkan pengaturan elemen n: th dalam daftar akses acak (seperti ArrayList) membutuhkan waktu O (1). Namun, sekali lagi, ini mungkin berlebihan ... Jika Anda memerlukan kode khusus semacam ini, semoga akan berada dalam situasi yang terisolasi.
voli

10
public static void main(String[] args){
    ArrayList<Object> al = new ArrayList<Object>();
    al.add("abc");
    al.add('a');
    al.add('b');
    al.add('a');
    al.add("abc");
    al.add(10.3);
    al.add('c');
    al.add(10);
    al.add("abc");
    al.add(10);
    System.out.println("Before Duplicate Remove:"+al);
    for(int i=0;i<al.size();i++){
        for(int j=i+1;j<al.size();j++){
            if(al.get(i).equals(al.get(j))){
                al.remove(j);
                j--;
            }
        }
    }
    System.out.println("After Removing duplicate:"+al);
}

Implementasi ini tidak mengembalikan elemen dalam daftar karena j-- terakhir
neo7

1
Implementasi ini berfungsi dengan sangat baik. Tidak ada masalah di balik ini dan untuk tugas ini saya hanya menggunakan satu arraylist. Jadi jawaban ini benar-benar baik. Sebelum memberikan umpan balik negatif, Anda juga akan menambahkan testcase sehingga setiap orang dapat memahami hasilnya. Terima kasih Manash
Manash Ranjan Dakua

5

Jika Anda ingin menggunakan perpustakaan pihak ketiga, Anda dapat menggunakan metode ini distinct()di Eclipse Collections (sebelumnya GS Collections).

ListIterable<Integer> integers = FastList.newListWith(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
    FastList.newListWith(1, 3, 2),
    integers.distinct());

Keuntungan menggunakan distinct()alih-alih mengonversi ke Set dan kemudian kembali ke Daftar adalah distinct()mempertahankan urutan Daftar asli, mempertahankan kemunculan pertama setiap elemen. Diimplementasikan dengan menggunakan Set dan Daftar.

MutableSet<T> seenSoFar = UnifiedSet.newSet();
int size = list.size();
for (int i = 0; i < size; i++)
{
    T item = list.get(i);
    if (seenSoFar.add(item))
    {
        targetCollection.add(item);
    }
}
return targetCollection;

Jika Anda tidak dapat mengonversi Daftar asli Anda menjadi jenis Koleksi Eclipse, Anda bisa menggunakan ListAdapter untuk mendapatkan API yang sama.

MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();

Catatan: Saya pengendara untuk Eclipse Collections.


3

Tiga baris kode ini dapat menghapus elemen yang digandakan dari ArrayList atau koleksi apa pun.

List<Entity> entities = repository.findByUserId(userId);

Set<Entity> s = new LinkedHashSet<Entity>(entities);
entities.clear();
entities.addAll(s);

2

Saat Anda mengisi ArrayList, gunakan kondisi untuk setiap elemen. Sebagai contoh:

    ArrayList< Integer > al = new ArrayList< Integer >(); 

    // fill 1 
    for ( int i = 0; i <= 5; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    // fill 2 
    for (int i = 0; i <= 10; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    for( Integer i: al )
    {
        System.out.print( i + " ");     
    }

Kami akan mendapatkan array {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}


2

Jika Anda ingin mempertahankan Pesanan Anda, maka yang terbaik adalah menggunakan LinkedHashSet . Karena jika Anda ingin meneruskan Daftar ini ke Query Sisipkan dengan menghapusnya, urutannya akan dipertahankan.

Coba ini

LinkedHashSet link=new LinkedHashSet();
List listOfValues=new ArrayList();
listOfValues.add(link);

Konversi ini akan sangat membantu ketika Anda ingin mengembalikan Daftar tetapi bukan Set.


2

Kode:

List<String> duplicatList = new ArrayList<String>();
duplicatList = Arrays.asList("AA","BB","CC","DD","DD","EE","AA","FF");
//above AA and DD are duplicate
Set<String> uniqueList = new HashSet<String>(duplicatList);
duplicatList = new ArrayList<String>(uniqueList); //let GC will doing free memory
System.out.println("Removed Duplicate : "+duplicatList);

Catatan: Pasti, akan ada overhead memori.


2
ArrayList<String> city=new ArrayList<String>();
city.add("rajkot");
city.add("gondal");
city.add("rajkot");
city.add("gova");
city.add("baroda");
city.add("morbi");
city.add("gova");

HashSet<String> hashSet = new HashSet<String>();
hashSet.addAll(city);
city.clear();
city.addAll(hashSet);
Toast.makeText(getActivity(),"" + city.toString(),Toast.LENGTH_SHORT).show();

1

LinkedHashSet akan melakukan triknya.

String[] arr2 = {"5","1","2","3","3","4","1","2"};
Set<String> set = new LinkedHashSet<String>(Arrays.asList(arr2));
for(String s1 : set)
    System.out.println(s1);

System.out.println( "------------------------" );
String[] arr3 = set.toArray(new String[0]);
for(int i = 0; i < arr3.length; i++)
     System.out.println(arr3[i].toString());

// output: 5,1,2,3,4


1
        List<String> result = new ArrayList<String>();
        Set<String> set = new LinkedHashSet<String>();
        String s = "ravi is a good!boy. But ravi is very nasty fellow.";
        StringTokenizer st = new StringTokenizer(s, " ,. ,!");
        while (st.hasMoreTokens()) {
            result.add(st.nextToken());
        }
         System.out.println(result);
         set.addAll(result);
        result.clear();
        result.addAll(set);
        System.out.println(result);

output:
[ravi, is, a, good, boy, But, ravi, is, very, nasty, fellow]
[ravi, is, a, good, boy, But, very, nasty, fellow]

1

Ini digunakan untuk daftar Objek Kustom Anda

   public List<Contact> removeDuplicates(List<Contact> list) {
    // Set set1 = new LinkedHashSet(list);
    Set set = new TreeSet(new Comparator() {

        @Override
        public int compare(Object o1, Object o2) {
            if (((Contact) o1).getId().equalsIgnoreCase(((Contact) o2).getId()) /*&&
                    ((Contact)o1).getName().equalsIgnoreCase(((Contact)o2).getName())*/) {
                return 0;
            }
            return 1;
        }
    });
    set.addAll(list);

    final List newList = new ArrayList(set);
    return newList;
}

1

Anda dapat menggunakan nested loop di follow:

ArrayList<Class1> l1 = new ArrayList<Class1>();
ArrayList<Class1> l2 = new ArrayList<Class1>();

        Iterator iterator1 = l1.iterator();
        boolean repeated = false;

        while (iterator1.hasNext())
        {
            Class1 c1 = (Class1) iterator1.next();
            for (Class1 _c: l2) {
                if(_c.getId() == c1.getId())
                    repeated = true;
            }
            if(!repeated)
                l2.add(c1);
        }

1

Seperti yang dikatakan sebelumnya, Anda harus menggunakan kelas yang mengimplementasikan antarmuka Set bukannya Daftar untuk memastikan keaslian elemen. Jika Anda harus menjaga urutan elemen, antarmuka SortedSet kemudian dapat digunakan; kelas TreeSet mengimplementasikan antarmuka itu.


1

Jika Anda menggunakan tipe model Daftar <T> / ArrayList <T>. Semoga ini bisa membantu Anda.

Ini kode saya tanpa menggunakan struktur data lain seperti set atau hashmap

for (int i = 0; i < Models.size(); i++){
for (int j = i + 1; j < Models.size(); j++) {       
 if (Models.get(i).getName().equals(Models.get(j).getName())) {    
 Models.remove(j);
   j--;
  }
 }
}

0
for(int a=0;a<myArray.size();a++){
        for(int b=a+1;b<myArray.size();b++){
            if(myArray.get(a).equalsIgnoreCase(myArray.get(b))){
                myArray.remove(b); 
                dups++;
                b--;
            }
        }
}

0
import java.util.*;
class RemoveDupFrmString
{
    public static void main(String[] args)
    {

        String s="appsc";

        Set<Character> unique = new LinkedHashSet<Character> ();

        for(char c : s.toCharArray()) {

            System.out.println(unique.add(c));
        }
        for(char dis:unique){
            System.out.println(dis);
        }


    }
}

0
public Set<Object> findDuplicates(List<Object> list) {
        Set<Object> items = new HashSet<Object>();
        Set<Object> duplicates = new HashSet<Object>();
        for (Object item : list) {
            if (items.contains(item)) {
                duplicates.add(item);
                } else { 
                    items.add(item);
                    } 
            } 
        return duplicates;
        }

0
    ArrayList<String> list = new ArrayList<String>();
    HashSet<String> unique = new LinkedHashSet<String>();
    HashSet<String> dup = new LinkedHashSet<String>();
    boolean b = false;
    list.add("Hello");
    list.add("Hello");
    list.add("how");
    list.add("are");
    list.add("u");
    list.add("u");

    for(Iterator iterator= list.iterator();iterator.hasNext();)
    {
        String value = (String)iterator.next();
        System.out.println(value);

        if(b==unique.add(value))
            dup.add(value);
        else
            unique.add(value);


    }
    System.out.println(unique);
    System.out.println(dup);

0

Jika Anda ingin menghapus duplikat dari ArrayList berarti temukan logika di bawah ini,

public static Object[] removeDuplicate(Object[] inputArray)
{
    long startTime = System.nanoTime();
    int totalSize = inputArray.length;
    Object[] resultArray = new Object[totalSize];
    int newSize = 0;
    for(int i=0; i<totalSize; i++)
    {
        Object value = inputArray[i];
        if(value == null)
        {
            continue;
        }

        for(int j=i+1; j<totalSize; j++)
        {
            if(value.equals(inputArray[j]))
            {
                inputArray[j] = null;
            }
        }
        resultArray[newSize++] = value;
    }

    long endTime = System.nanoTime()-startTime;
    System.out.println("Total Time-B:"+endTime);
    return resultArray;
}

1
Mengapa Anda memposting solusi kuadrat ke pertanyaan yang sudah memiliki solusi linear dan log-linear 2 tahun, yang juga lebih sederhana?
abarnert
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.