从这一章,开始学习集合。
集合的基本概念
我们如果要保存一组类型相同的元素时,我们应当使用一个容器来存储信息,数组就是这样一个容器。
数组的缺点:数组一旦定义,长度将不能再变化。
但是在实际开发时,经常需要保存一些不断变长的数据集合,于是,我们需要一些能够动态增长长度的容器来保存数据。
而在实际开发中,我们需要对数据的保存的逻辑可能是各种各样的,于是就有了各种各样的数据结构。所以java提供了许多不同的存储结构,而java中对于各种数据结构的实现,就是我们用到的集合。
1数组
2链表
3哈希
4树
集合的体系
集合是由很多接口,抽象类,具体类组成的,都位于java.util包中。
集合体系分为:单列集合 双节集合
关于Collection接口
Collection接口:定义了存取一组对象的方法,其子接口Set和List又分别定义了存储方式。
List:List中的数据对象应有顺序,而且元素可以重复。
Set:Set中的数据对象没有顺序,而且不可以重复。
Collection接口,作为单列集合中顶级接口,里面定义了单列集合共有的方法。
方法主要以 增,删,改,查,判断为主。
Collection<E> ,ArrayList<E>后面的<E>是JDK5之后的语法----泛型
可以通过泛型语法,为集合设置一个类型,这样就只能存储设置的数据类型。
集合中建议存储同一种数据类型。
集合是容器,可以存储不同的数据,严格上讲集合是可以存储任何类型的(只能存储引用类型)
注意!:数组的长度length,字符串长度length(),集合长度size()
下面看一下在Collection中定义的一些集合中的共有的方法。
/* Collection接口中的共有方法 a.add(); a.addAll(b); a.remove("A"); a.retainAll(b) a.size() a.isEmpty() a.equals(b) Object[] objs =a.toArray(); String[] sobject = a.toArray(new String[a.size()]); */
public class collectionDemo1 { public static void main(String[] args) { Collection<String> a =new ArrayList<>(); a.add("a"); a.add("b"); a.add("c"); a.add("d"); Collection<String> b =new ArrayList<>(); b.add("A"); b.add("B"); b.add("C"); b.add("D"); System.out.println(a); System.out.println(b); a.addAll(b);//将一个集合的元素全部添加到另一个集合中。 System.out.println(a); a.remove("A");//删除集合中指定的元素。如果删除成功,返回true,否则,返回false System.out.println(a.remove("A")); System.out.println(""); System.out.println(a.retainAll(b));//在集合 a 中保留集合b中共有的元素,如果发生变化,则返回true。 System.out.println(a); System.out.println(a.size());//判断 集合a 中包含元素的个数。 // a.clear();//清空一个集合中的所有元素。 System.out.println(a); System.out.println(a.isEmpty());//判断集合中元素个数是否为空,返回boolean类型结果值。 System.out.println(a.equals(b));//判断两个集合中元素是否一致,返回boolean类型结果值。 Object[] objs =a.toArray();//将集合转为Object类型数组。 String[] sobject = a.toArray(new String[a.size()]);//将集合转为指定类型数组 } }
List接口
List继承了Collection接口,并且有三个实现类 ArrayList , LinkedList , Vector .
List 共有的特点: 有序(按照添加的顺序排序),可以有重复的元素。
ArrayList 底层是通过数组实现的,是可以变长的。
查询快,中间增删慢。(后面的元素位置要发生改变)
LinkedList 底层是链表实现(双向链表)
查询慢(必须从头/尾开始查找,直到找到),中间增删快(只需要改变后继节点的位置)
Vector 数组列表,添加了同步锁,线程安全。
ArrayList
这里,我们重点讨论 add() 的底层实现逻辑。
public class ArrayList_Demo1 {
public static void main(String[] args) {
ArrayList<String> a =new ArrayList<>();
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
a.add("a");
/*
add();向集合中添加元素时,底层会默认创建一个长度为10的Object类型数组。
而当数组装满时,再次添加元素,会创建一个是 原来数组长度 1.5倍 的新数组。并将原数组赋值到新数组中。
最后将新数组地址赋给底层的数组。
*/
/*
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
if (minCapacity - elementData.length > 0)
grow(minCapacity); 底层扩容的方法。
*/
}
}
public class ArrayList_Demo1 { public static void main(String[] args) { ArrayList<String> a =new ArrayList<>(); a.add("a"); a.add("a"); a.add("a"); a.add("C"); a.add("a"); a.add("a"); a.add("D"); a.add("A"); a.add("a"); a.add("B"); a.add("a"); System.out.println(a); a.add(0,"x");//向指定位置下标处添加新元素 System.out.println(a); System.out.println(a.get(0));//得到集合中指定位置上的元素 System.out.println(a.remove("A"));//删除集合中指定元素 System.out.println(a.remove(0)); System.out.println(a); System.out.println(a.set(0,"R"));//向集合中指定位置的元素内容进行替换 System.out.println(a); } }
LinkedList
这里,我们重点讨论 get() 的底层逻辑
public class LinkedList_Demo1 {
public static void main(String[] args) {
LinkedList<String> a =new LinkedList<>();
a.add("A");
a.add("B");
a.add("C");
a.add("D");
a.add("E");
System.out.println(a.get(4));
a.addFirst("F");
a.addLast("L");
System.out.println(a);
System.out.println(a.removeFirst());
System.out.println(a.removeLast());
System.out.println(a);
/*
关于查找:如果要找的元素位置小于size/2,从头结点开始,由前向后查找,
如果要找的元素位置大于size/2,从尾节点开始,从后向前查找。
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
*/
}
}
Vector
底层也是数组实现的,但是是 线程安全的。