JavaScript'te bir diziyi kopyalamanın en hızlı yolu - slice vs. 'for' döngüsü


634

JavaScript'te bir diziyi çoğaltmak için: aşağıdakilerden hangisi daha hızlıdır?

Dilim yöntemi

var dup_array = original_array.slice();

For döngü

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

Her iki şekilde sadece sığ bir kopya yapmak biliyorum : original_array nesnelere başvurular içeriyorsa, nesneler klonlanmayacak, ancak yalnızca başvurular kopyalanacak ve bu nedenle her iki dizide de aynı nesnelere başvurular olacaktır. Ancak bu sorunun amacı bu değil.

Sadece hızı soruyorum.


3
jsben.ch/#/wQ9RU <= bir diziyi klonlamanın en yaygın yolları için bir kıyaslama
EscapeNetscape

Yanıtlar:


776

Bir diziyi klonlamanın en az 5 (!) Yolu vardır:

  • döngü
  • dilim
  • Array.from ()
  • concat
  • forma operatörü (HIZLI)

Aşağıdaki bilgileri sağlayan bir huuuge BENCHMARKS ipliği var :

  • için göz kırpma tarayıcılar slice(), en hızlı yöntemdir concat()biraz daha yavaş olduğunu ve while loop2.4x daha yavaştır.

  • diğer tarayıcılar while loopiçin en hızlı yöntemdir, çünkü bu tarayıcıların sliceve için dahili optimizasyonları yoktur concat.

Bu, Temmuz 2016'da da geçerli.

Aşağıda, tarayıcınızın konsoluna kopyalayıp yapıştırabileceğiniz ve resmi görmek için birkaç kez çalıştırabileceğiniz basit komut dosyaları bulunmaktadır. Milisaniye çıktılar, daha düşük daha iyidir.

döngü sırasında

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = Array(n); 
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

dilim

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

Lütfen bu yöntemlerin Array nesnesinin kendisini klonlayacağını, ancak dizi içeriğinin referans olarak kopyalandığını ve derin klonlanmadığını unutmayın.

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true

48
@ cept0 duygu yok, sadece kriterler jsperf.com/new-array-vs-splice-vs-slice/31
Dan

2
@ Ne olmuş yani? Test örneği sonuçlarınız: Gecelik Firefox 30 hala Chrome'dan ~% 230 daha hızlı. V8'in kaynak kodunu kontrol edin ve spliceşaşıracaksınız (...)
mate64

4
Ne yazık ki kısa diziler için cevap çok farklı . Örneğin, her birini çağırmadan önce bir dizi dinleyiciyi klonlamak. Bu diziler genellikle küçük, genellikle 1 elementtir.
gman

6
Bu yöntemi kaçırdınız:A.map(function(e){return e;});
wcochran

13
Yanıp sönen tarayıcılar hakkında yazıyorsunuz . Is not yanıp sadece düzen motoru başta olmak üzere HTML oluşturma etkileyen ve böylece önemsiz? Burada V8, Spidermonkey ve arkadaşları hakkında konuşmayı tercih ettiğimizi düşündüm. Sadece kafamı karıştıran bir şey. Aydınlat beni, eğer yanılıyorsam.
Neonit

241

Teknik slice olarak en hızlı yoldur. Ancak , başlangıç 0dizinini eklerseniz daha da hızlı olur .

myArray.slice(0);

daha hızlı

myArray.slice();

http://jsperf.com/cloning-arrays/3


Ve daha myArray.slice(0,myArray.length-1);hızlı myArray.slice(0);mı?
jave.web

1
@ jave.web you; dizinin son öğesini yeni düşürdüm. Tam kopya array.slice (0) veya array.slice (0, array.length)
Marek Marczak

137

es6 yolu ne olacak?

arr2 = [...arr1];

23
babel ile dönüştürüldüyse:[].concat(_slice.call(arguments))
CHAN

1
Nereden argumentsgeldiğinden emin değilim ... Bence babil ürününüz birkaç farklı özelliği karıştırıyor. Daha muhtemeldir arr2 = [].concat(arr1).
Sterling Archer

3
@SterlingArcher arr2 = [].conact(arr1), farklı arr2 = [...arr1]. [...arr1]sözdizimi deliği dönüştürür undefined. Örneğin arr1 = Array(1); arr2 = [...arr1]; arr3 = [].concat(arr1); 0 in arr2 !== 0 in arr3,.
tsh

1
Bunu Dan'ın yukarıdaki cevabına karşı tarayıcımda (Chrome 59.0.3071.115) test ettim. .Slice () 'den 10 kat daha yavaştı. n = 1000*1000; start = + new Date(); a = Array(n); b = [...a]; console.log(new Date() - start); // 168
Harry Stevens

1
Yine böyle klon şey olmaz: [{a: 'a', b: {c: 'c'}}]. Eğer cbireyin değer 'çoğaltılamaz' dizisinde değiştirilir sadece bir referans kopyası değil, bir klonu olduğu için, o, orijinal dizideki değişecektir.
Nörotransmitter

44

Diziyi veya Nesneyi derin klonlamanın en kolay yolu:

var dup_array = JSON.parse(JSON.stringify(original_array))

56
Yeni başlayanlar için önemli not: Bu JSON'a bağlı olduğundan, bu aynı zamanda sınırlamalarını da devralır. Diğer şeylerin yanı sıra, dizinizin içeremediği undefinedveya herhangi bir functions içeremeyeceği anlamına gelir . İşlem nullsırasında her ikisi de sizin için dönüştürülecektir JSON.stringify. (['cool','array']).slice()Bunları değiştirmeyecek, aynı zamanda dizi içindeki nesneleri derin klonlamayacak gibi diğer stratejiler . Yani bir ödünleşim var.
Seth Holladay

27
Çok kötü perf ve DOM, tarih, normal ifade, fonksiyon ... veya prototiplenmiş nesneler gibi özel nesnelerle çalışmaz. Döngüsel referansları desteklemeyin. Derin klon için asla JSON kullanmamalısınız.
Yukulélé

17
en kötü yol! Sadece bir sorun için diğerlerinin hepsi çalışmazsa kullanın. Yavaş, kaynaklar yoğun ve yorumlarda daha önce bahsedilen tüm JSON sınırlamalarına sahip. Nasıl 25 oy aldığını hayal bile edemiyorum.
Lukas Liesis

2
Dizileri ilkellerle kopyalar ve özelliklerin başka ilkellerle / dizilerle diziler olduğunu kopyalar. Bunun için sorun değil.
Drenai

4
Bunu Dan'ın yukarıdaki cevabına karşı tarayıcımda (Chrome 59.0.3071.115) test ettim. .Slice () 'den yaklaşık 20 kat daha yavaştı. n = 1000*1000; start = + new Date(); a = Array(n); var b = JSON.parse(JSON.stringify(a)) console.log(new Date() - start); // 221
Harry Stevens

29
var cloned_array = [].concat(target_array);

3
Lütfen bunun ne yaptığını açıklayın.
Jed Fox

8
Bu kod snippet'i soruyu cevaplayabilirken, nasıl veya nedenini açıklayan bir bağlam sağlamaz. Cevabınızı açıklamak için bir veya iki cümle eklemeyi düşünün.
brandonscript

32
Bu tür yorumlardan nefret ediyorum. Ne yaptığı belli!
EscapeNetscape

6
Basit bir soru için basit bir cevap, okunacak büyük bir hikaye yok. Bu tür cevapları beğendim +1
Achim

15
"Sadece hız hakkında soru soruyorum" - Bu cevap hız hakkında bir fikir vermiyor. Sorulan ana soru bu. brandonscript iyi bir noktaya sahiptir. Bunun bir cevap olduğunu düşünmek için daha fazla bilgiye ihtiyaç vardır. Ama daha basit bir soru olsaydı, bu mükemmel bir cevap olurdu.
TamusJRoyce

26

Hızlı bir demo hazırladım: http://jsbin.com/agugo3/edit

Internet Explorer 8'deki sonuçlarım 156, 782 ve 750'dir, slicebu durumda bu durum çok daha hızlıdır.


Bunu çok hızlı yapmanız gerekiyorsa çöp toplayıcısının ek maliyetini unutmayın. Dilim kullanarak hücresel otomattaki her hücre için her bir komşu dizisini kopyalıyordum ve bir önceki diziyi yeniden kullanmaktan ve değerleri kopyalamaktan çok daha yavaştı. Chrome, toplam sürenin yaklaşık% 40'ının çöp toplamak için harcandığını belirtti.
drake7707

21

a.map(e => e)bu iş için başka bir alternatif. Bugün itibariyle Firefox'ta .map()çok hızlı (neredeyse hızlı .slice(0)), ancak Chrome'da değil.

Diziler nesnelerdir ve nesneler referans tipleridir beri bir dizi, çok boyutlu ise bir dizi klonlama biri uygun yolu bir icadı Yani diğer bir yandan, dilim veya concat yöntemlerin hiçbiri ... bir çare olacak Array.prototype.clone()şekilde izler.

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));


Kötü değil, ancak dizinizde Object varsa maalesef bu işe yaramaz: \ JSON.parse (JSON.stringify (myArray)) bu durumda daha iyi çalışır.
GBMan

17

Ar Diziyi Klonlamanın En Hızlı Yolu

Bir dizi klonlamak için gereken süreyi test etmek için bu çok düz yarar işlevini yaptı. % 100 güvenilir değildir, ancak mevcut bir diziyi klonlamanın ne kadar sürdüğü konusunda size toplu bir fikir verebilir:

function clone(fn) {
    const arr = [...Array(1000000)];
    console.time('timer');
    fn(arr);
    console.timeEnd('timer');
}

Ve farklı yaklaşımı test etti:

1)   5.79ms -> clone(arr => Object.values(arr));
2)   7.23ms -> clone(arr => [].concat(arr));
3)   9.13ms -> clone(arr => arr.slice());
4)  24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5)  30.02ms -> clone(arr => [...arr]);
6)  39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7)  99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));

GÜNCELLEME :
Not: hepsinden, bir diziyi derin klonlamanın tek yolu kullanmaktır JSON.parse(JSON.stringify(arr)).

Bununla birlikte, diziniz döneceği gibi işlevler içeriyorsa yukarıdakileri kullanmayın null.
Bu güncelleme için @GilEpshtain teşekkür ederiz .


2
Cevabınızı karşılaştırmayı denedim ve çok farklı sonuçlar aldım: jsben.ch/o5nLG
mesqueeb

@mesqueeb, testler elbette ur makinenize bağlı olarak değişebilir. Ancak cevabı test sonucunuzla güncellemekten çekinmeyin. İyi iş!
Lior Elrom

Cevabınızı çok beğendim, ancak testinizi deniyorum ve aldım arr => arr.slice() en hızlı olanı .
Gil Epshtain

1
@LiorElrom, yöntemlerin serileştirilememesi nedeniyle güncellemeniz doğru değil. Örneğin:JSON.parse(JSON.stringify([function(){}])) çıktı olacak[null]
Gil Epshtain

1
Güzel bir kriter. Ben 2 tarayıcılarda benim Mac üzerinde test ettik: Chrome Sürümü 81.0.4044.113 Safari Version 13.1 (15609.1.20.111.8) ve en hızlı yayılma operasyondur: [...arr]ile 4.653076171875msChrome'da ve 8.565msSafari'de. Chrome'da İkinci hızlı dilim fonksiyonudur arr.slice()ile 6.162109375msve Safari'de ikinci [].concat(arr)ile 13.018ms.
edufinn

7

Şuna bir göz atın: bağlantı . Hız değil konforla ilgili. Eğer sadece kullanabilirsiniz gördüğünüz gibi yanında dilim (0) üzerinde ilkel türleri .

Bir dizinin referansın bir kopyası yerine bağımsız bir kopyasını oluşturmak için dizi dilimi yöntemini kullanabilirsiniz.

Misal:

Bir dizinin referansın bir kopyası yerine bağımsız bir kopyasını oluşturmak için dizi dilimi yöntemini kullanabilirsiniz.

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

Bir nesneyi kopyalamak veya kopyalamak için:

function cloneObject(source) {
    for (i in source) {
        if (typeof source[i] == 'source') {
            this[i] = new cloneObject(source[i]);
        }
        else{
            this[i] = source[i];
  }
    }
}

var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);

Kaynak: bağlantı


1
İlkel türleri açıklama için geçerlidir foryanı söz konusu döngü.
user113716

4
bir nesne dizisini kopyalarsam, yeni dizinin nesneleri kopyalamak yerine aynı nesnelere başvurmasını beklerdim.
lincolnk

7

@Dan'ın dediği gibi "Bu cevap güncelliğini yitirir. Gerçek durumu kontrol etmek için kıyaslama ölçütlerini kullanın ", jsperf'in kendisi için bir cevabı olmayan belirli bir cevap vardır: while :

var i = a.length;
while(i--) { b[i] = a[i]; }

koşucu ile 960.589 ops / sn vardı a.concat() % 60 578.129 op / sn, en.

Bu en son Firefox (40) 64 bitidir.


@aleclarson yeni, daha güvenilir bir karşılaştırma ölçütü oluşturdu.


1
Gerçekten jsperf'i bağlamanız gerekir. Düşündüğünüz dizinin kırılmasıdır, çünkü 'while loop' testi hariç her test durumunda yeni bir dizi oluşturulur.
aleclarson

1
Daha doğru olan yeni bir jsperf yaptım: jsperf.com/clone-array-3
aleclarson

% 60 ne? % 60 daha hızlı?
Peter Mortensen

1
@PeterMortensen: 587192 960589'un ~% 60'ı (61.1 ...).
serv-inc

7

ECMAScript 2015 yolu ile SpreadOperatör :

Temel örnekler:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

Tarayıcı konsolunda deneyin:

var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)

var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);

Referanslar


Muhtemelen forma ile hızlı olan tek şey onu yazmaktır. Bunu yapmanın diğer yollarından daha az performans gösterir.
XT_Nova

3
Lütfen argümanınızla ilgili bazı bağlantılar sağlayın.
Marian07

6

Tarayıcıya bağlıdır. Array.prototype.slice vs manuel dizi oluşturma blog yazısına bakarsanız , her birinin performansı için kaba bir kılavuz var:

Resim açıklamasını buraya girin

Sonuçlar:

Resim açıklamasını buraya girin


1
argumentsuygun bir dizi değil ve koleksiyon üzerinde çalışmaya callzorlamak sliceiçin kullanıyor . sonuçlar yanıltıcı olabilir.
lincolnk

Evet, yazımda bu istatistiklerin muhtemelen broswers geliştikçe değişeceğini belirtmek istedim, ancak genel bir fikir veriyor.
kyndigs

2
@diugalde Resim olarak kod göndermenin kabul edilebilir olduğu tek durumun, kodun potansiyel olarak tehlikeli olması ve kopyalanmaması gerektiğidir. Bu durumda olsa da, oldukça saçma.
Florian Wendelborn

6

Çok daha temiz bir çözüm var:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

Uzunluk kontrolü gereklidir, çünkü Arraykurucu tam olarak bir argümanla çağrıldığında farklı davranır.


2
Ama en hızlısı mı?
Chris Wesseling

14
splice()Belki daha semantik . Ama gerçekten, uygulamak ve bu tüm ama sezgiseldir.
Michael Piefel


3
Array.ofUzunluğu kullanabilir ve görmezden gelebilirsiniz:Array.of.apply(Array, array)
Oriol

6

.Slice () yönteminin iki boyutlu diziler için çalışmadığını unutmayın. Bunun gibi bir işleve ihtiyacınız olacak:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}

3
Javascript'te iki boyutlu diziler yoktur. Sadece dizi içeren diziler vardır. Yapmaya çalıştığınız şey derin bir kopya soruda gerekli olmayan .
Aloso

5

Dizinin uzunluğuna bağlıdır. Dizi uzunluğu <= 1.000.000 ise, sliceve concatyöntemleri yaklaşık olarak aynı süreyi alır. Ancak daha geniş bir aralık verdiğinizde concatyöntem kazanır.

Örneğin, şu kodu deneyin:

var original_array = [];
for(var i = 0; i < 10000000; i ++) {
    original_array.push( Math.floor(Math.random() * 1000000 + 1));
}

function a1() {
    var dup = [];
    var start = Date.now();
    dup = original_array.slice();
    var end = Date.now();
    console.log('slice method takes ' + (end - start) + ' ms');
}

function a2() {
    var dup = [];
    var start = Date.now();
    dup = original_array.concat([]);
    var end = Date.now();
    console.log('concat method takes ' + (end - start) + ' ms');
}

function a3() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with push method takes ' + (end - start) + ' ms');
}

function a4() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup[i] = original_array[i];
    }
    var end = Date.now();
    console.log('for loop with = method takes ' + (end - start) + ' ms');
}

function a5() {
    var dup = new Array(original_array.length)
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}

a1();
a2();
a3();
a4();
a5();

Original_array öğesinin uzunluğunu 1.000.000 olarak ayarlarsanız, sliceyöntem veconcat yöntem yaklaşık olarak aynı süreyi alır (rasgele sayılara bağlı olarak 3-4 ms).

Original_array öğesinin uzunluğunu 10.000.000 olarak ayarlarsanız, slice yöntem 60 ms'yi concatalır ve yöntem 20 ms'yi alır.


dup.pushyanlış a5, bunun yerine dup[i] = kullanılmalıdır
4esn0k


2
        const arr = ['1', '2', '3'];

         // Old way
        const cloneArr = arr.slice();

        // ES6 way
        const cloneArrES6 = [...arr];

// But problem with 3rd approach is that if you are using muti-dimensional 
 // array, then only first level is copied

        const nums = [
              [1, 2], 
              [10],
         ];

        const cloneNums = [...nums];

// Let's change the first item in the first nested item in our cloned array.

        cloneNums[0][0] = '8';

        console.log(cloneNums);
           // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

        // NOOooo, the original is also affected
        console.log(nums);
          // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

Bu nedenle, bu senaryoların gerçekleşmesini önlemek için

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);

cloneNums[0][0]Örneğinizde yapılan değişikliğin değişikliği nasıl yaydığına dikkat çekmek geçerli bir şeydir, nums[0][0]ancak bunun nedeni, nums[0][0]başvurunun cloneNumsforma operatörü tarafından kopyalanan etkili bir nesne olmasıdır . Bütün bunlar, bu davranış değere göre kopyaladığımız kodu etkilemez (int, string vb değişmez değerler).
Aditya MP

1

Karşılaştırma zamanı!

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {
  time_function = function(ms, f, num) {
    var z = 0;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++)
      f(num);
    return (z)
  }

  function clone1(arr) {
    return arr.slice(0);
  }

  function clone2(arr) {
    return [...arr]
  }

  function clone3(arr) {
    return [].concat(arr);
  }

  Array.prototype.clone = function() {
    return this.map(e => Array.isArray(e) ? e.clone() : e);
  };

  function clone4(arr) {
    return arr.clone();
  }


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    funcs = [clone1, clone2, clone3, clone4];
    results = [];
    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff.name);
      var s = time_function(2500, ff, Array(1024));
      results.push([ff, s]);
      console.log("Score: " + s);

    })
    return results.sort(compare);
  }
  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();

console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

Karşılaştırma, düğmeyi tıkladığınız için 10 saniye boyunca çalışır.

Benim sonuçlarım:

Krom (V8 motoru):

1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)

Firefox (SpiderMonkey Motoru):

1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)

Kazanan kod:

function clone1(arr) {
    return arr.slice(0);
}

Kazanan motor:

SpiderMonkey (Mozilla / Firefox)


1

JavaScript'te bir diziyi Sırayla çoğaltmanın hızlı yolları:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

Dizi nesnelerinizde kullanmak için daha iyi bir JSON serileştirilemeyen içerik (işlevler, Number.POSITIVE_INFINITY, vb.) Varsa

array1copy = JSON.parse(JSON.stringify(array1))


0

Bu kodu takip edebilirsiniz. Değişmez yol dizisi klonu. Bu, klonlamayı sıralamanın mükemmel bir yoludur


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)

0

ES6'da, Spread sözdizimini kullanabilirsiniz . .

Misal:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

Forma operatörünün tamamen yeni bir dizi oluşturduğunu, bu nedenle bir tanesinin değiştirilmesi diğerini etkilemeyeceğini lütfen unutmayın.

Misal:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.