集合
集合不能直接存储基本数据类型,也不能直接储存java对象,集合存储的是对象的内存地址
所有的集合类和接口都在java.util包下
集合的继承结构图
java.lang.Collection接口常用方法
没有使用泛型之前,Collection中可以存储Object的所有子对象
使用泛型后,Collection中只能存储某个具体的类型
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(100); // 自动装箱,实际上放进去了一个对象的内存地址 Integer x = new Integer(100) ,x指向的内存地址放进去了
c.add(3.14);
c.add(new Object());
System.out.println(c.size()); // 3
Object[] a = c.toArray();
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]); // 输出的都是java对象toString后的内容 100 3.14 java.lang.Object@483bf400
}
c.clear();
System.out.println(c.size()); // 0
c.add("hello");
c.add("world");
System.out.println(c.contains("hello")); // true
c.remove("world");
System.out.println(c);
System.out.println(c.isEmpty()); // false
}
Collection接口的contains和remove方法深入理解
public static void main(String[] args) {
Collection c = new ArrayList();
String s1 = new String("abc");
String s2 = new String("def");
c.add(s1);
c.add(s2);
String x = new String("abc");
System.out.println(c.contains(x)); // true 因为 contains方法底层调用了equals方法,equals比较的是内容不是内存地址
}
public class Test04 {
public static void main(String[] args) {
User u1 = new User("Vansal");
User u2 = new User("Vansal");
Collection c = new ArrayList();
c.add(u1);
// User类没重写equals方法前
boolean b1 = c.contains(u2); // false 因为User类没有重写equals方法,会调用父类Object的equals方法比较内存地址,所以false,重写后为true
// 重写后
User u3 = new User("Vansal");
c.remove(u3); // remove方法同理,底层也是用equals判断的
System.out.println(c.size()); // 0
}
}
class User {
String name;
public User() {
}
public User(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}
}
集合迭代/遍历
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(100);
c.add("a");
c.add("b");
// 要对c进行遍历,先获取该对象的迭代器
Iterator it = c.iterator();
// 通过迭代器对象对集合进行遍历
System.out.println(it.next());
while (it.hasNext()) {
System.out.println(it.next()); // 取出来的都是对象 100 a b
}
}
集合元素的删除
集合的结构发生改变后,一定要重新获取迭代器,不然会报ConcurrentModificationException错误
下面代码c.remove(o)虽然集合c删除了这个对象,但是迭代器中还存在这个对象的快照,两者不符就会报错
用迭代器去删除时会自动更新迭代器和集合
public class Test05 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(1);
c.add(2);
c.add(3);
Iterator it = c.iterator();
while (it.hasNext()) {
Object o = it.next();
// c.remove(o); 会报ConcurrentModificationException错误 要使用迭代器的remove方法删除
it.remove(); // 删除迭代器指向的当前元素
System.out.println(o);
}
System.out.println(c.size()); // 0
}
}
List接口特有的方法
public class Test06 {
public static void main(String[] args) {
List myList = new ArrayList();
myList.add(1);
myList.add(2);
myList.add(3);
// 在列表的指定位置插入元素,这个方法使用不多,因为对ArrayList集合涞水效率较低
myList.add(1, "aaa");
Iterator it = myList.iterator();
while (it.hasNext()) {
Object o = it.next();
System.out.println(o); // 1 aaa 2 3
}
// 根据下标获取元素
Object a = myList.get(1);
System.out.println(a);
// 因为有下标,List集合就有自己特殊的遍历方式
for (int i = 0; i < myList.size(); i++) {
System.out.println(myList.get(i));
}
// 获取指定对象在集合中第一次出现的索引
System.out.println(myList.indexOf("aaa")); // 1
// 获取指定对象在集合中最后一次出现的索引
System.out.println(myList.lastIndexOf("aaa")); // 1
// 删除指定下标的元素
myList.remove(1);
System.out.println(myList); // [1, 2, 3]
// 修改指定下标的元素
myList.set(1,100);
System.out.println(myList); // [1, 100, 3]
}
}
ArrayList类详解
public static void main(String[] args) {
// 默认初始化容量是10
ArrayList arr1 = new ArrayList();
// 指定初始化容量
ArrayList arr2 = new ArrayList(20);
Collection c = new HashSet();
c.add(1);
c.add(2);
c.add(3);
// 可以传入一个集合,将HashSet转换为ArrayList
ArrayList arr3 = new ArrayList(c);
System.out.println(arr3); // [1, 2, 3]
}
LinkedList类详解
单向链表数据结构
链表优点:随机增删元素效率较高,因为增删元素不涉及到大量元素位移的问题
链表缺点:查询效率比较低,因为链表的内存地址不连续,每一次查找元素都需要从头节点开始往下遍历
删除元素时
双向链表数据结构
Vector类详解
Vector中所有的方法都是同步的,是线程安全的,所以效率比较低,使用较少
非线程安全转换为线程安全
public static void main(String[] args) {
List myList = new ArrayList();
Collections.synchronizedList(myList);
myList.add(1);
myList.add(2);
myList.add(3);
}
集合总结
需要检索查找的选择ArrayList,需要增删的选择LinkedList,一般我们加元素都是在末尾添加,所以ArrayList用的更多
泛型
public class Test09 {
public static void main(String[] args) {
List<Animal> myList = new ArrayList<>();
Animal c = new Cat();
Animal b = new Bird();
myList.add(c);
myList.add(b);
// 使用泛型后,除了Animal和Animal的子类外,其他都不行
// myList.add(new Object()); 编译不通过,报错
Iterator<Animal> it = myList.iterator();
while (it.hasNext()) {
// 使用泛型之后,每一次返回的数据都是Animal类型,就不需要进行强制类型转换
Animal a = it.next();
a.move();
}
}
}
class Animal {
public void move() {
System.out.println("动物在移动");
}
}
class Cat extends Animal {
public void move() {
System.out.println("猫在抓老鼠");
}
}
class Bird extends Animal {
public void move() {
System.out.println("鸟在飞翔");
}
}
泛型的优缺点
优点:集合中存储的元素类型统一了,从集合中取出元素类型是泛型指定的类型,不需要进行大量的向下转型了
缺点:导致集合中的元素缺乏多样性
自定义泛型
自定义泛型时,<>内是一个标识符,可以随便写
一般用<E>或<T>,E代表Element元素,T代表Type类型
public class Customize<T> {
public static void main(String[] args) {
// new对象时指定了泛型的类型为String
Customize<String> c1 = new Customize<>();
// doSome方法只能传String类型的参数
c1.doSome("Vansal");
Customize<Integer> c2 = new Customize<>();
c2.doSome(100);
Integer a = c2.getBack(100);
System.out.println(a);
Customize<A> c3 = new Customize<>();
A a1 = c3.getBack(new A());
System.out.println(a1); // A
}
public void doSome(T o) {
System.out.println(o);
}
public T getBack(T t) {
return t;
}
}
class A {
public String toString() {
return "A";
}
}
增强for循环(foreach)
缺点:没有下标