Java里面的容器是核心概念,这个是面向对象区别C语言的主要区别(这个是我个人认为的,当然区别还有很多)
基本从学习java的第二天就会接触容器的概念,包括List、Vector(JDK1.5后基本不再用了)、ArrayList、HashMap等等。有了容器,在日常开发中能很容易把业务逻辑融合到容器中,能替代数组的大部分操作。
谈到数组,估计很多人第一印象是多维数组循环是多么的复杂,我当时学C时,二维数组就基本把人搞晕了,不过到Java里面,因为容器的存在,数组一般不会用到,多数都用容器替代,主要原因是数组初始化时需要定义数组长度,而容器则不需要,基本是可变长度。定义一个可变长度的数组还是比较麻烦的。
上面那个图中,其中淡绿色的表示接口,红色的表示我们经常使用的类。
基本概念
Collection
一个独立元素的序列,这些元素都服从一条或多条规则。其中List必须按照插入的顺序保存元素、Set不能有重复的元素、Queue按照排队规则来确定对象的产生顺序(通常也是和插入顺序相同)
Map
一组成对的值键对对象,允许用键来查找值。ArrayList允许我们用数字来查找值,它是将数字和对象联系在一起。而Map允许我们使用一个对象来查找某个对象,它也被称为关联数组。或者叫做字典。
List主要用法
代码参见:com.critc.container.ListTest.java
public static void main(String[] args) {
// List基本操作
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
List<Integer> list2 = new ArrayList<>();
//第一种循环输入方式
for (String str : list) {
System.out.println("第一种输出list值:" + str);
}
//第二种循环输出方式
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println("第二种输出list值:" + str);
}
//第三种循环输出方式
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println("第三种输出list值:" + it.next());
}
//第四种循环输出方式
list.forEach(str -> System.out.println("JDK8中输出list值:" + str));
}
List在使用过程中有个陷阱,就是remove方法,平时想从list列表中直接删除一个值,结果循环后发现不对,原理就是每删除一个元素,该list的size值变化了,导致后续的循环错位。
代码参见:com.critc.container.ListRemoveTest
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
/* for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
list.remove(str);
System.out.println("剩余元素:" + list.toString());
}*/
//从list的最后一个元素进行循环
for (int i = list.size() - 1; i >= 0; i--) {
String str = list.get(i);
list.remove(str);
System.out.println("剩余元素:" + list.toString());
}
}
** 解决这个问题的方式就是list进行倒序循环,保证删除一个元素后整个list的顺序是不变的,这就可以正常解决问题了**
set主要用法
set区别list的主要原因就是set不允许对象重复,list是可以的。
代码参见:com.critc.container.SetTest
public static void main(String[] args) {
Set set = new HashSet();
set.add("abc");
set.add("cde");
set.add("efg");
set.add("fgh");
set.add("abc"); //重复的abc,set会自动将其去掉
System.out.println("size=" + set.size());
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println("输出set的值:" + it.next());
}
}
queue主要方法
queue这个用的比较少,只允许在表的一端进行插入,而在另一端进行删除元素的线性表,一般用在模拟一个队列,从对头获取并删除(pool()),从队尾加入(offer())。
代码参见:com.critc.container.QueueTest
public static void main(String[] args) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue<String> queue = new LinkedList<String>();
//添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for (String q : queue) {
System.out.println("循环队列:" + q);
}
System.out.println("---------------------");
System.out.println("获取第一个元素并把这个删除:" + queue.poll()); //返回第一个元素,并在队列中删除
System.out.println("---------------");
System.out.println("获取第一个元素=" + queue.element()); //返回第一个元素,不移除,使用element()或peek()方法
for (String q : queue) {
System.out.println(q);
}
}
Map主要方法
Map集合的key具有一个特征:所有的key不能重复,且key之间没有顺序(TreeMap是有顺序的),如果将Map集合的所有key集中起来,那这些key就组成了一个set集合,则map集合提供了keySet()方法返回所有key组成的集合。
代码参见:com.critc.container.HashMapTest
public static void main(String[] args) {
HashMap<String, Double> scores = new HashMap<>();
scores.put("语文", 89.0);
scores.put("数学", 83.0);
scores.put("英文", 84.0);
System.out.println(scores.values());
Set<Map.Entry<String, Double>> entrySet = scores.entrySet();
for (Map.Entry<String, Double> entry : entrySet) {
System.out.println("取得key:" + entry.getKey());
System.out.println("对应的value为:" + entry.getValue());
}
System.out.println("是否包含key:"+ scores.containsKey("语文"));
System.out.println("是否包含value:"+ scores.containsValue(83.0));
}
Hashmap的主要方法有put(),containsKey().containsValue()
跟map有关的还有LinkedHashMap和TreeMap,这两个是有序的HashMap,具体区别找度娘即可。
java的容器是非常复杂的,这里面还涉及一个线程安全和线程不安全的概念,比如HashMap和HashTable,HashTable是线程安全的,可以在不同线程中通过HashTable来共享对象。普通开发中用HashMap足够,很少会用到HashTable的。我遇到过的用HashTable 的例子是通过HashTable来存储多个数据源对象,在多线程中调用HashTable来获取不同的数据源,一开始是用HashMap,后来发现存在数据源获取错误的时候,最后才发现是线程安全问题。