Java集合
集合的概念
在java中,集合是一个名词而不是动词,集合是一种存储数据的容器。
如果只是为了容纳数据,可以使用数组。为什么有数组了还需要集合来存储数据呢?
- 数组使用时要确定元素个数,当使用场景中的元素个数不确定时,使用数组不太方便。
集合指的是一系列框架,在java.util中。包含了集合一系列的接口和类。
对不确定个数的有关系的数据进行相同的逻辑处理时,我们使用集合来操作更好。
根据数据的不同,java中的集合分为两种类型:
- 单一数据体系:Collection接口定义了相关规范
- 成对出现的数据体系:所谓的成对,是值一对数据相关联,可根据第一个数据关联到第二个数据。也称之为键值对数据
("kevin",20)->(key,value)
。比如Map接口就定义了这种规则。
常用接口和类
Collection接口:
常用的子接口
- List:按照插入顺序保存数据,数据可以重复。
-
- 主要的具体实现类为:ArrayList,LinkedList
- Set:集,无序保存。数据不能重复。
-
- 主要的具体实现类为:HashSet
- Queue:队列
-
- 主要的具体实现类为:ArrayBlockingQueue
Map接口:
- 主要的具体实现类为:HashMap,HashTable
List
ArrayList
List:表示列表,清单。在清单中我们是先取出先插入的,所以list也是按照插入顺序读取,第一个插入,读取时也是第一个。并且它可以存储重复的数据。
Array:表示数组,序列。
在ArrayList这个集合类的内部就是使用数组来存储数据的。
ArrayList对象的创建:
ArrayList arrayList = new ArrayList();
其中,有三种方式创建一个ArrayList:
- 不传参数,直接new。底层会创建一个空数组
- 传一个int类型的参数,用于设定底层数组的长度
- 传一个Collection集合类型的对象,用于将其他集合放置在当前集合中
我们来打印这个ArrayList对象:
ArrayList arrayList = new ArrayList();
System.out.println(arrayList);// []
返回的集合对象中的数据(当前为空)
集合对象的基本操作
增加数据
我们使用add方法向集合添加数据:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qina");
arrayList.add("kun");
System.out.println(arrayList);// [kevin, qina, kun]
对于一个没有传参new出的对象,它底层的数组长度会被自动设置为10。
那如果我们的数据超出了底层的数组长度会怎样呢?
为了方便,我们就声明一个底层数组长度为3的集合:
ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qina");
arrayList.add("kun");
arrayList.add("yes");
System.out.println(arrayList);// [kevin, qina, kun, yes]
我们看到,依然会进行添加数据。并且也没有报错,那这是为什么呢?
原来,在底层,遇到元素个数超出数组长度时。会创建一个更大的数组,并且将索引的内存空间指向原来的对象,然后再进行添加。这个操作叫扩容。而原来的数组就不会被使用了。
获取数据
使用get方法获取数据,传入参数为索引。使用size()
方法来获取集合的元素个数
ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");
System.out.println(arrayList.size());// 4
System.out.println(arrayList.get(1));// qian
使用for循环来遍历集合:
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
如果不关心数据的位置,我们可以使用forin来遍历(按顺序遍历):
- for(类型 循环对象:集合){}
for (Object o : arrayList) {
System.out.println(o);
}
修改数据
使用set()
方法修改数据,传入两个参数:
- 第一个参数:下标(索引)
- 第二个参数:修改的值
方法会返回结果,返回值为更新前的值:
ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");
Object chage = arrayList.set(3, "no");
arrayList.set(0, 3.0);
System.out.println(chage);// yes
System.out.println(arrayList);// [3.0, qian, kun, no]
删除数据
使用remove()
方法可以删除集合的数据,传入参数为数据的位置(索引):
方法会返回结果,返回值为要删除的值
ArrayList arrayList = new ArrayList(3);
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("kun");
arrayList.add("yes");
Object o = arrayList.remove(1);
System.out.println(o);//qian
System.out.println(arrayList.size());// 3
System.out.println(arrayList);// [kevin, kun, yes]
ArrayList常用方法
当add()
传入两个参数时,可以用于向集合指定位置插入数据:
- 第一个参数:索引
- 第二个参数:添加的数据
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add(0, "demo");
System.out.println(arrayList);// [demo, kevin, qian]
当插入数据时,之前该位置的数据会与后面的数据一起向后移。
使用clear()
方法来清空集合中的数据:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
System.out.println(arrayList.size());// 2
arrayList.clear();
System.out.println(arrayList);// []
System.out.println(arrayList.size());// 0
使用removeAll()
来删除集合中的指定数据(如果移除了返回true,反之为false):
参数类型是一个集合,可以使用Collections.singleton()
来转为一个集合。
使用addAll()
方法可以将一个集合的所有数据添加到该集合中(同样可以传两个参数)。
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
ArrayList list2 = new ArrayList();
list2.add("ok");
list2.add("nice");
arrayList.addAll(list2);
arrayList.removeAll(Collections.singleton("qian"));
System.out.println(arrayList);//[kevin, ok, nice]
boolean b = arrayList.removeAll(list2);
boolean b1 = arrayList.removeAll(Collections.singleton("lll"));
System.out.println(b);//true
System.out.println(b1);//false
System.out.println(arrayList);//[kevin]
使用isEmpty()
方法来判断集合是否为空:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
ArrayList list2 = new ArrayList();
System.out.println(arrayList.isEmpty());// false
System.out.println(list2.isEmpty());// true
使用contains()
方法来判断集合中是否有这个数据:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
System.out.println(arrayList.contains("kevin"));// true
System.out.println(arrayList.contains("kun"));// false
使用indexOf()
方法和lastIndexOf()
找到第一次出现和最后一次出现该数据的位置
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
System.out.println(arrayList.indexOf("qian"));// 1
System.out.println(arrayList.lastIndexOf("qian"));// 3
集合转数组:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
Object[] objects = arrayList.toArray();
System.out.println(objects[1]);//qian
复制一份集合:
ArrayList arrayList = new ArrayList();
arrayList.add("kevin");
arrayList.add("qian");
arrayList.add("qian");
arrayList.add("qian");
Object clone = arrayList.clone();
System.out.println(clone);// [kevin, qian, qian, qian]
LinkedList
LinkedList是一种链表结构
LinkedList list = new LinkedList();
list.addFirst("kevin");
System.out.println(list.getFirst());// kevin
System.out.println(list.getLast());// kevin
System.out.println(list);// [kevin]
取第一个数据和最后一个:
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
System.out.println(list.getFirst());// qian
System.out.println(list.getLast());// kevin
System.out.println(list);// [qian, kevin]
添加数据(add默认添加到最后,两个参数为插入该索引位置):
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
System.out.println(list);// [qian, kun, kevin]
get
方法取数据
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
System.out.println(list.get(1));// kun
使用for循环遍历
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// qian
// kun
// kevin
使用forin遍历:
for (Object o : list) {
System.out.println(o);
}
set
方法修改数据
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
list.set(0, "kkkevin");
System.out.println(list.getFirst());// kkkevin
remove
方法移除集合中的传入第一个数据:
LinkedList list = new LinkedList();
list.addFirst("kevin");
list.addFirst("qian");
list.add(1, "kun");
list.addLast("kevin");
list.remove("kevin");
System.out.println(list);// [qian, kun, kevin]
LinkedList常用方法
除了上面的一些方法外,它还有一些api我们经常使用:
push()
方法用于向开头添加数据,类似addFirst。
LinkedList list = new LinkedList();
list.push("kevin");
list.push("qian");
list.push("kun");
list.add("yt");
System.out.println(list);//[kun, qian, kevin, yt]
element()
方法用于取第一个数据,类似getFirst。
System.out.println(list.element());//kun
pop用于弹出数据,弹出第一个数据,返回值也是弹出的数据。
LinkedList list = new LinkedList();
list.push("kevin");
list.push("qian");
list.push("kun");
list.add("yt");
System.out.println(list);//[kun, qian, kevin, yt]
System.out.println(list.pop());// kun
System.out.println(list);// [qian, kevin, yt]
泛型
由于多态性,集合在默认情况下下使用的时候非常不方便,因为数据的默认类型都是Object:
ArrayList list = new ArrayList();
list.add(new Person());
list.add(new Person());
Object o = list.get(1);
o.personName();// 无法解析 'Object' 中的方法 'personName'
Person类:
class Person {
public void personName() {
System.out.println("person...");
}
}
如果要使用,就必须使用强制类型转换。非常不方便,而且可能出现类型转换错误的异常。
Person o = (Person) list.get(1);
o.personName();// person...
这时候我们可以使用泛型,我们传入的数据都是同一个类型:
ArrayList<Person> list = new ArrayList();
list.add(new Person());
Person person = list.get(0);
person.personName();// person...
类型存在多态的使用,而泛型是没有多态的。
public class JavaCollection_05 {
public static void main(String[] args) {
Contains<User5> contains = new Contains();
test(contains);// 需要的类型:Contains<Object> 提供的类型:Contains<User5>
}
public static void test(Contains<Object> contains) {
System.out.println(contains);
}
}
class Contains<C> {
public C data;
}
class User5 {
}
比较器
集合提供了sort方法来进行排序,传入的是一个实现了比较器接口的类的实例对象:
import java.util.ArrayList;
import java.util.Comparator;
public class JavaCollection_06 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(2);
list.add(1);
list.add(3);
list.add(0);
list.sort(new NumberComparator());
System.out.println(list);// [0, 1, 2, 3]
}
}
class NumberComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;// 升序
}
}
其中,compare方法中:
- 参数1>参数2,返回值为正数,表示升序
- 参数1<参数,返回值为负,表示降序
- 参数1==参数2,返回值为0。
官方解释:compare方法比较其两个顺序参数。返回负整数、零或正整数,因为第一个参数小于、等于或大于第二个参数。
ArrayList与LinkedList比较
添加第一条数据,LinkedList更快
添加后面的数据,ArrayList更快
但是,当ArrayList需要扩容时,LinkedList更快。
而插入数据,LinkedList更快。
由于LinkedList没有索引的概念(在LinkedList中叫顺序),在LinkedList中使用索引实际上还是在按着顺序找。所以使用索引查找时,ArrayList更快。
如果不使用索引查找,两个集合的查找没有本质区别。