关于collection:Java集合collection

一、定义 /----Collection接口:单列汇合,用来存储一个一个的对象 /----List接口:存储有序的、可反复的数据。--->"动静"数组 /----ArrayList、LinkedList、Vector /----Set接口:存储无序的、不可反复的数据 /---- HashSet、LinkedHashSet、TreeSet二、Listpackage com.study.java;/** * /----Collection接口:单列汇合,用来存储一个一个的对象 * /----List接口:存储有序的、可反复的数据。--->"动静"数组 * /----ArrayList:作為List接口的次要實現,線程不平安,效率高,底层用Object[] elementData存储 * /----LinkedList 对于频繁的插入、删除操作,效率比ArrayList高,底层用双向链表存储 * /----Vector:List的古老實現類,線程平安,效率低,底层用Object[] elementData存储 * * 1. ArrayList 源码剖析 jdk 7 状况下 * ArrayList list = new ArrayList();// 底层创立了长度是10的object[]数组elementData * list.add(123);//elementData[0] = new Integer(123); * ... * list.add(11); // 如果此次增加导致底层elementData数组容量不够、则扩容; * 默认状况下,扩容原来容量的1.5倍,同时须要将原有数组中的数据复制到新的数组中 * * * 论断:倡议开发中应用带参结构器,ArrayList list = new ArrayList(int capacity); * jdk8中: * ArrayList list = new ArrayList(); // 底层object[] elementData 初始化为{},并没有创立长度为10的数组 * list.add(11); // 第一次调用add 底层才创立了长度为10的数组,并将数据123增加到elementData[0] * 后续增加和扩容操作与jdk7无异 * 结存:提早数组创立,更节俭内存 * * 2. LinkedList源码剖析: * LinkedList list = new LinkedList(); 外部申明了Node类型的first 和 last属性,默认值为null * list.add(123); 将123封装到Node中,创立了Node对象 * 其中,Node定义为: * private static class Node<E> { * E item; * Node<E> next; * Node<E> prev; * * Node(Node<E> prev, E element, Node<E> next) { * this.item = element; * this.next = next; * this.prev = prev; * } * } * 3.Vector源码剖析: * 通过Vector窗常见底层为10的数组,默认扩容为原来的,两倍 * * ArrayList、LinkedList、Vector三者异同? * 同:三个类都实现了List接口,存储数据的特点雷同:存储有序的、可反复数据 */public class ListTest {}三、package com.study.java;import org.junit.jupiter.api.Test;import java.util.*;/** * /----Collection接口:单列汇合,用来存储一个一个的对象 * /----Set接口:存储无序的、不可反复的数据 * /---- HashSet:作为Set接口的次要实现类:线程不平安:能够存储null值 * /----LinkedHashSet:作为HashSet的子类,遍历其外部数据时,可依照增加的程序遍历 * /----TreeSet:可依照增加对象的指定属性,进行排序 * 没有定义新的办法,用的都是Collection定义好的办法 * 要求:向Set中增加数据,其所在类肯定要重写hashCode() 和 equals() * 重写的hashCode()和equals()尽可能放弃一致性,相等的对象必须具备相等的散列码 * 重写的两个办法的技巧:对象中用作equals办法比拟的Field,都应该用来计算hashCode */public class SetTest { /* set :存储无序的、不可反复的数据 无序性:不等于随机性,存储的数据在底层数组中并非依照数组索引程序排序,而是依据数据的哈希值决定的 不可重复性:保障增加的元素依照equals()判断时,不能返回true 增加数据的过程:以HashSet为例: 增加元素a,首先调用元素a所在类的hashCode()办法,计算元素a的哈希值 此hash值通过某中算法算出在hashSet底层数组中的寄存地位(即索引地位),判断 数组此地位是否已有元素,无则增加胜利,有其余元素b(或以链表模式存在的多个元素),则比拟a与b的hash值 如果hash值不雷同,则a增加胜利,如果hash值雷同,进而须要调用元素a所在类的equals()办法 equals()返回true,元素a增加失败 equals()返回false,元素a增加胜利 对于后两种状况,元素a与曾经存在指定索引地位上的数据以链表的形式存储 jdk7 元素a放到数组指向原来的元素 jdk8 原来的元素在数组中,指向元素a HashSet底层:数组加链表 LinkedHashSet:作为HashSet的子类,增加数据时,还保护了两个援用,记录前一个和后一个数据 TreeSet : 天然排序:比拟两个对象是否雷同的规范为:compareTo()返回0,不再是equals(); 订制排序:比拟两个对象是否雷同的规范为:compare()返回0,不再是equals(); */ @Test public void test1() { Set set = new HashSet(); set.add(456); set.add("ddd0"); set.add(new Person("Tom", 12)); set.add(new Person("Tom", 12)); set.add(129); set.add(129); Iterator iterator = set.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } /** * 增加数据,要求同类的对象 */ @Test public void test2() { TreeSet treeSet = new TreeSet(); treeSet.add(new Person("jj", 44)); treeSet.add(new Person("ja", 55)); for (Object aa : treeSet) { System.out.println(aa); } } @Test public void test3() { Comparator com = new Comparator() { @Override public int compare(Object o1, Object o2) { if (o1 instanceof Person && o2 instanceof Person) { Person p1 = (Person) o1; Person p2 = (Person) o2; return Integer.compare(p1.getAge(), p2.getAge()); } else { throw new RuntimeException("输出数据类型不匹配"); } } }; TreeSet treeSet = new TreeSet(com); treeSet.add(new Person("jj", 44)); treeSet.add(new Person("ja", 55)); treeSet.add(new Person("ja", 11)); for (Object aa : treeSet) { System.out.println(aa); } }}

July 20, 2020 · 2 min · jiezi

mongodb的model名称细节

最近研究api设计,顺便研究了下mongodb,教程没有仔细看过,所以使用过程中也遇到了一些诡异的现象。 比如我使用中发现,我创建的模型名称,在对应数据库的collections内的名称不一致。我很纳闷,比如我创建的如下: const PersonModel = Mongoose.model("person", { firstname: String, lastname: String});当我将一条数据写入后,用工具Robo 3T发现,名称居然变成了people。 后来查了相关资料,原来mongodb有自己的一套规则,详细的规则,比如我这条:mongoose/lib/utils.js。当然这个是历史版本的例子了。关于这个现象,最新文档中也指出: The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural version of your model name. For example, if you use const MyModel = mongoose.model('Ticket', mySchema);Then Mongoose will create the model for your tickets collection, not your ticket collection. 一般情况他会创建一个复数的model,这种person算特殊的了。所以你写得model不一定在查询的时候会一样,即便不一样也不要惊讶哦~

May 22, 2019 · 1 min · jiezi

MongoDB固定集合(capped collection)

一 . 什么是固定集合MongoDB中有一种特殊类型的集合,值得我们特别留意,那就是固定集合(capped collection)。固定集合可以声明collection的容量大小,其行为类似于循环队列。数据插入时,新文档会被插入到队列的末尾,如果队列已经被占满,那么最老的文档会被之后插入的文档覆盖。固定集合特性:固定集合很像环形队列,如果空间不足,最早的文档就会被删除,为新的文档腾出空间。一般来说,固定集合适用于任何想要自动淘汰过期属性的场景。固定集合应用场景比如日志文件,聊天记录,通话信息记录等只需保留最近某段时间内的应用场景,都会使用到MongoDB的固定集合。固定集合的优点1.写入速度提升。固定集合中的数据被顺序写入磁盘上的固定空间,所以,不会因为其他集合的一些随机性的写操作而“中断”,其写入速度非常快(不建立索引,性能更好)。2.固定集合会自动覆盖掉最老的文档,因此不需要再配置额外的工作来进行旧文档删除。设置Job进行旧文档的定时删除容易形成性能的压力毛刺。固定集合非常实用与记录日志等场景。二 . 固定集合的创建不同于普通集合,固定集合必须在使用前显式创建。例如,创建固定集合coll_testcapped,大小限制为1024个字节。db.createCollection(“coll_testcapped”,{capped:true,size:1024});除了大小,创建时还可以指定固定集合中文档的数据量。例如,创建固定集合coll_testcapped,大小限制为1024个字节,文档数量限制为100。db.createCollection(“coll_testcapped2”,{capped:true,size:1024,max:100});创建固定集合还有另一途径,就是将普通集合装换为固定集合,使用的命令是convertToCapped。例如将testcol1集合转换为一个大小为1024字节的固定集合:db.runCommand({“convertToCapped”:“testcol1”,“size”:1024})三 . 固定集合信息的查看(1)判断集合是否为固定集合,其判定命令为:db.集合.isCapped() 。例如判断前面已创建的固定集合coll_testcapped2是否为固定集合:(2) 从集合信息中获取 有关固定集合的属性,查看集合的指令为:db.集合.stats()例如查看集合coll_testcapped2的信息:四 . 注意事项:(1) 固定集合创建之后就不可以改变,只能将其删除重建。(2) 普通集合可以使用convertToCapped转换固定集合,但是固定集合不可以转换为普通集合。(3) 创建固定集合,为固定集合指定文档数量限制时(指参数max),必须同时指定固定集合的大小(指参数size)。不管先达到哪一个限制,之后插入的新文档都会把最老的文档移除集合。(4) 使用convertToCapped命令将普通集合转换固定集合时,既有的索引会丢失,需要手动创建。并且,此转换命令没有限制文档数量的参数(即没有max的参数选项)。(5) 不可以对 固定集合 进行分片。(6) 对固定集合中的文档可以进行更新(update)操作,但更新不能导致文档的Size增长或缩小,否则更新失败。假如集合中有一个key,其value 对应的数据长度为100个字节,如果要更新这个key 对应的value,更新后的值也必须为100个字节,大于100个字节不可以,小于100个字节也不可以。报错信息为:Cannot change the size of a document in a capped collection : XXXX(XXXX代表某个数据字) !=XXXX。(7) 不可以对固定集合执行删除文档操作,但可以删除整个集合。删除文档时,报错信息为:cannot remove from a capped collection:XXXX(8) 还有一定需要注意,对集合估算size时,不要依据集合的storageSize ,而是依据集合的size。storageSize是wiredTiger存储引擎采用高压缩算法压缩后的。例如通过db.集合.stats()命令查看某集合的数据,“size” 和 “storageSize” 二者相差还是很大的。您可能感兴趣的文章:MongoDB在不同主机间复制数据库和集合的教程详解MongoDB中创建集合与删除集合的操作方法文章同步发布: https://www.geek-share.com/de…

December 27, 2018 · 1 min · jiezi

【源】ArrayDeque,Collection框架中不起眼的一个类

最近盯上了java collection框架中一个类——ArrayDeque。很多人可能没用过甚至没听说过这个类(i’m sorry,what’s fu*k this?),毕竟你坐在面试官面前的时候,关于数组链表的掌握情况,99%的可能性听到问题会是:说说ArrayList和LinkedList的区别?今天从ArrayDeque入手,换一个角度来检验下我们是否真正掌握了数组、链表。父类和接口不着急分析这个类的核心方法,先看下它的父类和接口,以便在Java Collection宇宙中找准它的定位,顺带从宏观角度窥探下Java Collection框架设计。父类父类是AbstractCollection,看下它的方法add、addAll、remove、clear、iterator、size……是不是都很常见?在你常用的xxList中经常会使用这些方法吧?可以说,AbstractCollection这个抽象类,是这种结构(数组、链表等等)的骨架!接口首先是Queue接口,定义出了最基本的队列功能:那么Deque接口呢?入眼各种xxFirst、xxLast,这种定义决定了它是双端队列的代表!框架设计相继看了接口和父类,楼主你到底想表达啥?嘿嘿,别急,我再反问一个经典问题——抽象类和接口有什么区别?你可能会有各种回答,比如抽象类能自己有自己的实现之类的。不能说不对,但这种答案相当于迷惑于奇技淫巧当中,未得正统。以设计角度来看,其实是is-a(抽象类)和has-a(接口)的区别!抽象类相当于某一个种族的基石比如定义汽车AbstractCar,会规定有轮子有发动机能跑的就是汽车;各家厂商生产的汽车都逃不出这个范畴,甭管你是大众宝马玛莎拉蒂。接口则关注各种功能有些汽车多了座椅加热;有些增设了天窗打开功能。但这些功能都是增强型的,并不是每种汽车都会有!抽象类和接口合理的组合,就产生了奇妙的效果:技能保证种族(类)的结构,又能对其进行扩展(接口)。给出大家熟悉的ArrayList和LinkedList,仔细感受下:这种设计不仅仅限于Java Collection,开源框架中也是如此,比如Spring IOC中的Context、Factory那部分……分析回归到本文的主角 ArrayDeque,既然它实现了Deque,自然具备双端队列的特性。类名中的 Array姓氏,无时无刻不在提醒我们,它是基于数组实现的。类注释中,有句话引起了我的注意:/** * This class is likely to be faster than * {@link Stack} when used as a stack, and faster than {@link LinkedList} * when used as a queue. */(Stack先不管)后半句说,ArrayDeque作为队列时比LinkedList快,看看它是怎么办到的!三大属性:transient Object[] elements; //基于数组实现transient int head; //头指针transient int tail; //尾巴指针技术敏感的同学已经能猜到它是怎么实现的了:数组作为基底,两个指分指头尾,插入删除操作时移动指针;如果头尾指针重合,则需要扩容……下面看看源码实现,是否和我们猜测的一致。构造器private static final int MIN_INITIAL_CAPACITY = 8;// ****** Array allocation and resizing utilities ******private static int calculateSize(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests “<=” because arrays aren’t kept full. if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } return initialCapacity;}规定最小值MIN_INITIAL_CAPACITY = 8,如果入参小于8,数组大小就定义成8;如果大于等于8,这一通右移是啥操作?假如我们传入了16,二进制10000,逐步分析下:1.initialCapacity |= (initialCapacity >>> 1)右移1位作|操作,10000->01000,‘或’ 操作后110002.initialCapacity |= (initialCapacity >>> 2)接上一步,右移2位作|操作,11000->00110,‘或’ 操作后111103.initialCapacity |= (initialCapacity >>> 4)接上一步,右移4位作|操作,11110->00001,‘或’ 操作后 11111……后面就两步都是11111 | 00000,结果就是 111114.initialCapacity++二进制数11111,+1之后100000,转换成十进制32最终的负值判断(用于处理超int正向范围情况),先不考虑。结论:这些’或’ 操作,最终得到了大于入参的2的次幂中最小的一个。底层数组始终是2的次幂,为什么如此?带着这个问题继续往下分析// The main insertion and extraction methods are addFirst,// addLast, pollFirst, pollLast. The other methods are defined in// terms of these.以上注释有云,核心方法就4个,我们从add方法入手。插入addFirstpublic void addFirst(E e) { if (e == null) throw new NullPointerException(); elements[head = (head - 1) & (elements.length - 1)] = e; //关键 if (head == tail) doubleCapacity();}head = (head - 1) & (elements.length - 1),玄机就在这里。如果你对1.8的HashMap足够了解,就会知道hashmap的数组大小同样始终是2的次幂。其中很重要的一个原因就是:当lengh是2的次幂的时候,某数字 x 的操作 x & (length - 1) 等价于 x % length,而对二进制的计算机来说 & 操作要比 % 操作效率更好!而且head = (head - 1) & (elements.length - 1),(head初始值0)第一次就将head指针定位到数组末尾了。画图分析下:可见,head指针从后向前移动。addLastpublic void addLast(E e) { if (e == null) throw new NullPointerException(); elements[tail] = e; if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity();}addLast和addFirst原理相同,只是addLast控制tail指针,从前向后移动!上图中再做一次add操作,指针将会重合。比如,再一次addFirst之后:if (head == tail) doubleCapacity(); //扩容触发扩容private void doubleCapacity() { assert head == tail; int p = head; int n = elements.length; int r = n - p; // number of elements to the right of p int newCapacity = n << 1; //左移,等价乘2,依然保持2的次幂 if (newCapacity < 0) throw new IllegalStateException(“Sorry, deque too big”); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); elements = a; head = 0; tail = n;}通过数组拷贝和重新调整指针,完成了扩容。至于pollFirst、pollLast是addFirst、addLast的相反操作,原理相似,不多做分析……参考这次,彻底弄懂接口及抽象类Jdk1.6 Collections Framework源码解析(3)-ArrayDeque ...

December 22, 2018 · 2 min · jiezi

集合模块collections

collections是Python内建的一个集合模块,提供了许多有用的集合类。namedtuple我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成:>>> p = (1, 2)但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。定义一个class又小题大做了,这时,namedtuple就派上了用场:>>> from collections import namedtuple>>> Point = namedtuple(‘Point’, [‘x’, ‘y’])>>> p = Point(1, 2)>>> p.x1>>> p.y2namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。可以验证创建的Point对象是tuple的一种子类:>>> isinstance(p, Point)True>>> isinstance(p, tuple)True类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义:namedtuple(‘名称’, [属性list]):Circle = namedtuple(‘Circle’, [‘x’, ‘y’, ‘r’])deque使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:>>> from collections import deque>>> q = deque([‘a’, ‘b’, ‘c’])>>> q.append(‘x’)>>> q.appendleft(‘y’)>>> qdeque([‘y’, ‘a’, ‘b’, ‘c’, ‘x’])deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。defaultdict(免因为没有key 报错KeyError)使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:>>> from collections import defaultdict>>> dd = defaultdict(lambda: ‘N/A’)>>> dd[‘key1’] = ‘abc’>>> dd[‘key1’] # key1存在’abc’>>> dd[‘key2’] # key2不存在,返回默认值’N/A’注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。OrderedDict使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。如果要保持Key的顺序,可以用OrderedDict:>>> from collections import OrderedDict>>> d = dict([(‘a’, 1), (‘b’, 2), (‘c’, 3)])>>> d # dict的Key是无序的{‘a’: 1, ‘c’: 3, ‘b’: 2}>>> od = OrderedDict([(‘a’, 1), (‘b’, 2), (‘c’, 3)])>>> od # OrderedDict的Key是有序的OrderedDict([(‘a’, 1), (‘b’, 2), (‘c’, 3)])注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:>>> od = OrderedDict()>>> od[‘z’] = 1>>> od[‘y’] = 2>>> od[‘x’] = 3>>> list(od.keys()) # 按照插入的Key的顺序返回[‘z’, ‘y’, ‘x’]OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:from collections import OrderedDictclass LastUpdatedOrderedDict(OrderedDict):def init(self, capacity): super(LastUpdatedOrderedDict, self).init() self._capacity = capacitydef setitem(self, key, value): containsKey = 1 if key in self else 0 if len(self) - containsKey >= self._capacity: last = self.popitem(last=False) print(‘remove:’, last) if containsKey: del self[key] print(‘set:’, (key, value)) else: print(‘add:’, (key, value)) OrderedDict.setitem(self, key, value)ChainMapChainMap可以把一组dict串起来并组成一个逻辑上的dict。ChainMap本身也是一个dict,但是查找的时候,会按照顺序在内部的dict依次查找。什么时候使用ChainMap最合适?举个例子:应用程序往往都需要传入参数,参数可以通过命令行传入,可以通过环境变量传入,还可以有默认参数。我们可以用ChainMap实现参数的优先级查找,即先查命令行参数,如果没有传入,再查环境变量,如果没有,就使用默认参数。下面的代码演示了如何查找user和color这两个参数:from collections import ChainMapimport os, argparse# 构造缺省参数:defaults = { ‘color’: ‘red’, ‘user’: ‘guest’}# 构造命令行参数:parser = argparse.ArgumentParser()parser.add_argument(’-u’, ‘–user’)parser.add_argument(’-c’, ‘–color’)namespace = parser.parse_args()command_line_args = { k: v for k, v in vars(namespace).items() if v }# 组合成ChainMap:combined = ChainMap(command_line_args, os.environ, defaults)# 打印参数:print(‘color=%s’ % combined[‘color’])print(‘user=%s’ % combined[‘user’])没有任何参数时,打印出默认参数:$ python3 use_chainmap.py color=reduser=guest当传入命令行参数时,优先使用命令行参数:$ python3 use_chainmap.py -u bobcolor=reduser=bob同时传入命令行参数和环境变量,命令行参数的优先级较高:$ user=admin color=green python3 use_chainmap.py -u bobcolor=greenuser=bobCounterCounter是一个简单的计数器,例如,统计字符出现的个数:>>> from collections import Counter>>> c = Counter()>>> for ch in ‘programming’:… c[ch] = c[ch] + 1…>>> cCounter({‘g’: 2, ’m’: 2, ‘r’: 2, ‘a’: 1, ‘i’: 1, ‘o’: 1, ’n’: 1, ‘p’: 1})Counter实际上也是dict的一个子类,上面的结果可以看出,字符’g’、’m’、‘r’各出现了两次,其他字符各出现了一次。小结collections模块提供了一些有用的集合类,可以根据需要选用。参考源码use_collections.py ...

December 19, 2018 · 2 min · jiezi

你可能需要了解下Laravel集合

前言集合通过 Illuminate\Database\Eloquent\Collection 进行实例,Laravel的内核大部分的参数传递都用到了集合,但这并不代表集合就是好的。Laravel作为快捷并优雅的开发框架,是有他一定的道理所在的,并非因他的路由、DB、监听器等等。当你需要处理一组数组时,你可能就需要它帮助你快捷的解决实际问题。创建集合$collection = collect([1, 2, 3]);显而易见,这是一部非常简单的操作,请打住你想说“这种操作很复杂”的话,它更类似与早起PHP5.x的版本的声明方式。$collection = array(1,2,3);laravel对于collection也没有做任何复杂的事情,会在下一章 《Laravel源码解析之集合》,谢谢打回原型如果你想将集合转换为数据,其使用方法也非常的简单collect([1, 2, 3])->all();——>[1, 2, 3]在不过与考虑性能的情况下,可以使用Laravel集合,毕竟它将帮你完成数组操作的百分之九十的工作。例如我们需要通过一个水平线切分数组,将其分为2个及以上的数组个数。使用集合可以酱紫做~$collection = collect([1, 2, 3, 4, 5, 6, 7]);$chunks = $collection->chunk(4);$chunks->toArray();// [[1, 2, 3, 4], [5, 6, 7]]并且有些还根据sql语句的查询方式来设计的方法,下面就让来看下具体都有哪些吧。方法列表这里列出一些常用的集合操作方法,具体及全部请操作官方。方法注释all将集合打回原型average & avg计算平均值chunk将集合拆成多个指定大小的小集合collapse将多个数组的集合合并成一个数组的集合combine可以将一个集合的值作为「键」,再将另一个数组或者集合的值作为「值」合并成一个集合concat将给定的数组或集合值附加到集合的末尾contains判断集合是否包含给定的项目count返回该集合内的项目总数dd打印集合的项目并结束脚本执行diff将集合与其它集合或纯 PHP 数组进行值的比较,然后返回原集合中存在而给定集合中不存在的值each迭代集合中的内容并将其传递到回调函数中filter使用给定的回调函数过滤集合的内容,只留下那些通过给定真实测试的内容first返回集合中通过给定真实测试的第一个元素groupBy根据给定的键对集合内的项目进行分组push把给定值添加到集合的末尾put在集合内设置给定的键值对sortBy通过给定的键对集合进行排序。排序后的集合保留了原数组键where通过给定的键值过滤集合致谢感谢你看到这里,希望本篇能够帮助到你。谢谢,还不抓紧去练习下集合?

October 22, 2018 · 1 min · jiezi