如何选择?
1、容器类和Array的区别、择取
* 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。
* 一旦将对象置入容器内,便损失了该对象的型别信息。
2、
* 在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList();
Vector总是比ArrayList慢,所以要尽量避免使用。
* 在各种Sets中,HashSet通常优于TreeSet(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。
TreeSet存在的唯一理由:能够维护其内元素的排序状态。
* 在各种Maps中
HashMap用于快速查找。
* 当元素个数固定,用Array,因为Array效率是最高的。
结论:最常用的是ArrayList,HashSet,HashMap,Array。而且,我们也会发现一个规律,用TreeXXX都是排序的。
注意:
1、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
2、Set和Collection拥有一模一样的接口。
3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get)
4、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。
5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。
HashMap会利用对象的hashCode来快速找到key。
* hashing
哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。
我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。
发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。
6、Map中元素,可以将key序列、value序列单独抽取出来。
使用keySet()抽取key序列,将map中的所有keys生成一个Set。
使用values()抽取value序列,将map中的所有values生成一个Collection。
为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。
Java集合就是一个容器。面向对象语言对事物的体现都是以对象的形式存在,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。如果往集合里存放基本数据类型,在存取过程中会有个自动装箱和拆箱。
因为容器中数据结构不同,容器有很多种。不断地将共性功能向上抽取,形成了集合体系,称之为集合框架。
集合框架的顶层就称之为Collection接口。所有的集合类都位于java.util包下,查阅API可以得到如下体系结构。在使用一个体系时,原则:参阅顶层内容。建立底层对象。
集合和数组的区别:
1:数组是固定长度的;集合可变长度的。
2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
Collection<E>接口
Collection:单列集合
|--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引,允许重复元素。
|--Set:无序(存入和取出顺序有可能不一致),不允许重复元素,必须保证元素的唯一性。
java.util.Collection接口中的共性方法有:
1.添加:
boolean add(Object obj):一次添加一个。
boolean addAll(Collection c):将指定容器中的所有元素添加。
2.删除:
void clear():将集合中的元素全删除,清空集合。
boolean remove(Object o):删除集合中指定的对象。注意:删除成功,集合的长度会改变。
boolean removeAll(Collection c):删除部分元素。部分元素和传入Collection一致。
3.取交集:
boolean retainAll(Collection c):对当前集合中保留和指定集合中的相同的元素。
如果两个集合元素相同,返回false;如果retainAll修改了当前集合,返回true。
4.获取长度:
int size():集合中有几个元素。
5.判断:
boolean isEmpty():集合中是否有元素。
boolean contains(Object o):集合中是否包含指定元素。
boolean containsAll(Collection c)集合中是否包含指定的多个元素。
6.将集合转成数组。
toArray()
toArray([])
package ustc.lichunchun.collection.demo;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
methodDemo(coll);
System.out.println("------------------");
methodAllDemo();
}
/*
* 演示Collection中的基本功能。
*/
public static void methodDemo(Collection coll){
//1.添加元素。
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
//2.删除
coll.remove("abc2");//移除和添加元素 --> 会改变集合的长度 --> 集合里面实际上存的是对象们的引用
//3.清除。
coll.clear();
//4.判断包含。
System.out.println("contains: "+coll.contains("abc1"));//底层实现判断用的是equals()
System.out.println(coll);
}
/*
* 演示带All的方法。
*/
public static void methodAllDemo(){
//1.创建两个容器。
Collection c1 = new ArrayList();
Collection c2 = new ArrayList();
//2.添加元素。
c1.add("abc1");
c1.add("abc2");
c1.add("abc3");
c1.add("abc4");
c2.add("abc2");
c2.add("abc3");
c2.add("abc5");
//往c1中添加c2。
c1.addAll(c2);
//判断c1中是否包含c2中的所有元素。
boolean b = c1.containsAll(c2);
System.out.println("b = "+b);
//从c1中删除c2。将c1中和c2相同的元素从c1中删除。
c1.removeAll(c2);
//将c1中和c2不同的元素从c1中删除。保留c1中和c2相同的元素。
c1.retainAll(c2);
System.out.println(c1);
}
}
Collection 接口中明明没有toString()声明,怎么可能有权利调用这个ArrayList类的"特有"方法? (虽然ArrayList类继承它父类有toString()复写的方法了,但这个是ArrayList子类特有的方法啊,不符合多态的解释呀?)
楼主懂得思考,先表扬一下。下面将引用一段接口的说明,你可以看看:
9.2 Interface Members
The members of an interface are:Those members declared in the interface.
Those members inherited from direct superinterfaces.
If an interface has no direct superinterfaces, then the interface implicitly declares a public abstract member method m with signature s, return type r, and throws clause t corresponding to each public instance method m with signature s, return type r, and throws clause t declared in Object, unless a method with the same signature, same return type, and a compatible throws clause is explicitly declared by the interface. It is a compile-time error if the interface explicitly declares such a method m in the case where m is declared to be final in Object.
大致意思如下:
9.2 接口方法
一个接口中的方法有:
1).直接声明在接口中的成员方法;
2).直接从父类接口中继承而来的方法;
3).如果一个接口没有直接的父类接口(也就是其自身就是顶层接口),并且在其没有显示声明相关方法时,那该接口则会根据Object中所有的public的实例方法进行一一映射(比如toString,Hashcode等)。当然如果此接口显示去声明一个与Object签名相同并且带有final修饰的方法时,则会有编译期错误。
所以:由超类声明,子类来new。调用的最终是子类中定义的方法,如果子类没有,则调用子类的父类方法。这存在一种向上追溯的过程。说明是完全正确的。
根据这一条说明,在List list=new ArrayList()之后,在list当中将可以调用object当中所有声明public的方法,而调用的方法实体是来自ArrayList的。而之所以没有list不可以调用,clone()与finalize()方法,只是因为它们是protected的。
学习了。
Iterator<E>接口
java.util.Iterator接口是一个对 collection 进行迭代的迭代器,作用是取出集合中的元素。
Iterator iterator():获取集合中元素上迭代功能的迭代器对象。
迭代:取出元素的一种方式。有没有啊?有!取一个。还有没有啊?有!取一个。还有没有啊?没有。算了。
迭代器:具备着迭代功能的对象。迭代器对象不需要new。直接通过 iterator()方法获取即可。
迭代器是取出Collection集合中元素的公共方法。
每一个集合都有自己的数据结构,都有特定的取出自己内部元素的方式。为了便于操作所有的容器,取出元素,将容器内部的取出方式按照一个统一的规则向外提供,这个规则就是Iterator接口。
也就说,只要通过该接口就可以取出Collection集合中的元素,至于每一个具体的容器依据自己的数据结构,如何实现的具体取出细节,这个不用关心,这样就降低了取出元素和具体集合的耦合性。
Iterator it = coll.iterator();//获取容器中的迭代器对象,至于这个对象是是什么不重要。这对象肯定符合一个规则Iterator接口。
package ustc.lichunchun.collection.demo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
//1.创建集合。
Collection coll = new ArrayList();
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
//方式一:获取该容器的迭代器。
Iterator it = coll.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//方式二:直接for+alt+/,选择第三个。
for (Iterator it = coll.iterator(); it.hasNext();) {
System.out.println(it.next());
}
System.out.println(it.next());//abc1
System.out.println(it.next());//abc2
System.out.println(it.next());//abc3
System.out.println(it.next());//java.util.NoSuchElementException
}
}
为了降低容器的数据结构和取出容器元素的方法之间的耦合性,把访问、取出容器元素的容器的内部类进行共性抽取,即各种容器的相应内部类都实现了Iterator接口,实现了hasNext()、next()、remove()方法。例如如下截取自ArrayList类的iterator()方法的底层实现代码:
public Iterator<E> iterator() {
return new Itr();//取出ArrayList容器中元素的迭代器功能,返回的是一个Itr()迭代器对象,也就是实现Iterator接口的内部类对象。
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {//-->ArrayList容器的内部类,实现了Iterator迭代接口(迭代器),里面有hasNext()、next()、remove()方法。
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
List<E>接口
List本身是Collection接口的子接口,具备了Collection的所有方法。List集合的具体子类:子类之所以区分是因为内部的数据结构(存储数据的方式)不同。
List:有序(元素存入集合顺序和取出一致),元素都有索引,允许重复元素-->自定义元素类型都要复写equals方法。
|--Vector:底层的数据结构是数组。数组是可变长度的。线程同步的。增删和查询都巨慢!
|--ArrayList:底层的也是数组结构,也是长度可变的。线程不同步的,替代了Vector。增删速度不快。查询速度很快。(因为在内存中是连续空间)
|--LinkedList:底层的数据结构是链表,线程不同步的。增删速度很快。查询速度较慢。(因为在内存中需要一个个查询、判断地址来寻找下一元素)
可变长度数组的原理:
不断new新数组并将原数组元素复制到新数组。即当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。
ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。
Vector:是按照原数组的100%延长。
首先学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引(角标),这是该集合最大的特点。也就是说,List的特有方法都是围绕索引(角标)定义的。
List集合支持对元素的增、删、改、查。
1.添加(增):
add(index, element):在指定的索引位插入元素。
addAll(index, collection):在指定的索引位插入一堆元素。
2.删除(删):
remove(index):删除指定索引位的元素。 返回被删的元素。
3.获取(查):
element get(index):通过索引获取指定元素。
int indexOf(element):获取指定元素第一次出现的索引位,如果该元素不存在返回—1;所以,通过—1,可以判断一个元素是否存在。
int lastIndexOf(element) :反向索引指定元素的位置。
List subList(start,end) :获取子列表。
4.修改(改):
element set(index, newElement):对指定索引位进行元素的修改。
下面的代码演示了List的特有方法:
package ustc.lichunchun.list.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList();
methodDemo(list);
}
/*
* 演示List特有的方法。
*/
public static void methodDemo(List list){
//1.常规添加元素。
list.add("abc1");
list.add("abc2");
list.add("abc3");
//2.插入元素。
list.add(1,"hehe");
//3.删除。
list.remove(1);
list.remove(1);
//4.获取。
System.out.println(list.get(3));// java.lang.IndexOutOfBoundsException
System.out.println(list.get(1));
System.out.println(list.indexOf("abc3"));
//5.修改。
list.set(1,"keke");
System.out.println(list);
//6.取出集合中所有的元素。
for (Iterator it = list.iterator(); it.hasNext();) {
System.out.println("iterator: "+it.next());
}
//7.List集合特有的取出方式。遍历。
for (int i = 0; i < list.size(); i++) {
System.out.println("get: "+list.get(i));
}
}
}
什么时候使用Map集合呢?
当需求中出现映射关系时,应该最先想到map集合。
package ustc.lichunchun.map;
import java.util.HashMap;
import java.util.Map;
import ustc.lichunchun.exception.NoWeekException;
public class MapTest {
public static void main(String[] args) {
/*
* 什么时候使用map集合呢?
* 当需求中出现映射关系时,应该最先想到map集合。
*/
String cnWeek = getCnWeek(3);
System.out.println(cnWeek);
String enWeek = getEnWeek(cnWeek);
System.out.println(enWeek);
}
/*
* 根据中文的星期,获取对应的英文星期。
* 中文与英文相对应,可以建立表,没有有序的编号,只能通过map集合。
*/
public static String getEnWeek(String cnWeek){
//创建一个表。
Map<String,String> map = new HashMap<String, String>();
map.put("星期一","Monday");
map.put("星期二","Tuesday");
map.put("星期三","Wednesday");
map.put("星期四","Thursday");
map.put("星期五","Friday");
map.put("星期六","Saturday");
map.put("星期日","Sunday");
return map.get(cnWeek);
}
/*
* 根据用户指定的数据获取对应的星期。
*/
public static String getCnWeek(int num){
if (num>7 || num<=0)
throw new NotWeekException(num+", 没有对应的星期");
String[] cnWeeks = {"","星期一","星期二","星期三","星期四","星期五","星期六","星期日"};
return cnWeeks[num];
}
}
package ustc.lichunchun.exception;
public class NotWeekException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public NotWeekException() {
super();
}
public NotWeekException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public NotWeekException(String message, Throwable cause) {
super(message, cause);
}
public NotWeekException(String message) {
super(message);
}
public NotWeekException(Throwable cause) {
super(cause);
}
}