对于数据的存储,我们已经介绍了数组,但是数组存储存在着很多的不足,如:
① 一旦初始化以后,其长度就不可修改
② 对于添加、删除、插入数据等操作非常不便,效率不高
③ 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足
④ 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
Java集合分为Collection和Map两种体系
一.Collection接口
Collection接口用于存储单列数据,
1)List接口:元素有序、可重复的集合
2)Set接口:元素无序、不可重复的集合
1.Collection接口中的主要方法
//Collection集合的主要方法说明
public class CollectionTest {
public static void main(String[] args) {
Collection arrayList = new ArrayList();
Collection arrayList1 = new ArrayList();
//1.add(object obj):向集合中添加元素
arrayList.add("hello");
arrayList.add(123);
arrayList1.add('A');
arrayList1.add(new String("中国"));
//2.size():返回集合元素个数
System.out.println(arrayList.size());
//3.addAll(Collection c):将指定集合c中的元素添加到指定集合中
arrayList.addAll(arrayList1);
System.out.println(arrayList.size());
//4.clear():删除集合中的所有元素
//arrayList.clear();
System.out.println(arrayList.size());
//5.isEmpty():判断当前集合是否为空
System.out.println(arrayList.isEmpty());
//6.contains(object obj):判断当前集合是否包含指定元素
//注意:此时实际调用的是实际对象obj重写后的equals()方法,若没有重写,则调用的是Object类中的equals()方法==
System.out.println(arrayList1.contains(new String("中国")));//true
//7.containsAll(collection c):判断该集合是否包含指定集合c中所有的元素
System.out.println(arrayList.containsAll(arrayList1));
System.out.println(arrayList);
//8.equals(collection c):判断该集合是否和指定集合c相等(元素相同,注意集合有序时还要顺序相同)
System.out.println(arrayList.equals(arrayList1));
//9.remove(object o):删除该集合中指定的元素
System.out.println(arrayList.remove(123));
System.out.println(arrayList);
//10.removeAll(collection c):删除包含指定集合c中所有的元素,返回boolean值
System.out.println(arrayList.removeAll(arrayList1));//true
//11.retainAll(Collection c):保留包含指定集合c中所有的元素
System.out.println(arrayList);
System.out.println(arrayList1);
System.out.println(arrayList.retainAll(arrayList1));//true
//12.hashCode();返回当前对象的哈希值
System.out.println(arrayList.hashCode());
//13.toArray():集合--->数组
Object[] array = arrayList1.toArray();
for(int i=0;i<array.length;i++){
System.out.print(array[i]+"\t");
}
System.out.println();
//扩展:数组--->集合
List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(strings);
List<int[]> ints = Arrays.asList(new int[]{1, 1, 3, 44});
System.out.println(ints);//[[I@6d6f6e28]认为是一个元素,要写成对应的包装类就可以了
List<Integer> integers = Arrays.asList(new Integer[]{11, 22, 33});
System.out.println(integers);
}
}
2.Collection集合中元素的遍历
迭代器模式:提供了一种方法访问一个容器对象中各个元素,而不需要暴露对象的内部细节。
(1)迭代器实现集合遍历
@Test//iterator迭代器,实现集合的遍历操作。
public void test01(){
Collection arrayList = new ArrayList();
arrayList.add("ABC");
arrayList.add(new String("中国"));
arrayList.add(123);
//① next():迭代中的下一个元素 ② hasNext():判断下一个元素是否存在
Iterator iterator = arrayList.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前,此外,迭代器本身不存储数据,只是用于遍历集合。
(2)增强for循环实现集合遍历
@Test//增强for循环遍历集合、数组
public void test03(){
Collection arrayList = new ArrayList();
arrayList.add("ABC");
arrayList.add(new String("中国"));
arrayList.add(123);
for(Object obj: arrayList){//实际就是将值赋值给obj后输出
System.out.println(obj);
}
}
3.使用迭代器删除指定的元素
@Test
public void test02(){
Collection arrayList = new ArrayList();
arrayList.add("ABC");
arrayList.add(new String("中国"));
arrayList.add(123);
Iterator iterator = arrayList.iterator();
while(iterator.hasNext()){
//iterator.remove();
Object next = iterator.next();//必须先next(),后remove(),否则会IllegalStateException
if(next.equals("ABC")){
iterator.remove();
}
}
}
二.List接口
List集合元素有序、可重复,实现了动态”数组“,线性存储,可以用于替换原有的数组。
1.ArrayList(最常用)
JDK 7下:
源码分析:ArrayList list = new ArrayList();底层是创建了长度为10的Object[] elementData数组,执行list.add(数据)时,当添加的个数超过10个,就会扩容为原来容量的1.5倍,并将原来的数据复制到新的list中。
开发建议使用带参数的构造器,指定长度:ArrayList list = new ArrayList(int Capacity);
JDK 8以后
源码分析: ArrayList list = new ArrayList();底层Object[] elementData初始化为{},当执行第一次list.add()后,才创建了长度为10的数组,后续操作和上述一样实现扩容。
List常用方法
Lis接口t继承自Collection接口,允许出现重复的元素,元素有序,可以使用索引访问,不但继承了Collection的全部方法,还增加了一些特有方法:
public class ListTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
List list = Arrays.asList(1, 2, 3, 5);
//1.boolean add(object obj):向集合中添加一个元素
arrayList.add("Hello");
arrayList.add(111);
arrayList.add(true);
arrayList.add(new String("设计模式"));
System.out.println(arrayList);
//2.boolean addAll(Collection c):将指定的集合添加到该集合中
arrayList.addAll(list);
System.out.println(arrayList);
//3.Object set(int index,Object element):修改指定索引处的元素,返回修改后的结果
System.out.println(arrayList.set(0,"小王"));
//4.Object get(int index):返回索引位置处的元素
System.out.println(arrayList.get(0));
//5.Object remove(int index):删除指定位置的元素,返回删除的元素
//Object remove(object obj):
System.out.println(arrayList.remove(0));
//6.void add(int index,Object obj):在指定的位置插入元素,返回插入后的集合
arrayList.add(0,"小吕");
System.out.println(arrayList);
//7.int size():返回该集合的长度
System.out.println(arrayList.size());
//8.List subList(int fromIndex,int toIndex):返回索引之间的子集合(左闭右开),原集合不变
System.out.println(arrayList.subList(1,5));
System.out.println(arrayList);
}
}
集合三种遍历操作:
@Test
public void test01(){
ArrayList arrayList = new ArrayList();
arrayList.add("Hello");
arrayList.add(111);
arrayList.add(true);
arrayList.add(new String("设计模式"));
//1.iterator迭代器
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t");
}
System.out.println();
//2.增强for循环
for(Object obj: arrayList){
System.out.print(obj+"\t");
}
System.out.println();
//3.普通for循环
for(int i=0;i<arrayList.size();i++){
System.out.println(arrayList.get(i)+"\t");//使用索引访问集合元素
}
}
2.LinkedList
源码分析:LinkedList linkedList = new LinkedList();内部声明了Node类型的first和last属性维护一个双向循环链表,默认值为null;当执行linkedList .add(123);将123封装到Node中,创建了Node对象。
LinkedList中部分常见的特有方法
public class ListTest02 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
//1.void add(Object obj):添加元素
linkedList.add("Java");
//2.void add(int index,E element):指定位置插入指定元素
linkedList.add(0,"设计模式");
//3.void addFirst(Object obj):将指定元素插入集合开头
linkedList.addFirst(123);
//4.boolean offer(Object obj):向集合尾部追加元素
System.out.println(linkedList.offer("offer"));//true
//5.void push(Object obj):向集合头部追加元素
linkedList.push(true);
System.out.println(linkedList);//[true, 123, 设计模式, Java, offer]
//6.Object poll():移除并返回第一个元素
System.out.println(linkedList.poll());//true
//7.Object peek():获取集合中的第一个元素
Object peek = linkedList.peek();
System.out.println(peek);//123
System.out.println(linkedList);//[123, 设计模式, Java, offer]
//8.Object removeFirst():删除集合中第一个元素,返回删除的元素
System.out.println(linkedList.removeFirst());//123
//9.Object pollLast():删除集合最后一个元素,返回删除的元素
System.out.println(linkedList.pollLast());//offer
System.out.println(linkedList);//[设计模式, Java]
}
}
3.Vector
Vector线程安全,在JDK 7和JDK 8使用Vector()构造器创建时创建的数组长度为10,扩容方面扩容为原来长度的2倍。
三.Set接口
Set接口也继承自Collection接口,底层数组长度为16,但未对Collection接口中的方法进行扩充,Set接口中的元素无序,不可重复,添加对象时该对象所在的类必须重写hashCode()和equals()方法,判断两个对象相同使用equals()方法。
① 无序性:不等于随机性,存储的元素不是连续存储,而是根据hash值存储。
② 不可重复性:当向HashSet集合中添加元素时,首先会调用该元素的hashCode()方法来确定该元素的存储位置,然后再调用元素的equals()方法来确保该位置没有重复元素。
1.HashSet
作为Set的主要实现类,线程不安全,底层是数组加链表,可以存储null值。
public class HashSetTest {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(123);
hashSet.add(true);
hashSet.add(new String("Java"));
hashSet.add(new User("小王", 18));
hashSet.add(new User("小王", 18));
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class User{
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
System.out.println("User类的equals方法调用了...");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age &&
Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
1.1LinkedHashSet:
作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序输出,对于频繁的遍历操作,LinkedHashset效率高于HashSet。
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add("Java");
linkedHashSet.add(1234);
linkedHashSet.add(new String("设计模式"));
linkedHashSet.add(true);
Iterator iterator = linkedHashSet.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t");
}
}
}
2.TreeSet
TreeSet是Set接口的一个实现类,底层采用平衡二叉树存储元素,使得TreeSet集合中不存在相同的元素,并且可以对元素加进行排序。要求向TreeSet中添加的元素必须是相同类的对象。
① 自然排序:比较两个对象是否相同的标准为: compareTo()返回0,不再是equals().
public class TreeSetTest {//TreeSet自然排序实现
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Person(18,"ALi"));
treeSet.add(new Person(18,"Jhon"));
treeSet.add(new Person(17,"Jack"));
treeSet.add(new Person(19,"Tom"));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class Person implements Comparable{
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Object o) {//按age排序,年龄相同时按name排序
if(o instanceof Person){
Person p = (Person)o;
int num = Integer.compare(this.age,p.age);
if(num!=0){
return num;
}else{
return this.name.compareTo(p.name);
}
}else
throw new RuntimeException("输入的类型不匹配...");
}
}
② 定制排序:比较两个对象是否相同的标准为: compare()返回0,不再是equals().
public class TreeSetTest01 {
public static void main(String[] args) {
Comparator comparator = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Animal && o2 instanceof Animal){
Animal o11 = (Animal) o1;
Animal o21 = (Animal) o2;
return Integer.compare(o11.getAge(),o21.getAge());//比较age,如果age相同,不再存储
}
throw new RuntimeException("传入的类型不匹配...");
}
};
TreeSet treeSet = new TreeSet(comparator);//指定是定制排序
treeSet.add(new Animal(18,"ALi"));
treeSet.add(new Animal(18,"Jhon"));
treeSet.add(new Animal(17,"Jack"));
treeSet.add(new Animal(19,"Tom"));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class Animal {
private int age;
private String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
面试题:
1.ArrayList、LinkedList、Vector三者有何异同?
相同:三者都实现了List接口,存储有序、可重复的元素,动态存储
不同:
ArrayList:List主要实现方式,底层使用Object[] elementData存储,线程不安全,效率高
LinkedList:底层使用双向链表存储,对于频繁的插入、删除操作效率高
Vector:底层使用Object[] elementData存储,线程安全、效率低
2.输出下列程序的运行结果
@Test
public void test02(){
ArrayList arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
updateList(arrayList);
System.out.println(arrayList);
}
public void updateList(List list){
//list.remove(2);//输出[1, 2]
list.remove(new Integer(2));//输出[1, 3]
}
方式1是索引,删除的是索引位置的元素
方式2是元素,删除的是元素