从本章开始,我们将深入学习Java中的集合内容。据统计,在实际业务开发中,Java集合的使用频率要远超其他类库中大部分类,可以说,集合类库是Java中最为重要的类库。本章将从集合的基础知识、集合的分类、集合框架及底层原理来深入理解集合。学习本章的集合知识需要建立以下学习目标:

    了解集合的基本概念;
   理解常见集合的特点、基本操作及实际应用;
   了解集合框架及框架体系;
   深入理解集合的底层原理。

下图描述了Java集合的知识体系:

1.集合的由来
在Java的初始版本中,由于缺少对集合的支持,选择性能优越的数组作为存储数据的“容器”。但是数组的缺点在于具有固定的尺寸,然而在绝大部分情况下,我们无法预知程序需要多少个对象,因此尺寸固定的数组的应用场景就具有很大的局限性了。由此,Java集合应运而生。

2.集合的基本概念
2.1.什么是集合?
集合是一个用来存放对象的容器,Java集合类存放于java.util包中。集合通常具有以下几个特点:

    集合存放的是多个对象的引用,对象本身还是放在堆内存中;
   集合只能存放对象的引用。例如,现在存储一个int基本数据类型的数据到集合中,
但编译器会将其自动转换成Integer类型,Java中每一种基本数据类型都有对应的包装类型。
   由于的泛型存在,因此集合可以支持不同类型,不限数量的数据类型。

2.2.集合的基本操作
集合的基本操作一般有添加、查询、遍历和删除。下面以最基本的集合ArrayList、HashSet和HashMap为例,通过实际案例来描述集合的基本操作。
2.2.1.添加元素
ArrayList或HashSet可以通过add()方法来添加一个元素,还可以通过addAll来接收一个集合,即批量添加另一个集合的所有元素:

public class CollectionDemo {public static void main(String[] args) {add();}private static void add() {// 1.ArrayList添加元素,允许元素按照插入顺序排列且重复List<String> list = getList();System.out.println("list : " + list);List<String> list2 = new ArrayList<>(10);// 批量添加集合的所有元素list2.addAll(list);System.out.println("list2 : " + list2);// 2.HashSet添加元素,确保元素的唯一性Set<String> set = getSet();System.out.println("set : " + set);Set<String> set2 = new HashSet<>(16);// 批量添加集合的所有元素set2.addAll(set);System.out.println("set2 : " + set2);/*** 3.通过关键信息查找完整数据的需求在软件开发中会经常用到,* 比如知道了商品ID,想要查找完整的商品信息;知* 道了词条名称,想要查找百科词条中的详细信息等。* 这类场景就需要用到散列表(或称哈希表)这种数据结构。*/Map<String, Integer> map = getMap();System.out.println("map : " + map);Map<String, Integer> map2 = new HashMap<>(16);// 批量添加集合的所有元素map2.putAll(map);System.out.println("map2 : " + map2);}private static Map<String, Integer> getMap() {Map<String, Integer> map = new HashMap<>(16);map.put("a",1);map.put("b",2);map.put("c",3);return map;}private static Set<String> getSet() {Set<String> set = new HashSet<>(16);set.add("a");set.add("a");set.add("b");return set;}private static List<String> getList() {List<String> list = new ArrayList<>(10);list.add("a");list.add("b");list.add("c");return list;}
}
// ~output:
list : [a, b, c]
list2 : [a, b, c]
set : [a, b]
set2 : [a, b]
map : {a=1, b=2, c=3}
map2 : {a=1, b=2, c=3}

除了以上两个用于添加集合元素的常规方法外,Java设计者还提供了Collections.addAll(Collection<? super T> c, T… elements)方法,通过可变参数批量添加一组元素:

public class CollectionDemo {public static void main(String[] args) {add();}private static void add() {// 1.ArrayList添加元素,允许元素按照插入顺序排列且重复List<String> list = getList();List<String> list2 = new ArrayList<>(10);// 批量添加集合的所有元素list2.addAll(list);System.out.println("list2 : " + list2);// 2.HashSet添加元素,确保元素的唯一性Set<String> set = getSet();Set<String> set2 = new HashSet<>(16);// 批量添加集合的所有元素set2.addAll(set);System.out.println("set2 : " + set2);List<String> list3 = new ArrayList<>(10);list3.add("d");list3.add("e");Collections.addAll(list3, list2.toArray(new String[list2.size()]));System.out.println("list3 : " + list3);Set<String> set3 = new HashSet<>(10);set3.add("d2");set3.add("e2");Collections.addAll(set3, set2.toArray(new String[set2.size()]));System.out.println("set3 : " + set3);}private static Set<String> getSet() {Set<String> set = new HashSet<>(16);set.add("a");set.add("a");set.add("b");return set;}private static List<String> getList() {List<String> list = new ArrayList<>(10);list.add("a");list.add("b");list.add("c");return list;}
}
//~output:
list2 : [a, b, c]
set2 : [a, b]
list3 : [d, e, a, b, c]
set3 : [a, b, e2, d2]

2.2.2.查询元素
2.2.2.1.随机访问与顺序访问

随机访问:随意访问该数据结构中的任意一个节点。例如,数组在内存中是按顺序存储
的,可以通过下标直接定位到某一个元素存放的位置。
顺序访问:从第一个元素开始逐个地读取该数据结构的元素。例如,链表只 能顺序访问:
要读取链表的第十个元素,得先读取前九个元素,并沿链接找到第十个元素。

2.2.2.2.RandomAccess接口
RandomAccess接口属于标记接口,因此内部不存在任何方法:

在Java集合中,通过实现RandomAccess标记接口来标记该集合自身支持随机访问,以便在一些框或算法中,就可以根据是否实现了RandomAccess接口做出更好的决策方式(方法实现)。例如,在ArrayList中,集合的for循环访问数据比使用iterator访问来的高效快速,我们可以通过代码验证这个结论:

public class CollectionDemo {public static void main(String[] args) {testLoop();}private static void testLoop(){List<Integer> list = new ArrayList<>(10);for(int i = 0;i<10000000;i++){list.add(i);}long startTime = System.currentTimeMillis();for(int i = 0;i<10000000;i++){Integer integer = list.get(i);}long endTime = System.currentTimeMillis();System.out.println("随机访问遍历耗时:" + (endTime - startTime));startTime = System.currentTimeMillis();Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {Integer next = iterator.next();}endTime = System.currentTimeMillis();System.out.println("迭代器遍历耗时:" + (endTime - startTime));}
}
//~output:
随机访问遍历耗时:55
迭代器遍历耗时:66

在此基础上,Java设计者就让ArrayList实现了RandomAccess标记接口,以便在Collections等类中通过判断集合是否实现了RandomAccess接口,尽可能定制最优算法处理策略:

    若集合实现了RandomAccess标记接口,则直接通过随机访问(即
get(index))的方式遍历集合;
   若集合未实现RandomAccess标记接口,例如LinkedList,则通过迭代
器的方式遍历集合。



随机访问:

迭代器访问:


注意:随机访问是集合自身所具有的特点和行为,并不能错误地认为集合实现了RandomAccess接口才具备了随机访问的能力,而应该理解为若某个集合本身具备随机访问的能力,那么该集合应当去实现RandomAccess接口,以便在Collections等算法类中尽可能定制最优算法处理策略(若集合实现了RandomAccess接口,都会调用性能最优的随机访问方法)。换句话说,RandomAccess接口作用在于用来标记该集合具备随机访问的能力(特指实现了某个随机访问的方法),而不是提供了随机访问的能力。例如,在Java集合中,ArrayList的底层由数组实现,因此它支持随机访问,所以该集合才实现了RandomAccess接口:

而LinkedList的底层由链表实现,因此它不支持随机访问,只支持顺序访问,所以该集合没有实现RandomAccess接口:

HashSet与ArrayList是两种完全不同的集合,前者基于散列表实现,它可以确保保存的元素不重复,但无法保证元素的顺序,因而没有维护元素的下标,因为这样做几乎是没有意义的;而ArrayList是基于数组实现,它允许元素按照插入顺序排列且重复,由于ArrayList是基于数组实现的,因此它可以通过下标获取元素,即支持随机访问。由于HashSet没有维护元素的下标,因此它不支持随机访问,但可以通过ArrayList的构造器转换为List,这样就支持随机访问了:

Set<String> set3 = new HashSet<>(10);
set3.add("d2");
set3.add("e2");
List<String> list4 = new ArrayList<>(set3);

假如你只想获取Set集合的第一个元素或者说Set集合中只有一个元素时,那么可以这样做:

Set<String> set3 = new HashSet<>(10);
String next = set3.iterator().next();
System.out.println("next :" + next);

但是在通过iterator().next()方法获取Set集合的第一个元素时,要确保Set集合中至少存在一个元素,否则会抛出一个java.util.NoSuchElementException异常,同样地,List在获取元素时也要保证这一点,防止发生java.lang.IndexOutOfBoundsException异常。
HashMap是一种散列表,它的元素是一个键值对,键和值构成映射关系,因此散列表的作用在于将某个对象与另一个对象关联起来。我们可以通过键来查找值:

Map<String, Integer> map = getMap();
Integer value = map.get("a");

另外,需要特别注意一点:若HashMap的值为集合类型,通过put维护键值对时,应该避免使用containsKey()方法。我们通过一个简单的例子来看看这样做有什么弊端:

Map<String, List<Integer>> map2 = new HashMap<>();
List<Integer> valueList;
if(map2.containsKey("a")) {valueList = map2.get("a");
} else {valueList = new ArrayList<>(10);
}
valueList.add(1);

在上面的案例中,程序首先通过containsKey()方法判断map2中是否存在键对象”a”,若存在则再通过get(“a”)获取值对象。实际上,大可不必这样,原因在于HashMap的get()方法和containsKey()方法本质上都是调用getNode()方法实现的:

因此这就重复执行了两次getNode()方法。优化后的代码如下所示:

Map<String, List<Integer>> map2 = new HashMap<>();
List<Integer> valueList = map2.get("a");
if(valueList == null) {valueList = new ArrayList<>(10);
}
valueList.add(1);

我们看到,优化后的代码性能得到了提升,并且也清爽、简洁了许多。

2.2.3.集合的遍历
ArrayList支持三种最基本的遍历方式,我们通过一个案例来了解这几种遍历方式:

List<String> list = getList();
// 1.通过标准for循环语句遍历
for (int i = 0; i < list.size(); i++) {String element = list.get(i);System.out.println(element);
}
// 2.通过Foreach语句遍历
for (String element : list) {System.out.println(element);
}
// 3.通过迭代器进行遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String element = iterator.next();System.out.println(element);
}

需要注意地是,Foreach语句经编译器优化处理后,会得到一个迭代器遍历,因此理论上它的性能甚至不如迭代器遍历:

所以,以上三种遍历方式的性能从好到坏的顺序为:标准for > 迭代器遍历 > Foreach,不过Foreach的价值在于简单、方便,并且从实际测试效果来看,它们之间的性能差异其实非常微妙,可以忽略不计,基于这个原因,Foreach语句反而成了最受欢迎的集合遍历方式。
Set集合除了不支持标准的for语句外,迭代器和Foreach都是遍历Set集合常用方式,并且用法与ArrayList相同。
Map包含多种遍历方式,实践证明,下面的方式是遍历Map的最佳方式:

Map<String, Integer> map = getMap();
for(Map.Entry<String, Integer> entry : map.entrySet()) {String key = entry.getKey();Integer value = entry.getValue();
}

在上面的代码中,entrySet()方法返回的类型是一个以键值对为元素的Set集合,即在HashMap中实现的EntrySet集合类型:

可见,EntrySet与HashSet是同级别的Set类型集合。因此,for(Map.Entry<String, Integer> entry : map.entrySet())语句实际上是专门用来遍历Map的Foreach语句。

2.2.4.删除元素
对于List而言,删除单个元素的方法为remove(Object o)和remove(int index);List还提供了删除一组元素的方法—removeAll(Collection<?> c)),如果想删除整个list的元素,那么可以调用clear()方法。例如:

List<String> list2 = new ArrayList<>(10);
list2.add("a");
list2.add("b");
list2.add("c");
list2.add("d");
list2.add("e");
list2.add("e");
List<String> list3 = new ArrayList<>(10);
list3.add("b");
list3.add("c1");
list3.add("d2");
list3.add("e");
list2.removeAll(list3);
System.out.println(list2);
list2.clear();
System.out.println(list2);
List<String> list4 = new ArrayList<>(10);
list4.add("a");
list4.add("b");
list4.add("b");
list4.remove("b");
System.out.println(list4);
//~output:
[a, c, d]
[]
[a, b]

需要注意地是,remove(Object o)无法删除重复元素,只能删除重复元素中的第一个,我们可从源码中得出这一结论:

而removeAll()则可以删除当前集合中所有重复的元素:

List<String> list4 = new ArrayList<>(10);
list4.add("a");
list4.add("b");
list4.add("b");
List<String> list5 = new ArrayList<>(10);
list5.add("b");
list4.removeAll(list5);
System.out.println(list4);
//~output:a

Set集合除了不支持删除方法remove(int index)外,其他删除操作基本与List类似。
对于Map而言,可以根据键来完成对键值对元素的删除:remove(Object key)。
不过,要考虑一种特殊的情况:在for循环语句或forEach语句中删除元素时,List和Set要避免使用remove()方法,在for循环语句中可能会抛出IndexOutOfBoundsException异常,并且可能无法删除目标元素,例如:

List<String> list6 = new ArrayList<>(10);
list6.add("a");
list6.add("b");
list6.add("c");
list6.add("d");
list6.add("e");
for(int i = 0;i<list6.size();i++) {list6.remove(i);
}
System.out.println("list6 : " + list6);
//~output:
list6 : [b, d]

原因在于List在删除元素时,成员变量size也会随之减小,而当for循环的变量i大于等于size时,将抛出一个IndexOutOfBoundsException异常,即便没有发生该异常,随着size的减小,变量i却在增加,因此必然会导致元素删除错误:

而在forEach语句中删除集合的元素则可能会抛出一个

java.util.ConcurrentModificationException异常:
List<String> list6 = new ArrayList<>(10);
list6.add("a");
list6.add("b");
list6.add("c");
list6.add("d");
list6.add("e");
for(String str : list6) {list6.remove(str);
}
//~output:
Exception in thread "main" java.util.ConcurrentModificationException

原因在于forEach语句的底层是基于迭代器实现,而在迭代器中不允许执行新增、删除等操作迭代器通过维护一个expectedModCount成员变量来约束这一行为:

而在迭代器外部的add()、remove()方法中,程序每调用一次add()、remove()方法都会修改modCount(即Modification Count,修改次数)成员变量值:

因此在迭代器遍历集合时,若调用迭代器外部的add()、remove()方法都会导致List的modCount成员变量值与内部迭代器的expectedModCount成员变量值不一致,因此当迭代器通过next()方法获取元素时,首先会检查成员变量modCount与成员变量expectedModCount的值是否相等:若不相等,则抛出java.util.ConcurrentModificationException异常,通过该异常来约束迭代器在遍历过程中的行为—即只允许调用迭代器自身的remove()方法,不允许调用迭代器外部的方法来修改list:


注意到,在迭代器接口中,包含了如下几个方法:

    boolean hasNext():判断集合中是否还存在下一个元素;
   E next():获得集合中的下一个元素;
   void remove():删除集合中的元素。

可见,迭代器的作用主要有两个:遍历元素和删除元素。因此,我们可借助迭代器来实现循环中的元素删除:

List<String> list = new ArrayList<>(10);
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {iterator.next();iterator.remove();
}
System.out.println("list : " + list);
//~output:list:[]

在遍历Map的过程中,若尝试删除键值对元素,也会遇到同样的问题:

Map<String, Integer> map3 = new HashMap<>(16);
map3.put("a", 1);
map3.put("b", 2);
map3.put("c", 3);
for(Map.Entry<String, Integer> entry : map3.entrySet()) {String key = entry.getKey();map3.remove(key);
}
或
for(Map.Entry<String, Integer> entry : map3.entrySet()) {String key = entry.getKey();map3.put("d", 4);
}
//~output: Exception in thread "main" java.util.ConcurrentModificationException

在使用上述方式遍历Map时,虚拟机会将其转换为一个迭代器来执行,如下图所示:

为什么会转换为一个迭代器呢?在上面我们已经证明for(Map.Entry<String, Integer> entry : map.entrySet()){}这种语句实际上是专门用来遍历Map的Foreach语句,该语句会被虚拟机自动转换为一个EntryIterator迭代器(注:一种实现在HashMap内部的迭代器)来执行:



final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;//...if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}// ...
}

可以看到,在遍历Map时,若在循环内部调用了迭代器外部的put()、remove()等方法,那么将会抛出一个ConcurrentModificationException异常,抛出异常的原理与List类似,这里不再赘述。
如何解决Map在遍历时不能删除键值对元素的问题呢?实际上,处理办法与List类似,将Map的Foreach语句转换为一个迭代器来处理即可:

Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {Map.Entry<String, Integer> entry = iterator.next(); // 1iterator.remove(); // 2
}
System.out.println(map);
//~output:{}

注意:代码1和代码2的顺序1不能交换,否则会抛出特定的异常,也就是说,迭代器的remove()方法依赖于next()方法,所以必须让next()方法优先执行。

2.2.5.其他实用操作
2.2.5.1.listIterator()
在List集合中,可以通过listIterator()获得一个ListIterator迭代器,它是一个比Iterator更加强大的迭代器,属于Iterator的子类:

ListIterator与Iterator区别在于:ListIterator不仅继承了Iterator的全部方法,还实现了自己的方法。例如,Iterator只能通过next()方法单向移动获取元素,而ListIterator可以分别通过previous()和next()方法完成双向移动和元素的获取;此外,ListIterator还解决了Iterator不能添加元素的问题。例如:

public class CollectionDemo2 {public static void main(String[] args) {List<String> list = getList();ListIterator<String> listIterator = list.listIterator();int count = 0;// 向后移动游标while (listIterator.hasNext()) {int i = listIterator.previousIndex();System.out.println("previous:" + (i >= 0 ? list.get(i) : null));String curr = listIterator.next();System.out.println("curr:" + curr);}
// 一个迭代器对象只能循环一次,重新创建一个迭代器,并通过迭代器向list中添加新的元素
listIterator = list.listIterator();while (listIterator.hasNext()) {listIterator.next();listIterator.add("e" + (++count));}// 此时游标位于迭代器的末尾,可以向前移动游标while (listIterator.hasPrevious()) {String previous = listIterator.previous();System.out.println(previous);}// 将集合list的元素反向插入到新的List集合中List<String> newList = new ArrayList<>(10);int size = list.size();while (listIterator.hasNext()) {int i = listIterator.nextIndex();int newIndex = size - 1 - i;String newE = list.get(newIndex);newList.add(newE);listIterator.next();}System.out.println("list : " + list);System.out.println("newList : " + newList);}private static List<String> getList() {List<String> list = new ArrayList<>(10);list.add("a");list.add("b");list.add("c");return list;}
}
//~output:
previous:null
curr:a
previous:a
curr:b
previous:b
curr:c
e3
c
e2
b
e1
a
list : [a, e1, b, e2, c, e3]
newList : [e3, c, e2, b, e1, a]

注意:previous()和next()方法不能同时应用到一个遍历程序中,否则会引发死循环。

2.2.5.2.subList(int fromIndex, int toIndex)
该方法返回原始列表List连续元素的子列表,并且子列表的修改(删除、添加)结果都会反映到父列表中,原因在于subList()方法对应的子列表的add()、remove()等方法实现直接调用了父列表的add()、remove()方法:



例如:

public class CollectionDemo2 {public static void main(String[] args) {List<String> list = getList();List<String> subList = list.subList(1, 3);System.out.println("subList : " + subList);System.out.println("list : " + list);// 在子列表中添加元素后,增加的元素也会反映到父列表list中的特定位置subList.add("f");System.out.println("subList : " + subList);System.out.println("list : " + list);subList.remove(0);System.out.println("subList : " + subList);System.out.println("list : " + list);}private static List<String> getList() {List<String> list = new ArrayList<>(10);list.add("a");list.add("b");list.add("c");return list;}}
//~output:
subList : [b, c]
list : [a, b, c]
subList : [b, c, f]
list : [a, b, c, f]
subList : [c, f]
list : [a, c, f]

2.2.5.3.retainAll(Collection<?> c)
该方法用来保留当前集合与另一个集合中相同的元素,是一种获取两个集合间交集的解决办法,并且以当前集合作为交集元素的容器。因此,要注意调用该方法可能会替换当前集合中的元素。例如:

public class CollectionDemo3 {public static void main(String[] args) {List<String> list = getList();List<String> list2 = new ArrayList<>(10);list2.add("1");list2.add("a");list2.add("c");list2.add("2");// 保留相同元素—交集System.out.println("list : " + list);list.retainAll(list2);System.out.println("通过list求(list和list2)的交集 : " + list);System.out.println("list2 : " + list2);list2.retainAll(getList());System.out.println("通过list2求(list和list2)的交集 : " + list2);  }private static List<String> getList() {List<String> list = new ArrayList<>(10);list.add("a");list.add("b");list.add("c");return list;}}
//~output:
list : [a, b, c]
通过list求(list和list2)的交集 : [a, c]
list2 : [1, a, c, 2]
通过list2求(list和list2)的交集 : [a, c]

3.为什么选择集合而不是数组?
(1)集合都拥有自己的扩容机制,可以自动调整自身大小,而数组在初始化后其大小是固定不变的;
(2)集合具有丰富的特性,不同的集合应用也不同,而数组的特性单一;
例如,Set的元素是唯一的,List允许元素按照插入顺序排列且重复,Map允许你将某些对象和其他对象关联起来组成一个关联映射表。
(3)集合更好地体现了面向对象的思想。
数组与集合一样,都能充当对象的容器,但数组支持基本数据类型,而集合支持包装类型,因此集合更好地体现了面向对象的思想。Java本身是一门面向对象的语言,虽然数组存在的价值在于具有优越的性能,并且它天然支持基本数据类型,但正是因为基本数据类型才使得Java语言不再是“纯粹”的面向对象语言,所以在Java后期的版本中支持了自动包装类和泛型后,使得集合也能轻而易举地添加基本数据类型数据对应的包装类型引用。显然,与集合相比,数组除了优越的性能外,其他方面都黯然失色,几乎很少有应用的价值。
以上这些因素使得集合完全取代了数组。

Java集合深入解析(一)——集合的基本概念相关推荐

  1. java list_java中的list集合

    Java 集合 java集合概览 几幅图让你认识Java集合 java中List.Array.Map.Set等集合相互转换的最佳方法 关于Java集合最被关注的10 个问题 本列表会不断更新 文章 s ...

  2. java list详解_java集合List解析

    作为一个Developer,Java集合类是我们在工作中运用最多的.最频繁的类.相比于数组(Array)来说,集合类的长度可变,更加适合于现代开发需求: Java集合就像一个容器,可以存储任何类型的数 ...

  3. Java源码解析系列(一) Java集合框架

    版权声明:本文为博主原创文章,欢迎大家转载! 但是转载请标明出处: https://blog.csdn.net/t000818/article/details/82785664 ,本文出自:[唐宏宇的 ...

  4. spark- PySparkSQL之PySpark解析Json集合数据

    PySparkSQL之PySpark解析Json集合数据 数据样本 12341234123412342|asefr-3423|[{"name":"spark", ...

  5. JDK源码解析之集合篇2--Collection

    为什么80%的码农都做不了架构师?>>>    源码解析仅个人记录,若有不正确,请留言修改 package java.util;import java.util.function.P ...

  6. JAVA程序员面试题集合

    JAVA程序员面试题集合 分类: 编程语言 2012-12-08 12:10 50人阅读 评论(0) 收藏 举报 1.面向对象的特征有哪些方面 (1)抽象: 抽象就是忽略一个主题中与当前目标无关的那些 ...

  7. 杨老师课堂之ArrayList集合常用方法解析

    ArrayList集合常用方法的解析 1.概述 ​ 在前面我们学习了数组,数组可以保存多个元素,但在某些情况下无法确定到底要保存多少个元素,此时数组将不再适用,因为数组的长度不可变.例如,要保存一个学 ...

  8. JAVA基础加强篇08——集合

    集合概述 集合和数组都是容器. 数组的特点 数组定义完成并启动后, 类型确定.长度固定. 在进行增删数据操作的时候,数组是不太合适的,增删数据都需要放弃原有数组或者移位. 数组适合的场景 当业务数据的 ...

  9. JAVA 开发规范标准(集合)

    JAVA 开发规范 一.编程规约 (一)命名规约 1. [强制] 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符 号结束. 反例:_name / __name / $Object / ...

最新文章

  1. numpy.transpose()用法
  2. 统计计量 | 诺奖得主Angrist的因果推断课程文献读物单子
  3. AI在智能建筑中的应用和发展
  4. pycharm下找不到要安装的模块包(如sqlalchemy)
  5. c语言通用Makefile
  6. 教你一步步发布一个开源库到 JCenter
  7. 详解实时查看网卡流量的几款工具
  8. wifi 小米pro 驱动 黑苹果_搞定小米黑苹果自带WIF,又可省一个USB接口了
  9. 异常将上下文初始化事件发送到类的侦听器实例_Spring的Bean实例化原理,这一次彻底搞懂了!...
  10. hive+python数据分析入门
  11. sql 逻辑运算符_SQL Like逻辑运算符介绍和概述
  12. 讯飞输入法(原讯飞语音输入法) V2.1.1708 官方版-完美软件下载
  13. 解决navicat连接不上mysql8
  14. 决策树分类器vc维如何计算_机器学习入门第三章:决策树分类器-理论
  15. CentOS 7各版本镜像合集下载
  16. 查看DELL服务器保修期
  17. 轻松入门微信小程序云开发(详细)
  18. 【微信小程序】全局配置 - tabBar
  19. 基于SpringBoot开源框架的MES生产制造执行系统源码
  20. HHKB连不上Ubuntu

热门文章

  1. Thonny+Micropython ESP32固件下载后显示没有反应
  2. EMC 电磁兼容测试标准
  3. Stm32型号查阅手册
  4. 模块学习(五)——矩阵键盘
  5. GPS北斗授时服务,NTP让网络时钟同步更精确
  6. linux新磁盘初始化,如何在Linux操作系统中初始化磁盘
  7. Delphi2007来了
  8. 创新型中小企业认定条件有哪些?
  9. Matplotlib contours 等高线图
  10. 985助理教授与二本教授哪个水平高?