(一)集合(又称为容器)概念(位于java.util包)
何为集合,集合就是相当于一个对象的容器。集合是类似数组的一个作用。既然有了数组,为何还要有集合呢,由于数组对象一旦创建,其大小便不可以更改,我们只能往数组中存放创建时数量的对象。而集合中的长度却是动态可变化的,我们可以往集合中添加任意数量的对象。并且我们可以通过集合更方便地实现数据结构如队列,栈等等。在集合中我们更可以定义自己想要的操作。
注意:对于线程同步,推荐使用java.util.concurrent包下的线程安全的类
ConcurrentHashMap、CopyOnwriteArrayList等
其中Arrays类和Collections类提供了对数组和集合的搜索和排序算法。
实现Comparable接口的对象列表(和数组)可以通过 Collections.sort
(和 Arrays.sort
)进行自动排序。
1、集合类型
集合的类型一共有三种,分别是:set list map
第一,set:数据对象没有顺序且不可以重复
第二,list:数据对象有顺序且可以重复。在数据结构中表现为数组、向量、链表、堆栈和队列。
第三,map:每个元素由key-value构成。key是唯一的。
2、存储结构分类
第一,顺序存储:指元素在内存中连续的存储在一起,根据第一个元素的地址和每个元素所占的字节很容易计算其他任意位置的元素的地址,进而可以访问钙元素,如数组。这种存储方式优点:利于元素访问。缺点是:增加和删除元素的性能不高。如ArrayList。
第二,链式存储:元素一般由值data和next域构成,元素在内存中不需要连续的空间,通过next即可获得下一个元素地址。优缺点与第一种相反。如LinkList是双向链表。
第三,散列存储:元素值(具有唯一性)通过一种散列技术决定了对象在内存中的存储位置。如HashSet。
第四,映射存储:每个元素由key-value构成,根据key以及相应的散列算法计算元素存储地址。
(二)Collection接口(最基本的集合接口,类似于Object)
Collection接口:定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
注意:定义成接口就是定义标准,规定实现该接口的集合必须对外提供接口中规定的方法,具体如何实现不关注。
1、Collection接口中定义的方法
自己可以考虑一下该有哪些方法?从最基本的增删查改四个方法出发
int size()方法 boolean add(Object element) boolean contains(Object element) boolean remove(Object element)
Object[] toArray()方法:返回包含此 collection 中所有元素的数组。
Iterator iterator()方法:返回在此 collection 的元素上进行迭代的迭代器。
2、集合类对象在调用remove(),contains()等方法时,需要比较对象是否相等,这会涉及到对象类型的equals()方法和hashcode()方法;
对于自定义类,需要重写equals()方法和hashcode()方法以实现自定义的对象相等规则。(重点)
(三)Iterator接口(应用了多态)
Iterator就是 一个统一的用来遍历Collection里所有元素的方法。
所有实现了Collection接口的集合类都有一个iterator()方法用以返回一个实现了Iterator接口的对象。
Iterator对象称作迭代器,用于方便的实现对集合内元素的遍历操作。
1、Iterator接口中的方法
boolean hasNext()方法:判断游标右边是否还有元素
Object next()方法:返回游标右边的元素并把游标移动到下一个位置
void remove()方法:删除游标左边的元素,每次调用 next 只能调用一次此方法
注意:collection接口也有一个remove()方法,在使用Iterator接口遍历集合元素时,只能使用Iterator接口的remove()方法。(Iterator遍历过程中会锁定数据,线程锁)
ListIterator 继承了Iterator,以允许双向遍历列表和修改元素。
一、增强的for循环
1、增强for循环可以方便的遍历Array和Collection。
语法:for(数据类型 变量名 : 数组或者集合名)
缺陷:
数组:不能方便的访问下标值。例如:不能删除指定元素
集合:与使用Iterator相比,不能方便的删除集合中的内容。(本质:在内部也是调用Iterator),通过加判断条件,实现删除指定元素。
结论:除了简单遍历并读出其中的内容外,不建议使用增强for
实例:
//第一种遍历方法使用foreach遍历List
for (String str : list) { //也可以改写for(int i=0;i<list.size();i++)这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为 foreach(String str:strArray)这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
Set接口是Collection的子接口,Set接口没有提供额外的方法。
set接口的实现类:HashSet,TreeSet(实现了排序功能),
LinkedHashSet(可预知迭代顺序的 Set 接口的哈希表和链接列表实现,按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。)
注意:实现Set接口的集合类中的元素是没有顺序的,并且不能重复,最多允许有一个null值。
(五)List接口(类似于数组,底层就是数组实现的)
List接口是Collection的子接口,实现了List接口的集合类中的元素是有顺序的,并且可以重复。
List接口的实现类:
1、ArrayList(该类不是线程同步的)
2、LinkedList(允许null值,多线程需要自己设置同步方法 List list = Collections.synchronizedList(new LinkedList(...));)
3、vector 是线程同步的,但是属于过时集合,不推荐使用
实例:
- LinkedList queue = new LinkedList();
- Object object = "";
- queue.add(object);
- Object o = queue.removeFirst();
- queue = (LinkedList) Collections.synchronizedList(queue);
1、List接口方法
Object set(int index, Object element)方法:该方法返回的是之前在该位置的元素(即:替换之前的元素)
void add(int index , Object element) 方法:在此列表中指定的位置插入指定的元素。移动当前在该位置处的元素(如果有),所有后续元素都向右移(在其索引中添加 1)。
(六)List常用算法
java.util.Collections类(与接口Collection区分开)提供了一些静态方法实现了基于List集合的一些常用算法。
注意:提供的是静态方法,对于方法的调用,直接使用类名.方法名即可,如:Collections.sort()方法。
void sort(List):对List集合内的元素排序(注意:List集合中的对象必须实现Comparable接口,才能使用sort()方法)
void shuffle(List) :对List容器内的对象进行随机排列
void reverse(List):对List集合内的对象进行逆序排列(注意:相比ArrayList,LinkedList的reverse()方法效率高,原因:只需要将指针调换即可)
void fill(List,Object):用一个特定的对象重写整个List集合
void copy(List dest,List src):将src List容器内容拷贝到dest List容器
int binarySearch(List ,Object):使用该方法需要先对List集合排序,采用二分法查找指定的对象
(七)java.lang.Comparable接口
所有可以排序的类都实现了java.lang.Comparable接口,Comparable接口只有一个方法
public int compareTo(T t);
返回0:表示this == t
返回正数:表示this > t
返回负数:表示this < t
(八)选择数据结构
衡量标准:读的效率和改的效率
1、Array:读快改慢
2、Linked:改快读慢
3、Hash:二者之间
注意:以前的集合类:HashTable,Vector等,内部是锁定的,读写效率很低。不推荐使用。
(九)Map接口(重点)
实现Map接口的类用来存储键——值对,其中键值不能重复。使用equals()方法比较两个键值是否重复,效率低,不如直接使用hashcode来区分效率高。因此重写equals()方法,必须重写hashcode()方法。
Map接口的实现类有:HashMap和TreeMap
1、HashMap类(不支持线程同步)
根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
2、TreeMap类(不支持线程同步)
基于红黑树(Red-Black tree)的 NavigableMap 实现。红黑树(Red Black Tree) 是一种自平衡二叉查找树(一个元素下只有两个子元素)。
3、Hashtable类(支持线程同步)
3、Map接口的方法
Object put(Object key ,Object value)方法:替换指定键值映射的值,方法返回原来的值,即替换之前映射的值。
注意:方法两边都是对象。
Set entrySet()方法:返回存放Map类型的Set集合。这样就可以使用Iterator()方法了。
Set keySet()方法:返回存放key值的Set集合。
Collection values()方法:返回存放映射值的Collection集合
实例:
- Map<String,String> map=new HashMap<String,String>();
- 11. map.put("1", "张三");
- 12. map.put("2", "李四");
- 13. map.put("3", "王五");
- 14. /*方法一 :迭代程序*/
- 15. System.out.println("方法一:");
- 16. Iterator iterator=map.entrySet().iterator(); //返回一个迭代器对象实现遍历
- 17. while(iterator.hasNext()){
- 18. Map.Entry<String, String> entry= (Entry<String, String>) iterator.next();
- 19. System.out.println("key:"+entry.getKey()+" value"+entry.getValue());
- 20. }
- 21. /*方法二*/
- 22. System.out.println("方法二:");
- 23. for (Map.Entry<String, String> m : map.entrySet()) {
- 24. System.out.println("key:"+m.getKey()+" value"+m.getValue());
- 25. }
- 26. }
(十)、Map.Entry<K,V>:映射项(键—值对)
获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。即调用map.entrySet();方法。
Map.Entry是Map的一个内部接口。entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。
1、Map.Entry接口的方法
getKey()方法: 返回与此项对应的键。
getValue()方法: 返回与此项对应的值。
setValue(V v)方法:用指定的值替换与此项对应的值。返回此项对应的旧值。
实例:
Set entries = map.entrySet( );
if(entries != null) {
Iterator iterator = entries.iterator( );
while(iterator.hasNext( )) {
Map.Entry entry =iterator.next( );
Object key =entry.getKey( );
Object value =entry.getValue();
}
(十一)泛型(Generic)
问题:装入集合的类型都被当做Object对待,从而失去自己的实际类型
从集合中取出时往往需要转型,效率低,容易产生错误。
泛型是jdk5中引入,提供了编译时类型安全检测机制。泛型的本质:把所操作的数据类型看作一个参数。
泛型声明有尖括号分隔,参数一般用大写字母,格式:<T>
1、泛型方法
泛型方法的定义就是将泛型参数列表置于返回类型之前即可
- public <T> void f(T x){
- System.out.println(x.getClass().getName());
- }
2、泛型类
泛型类和非泛型类定义类似,只是在类名后面加上泛型参数声明接口。泛型参数声明格式:<T>
public class Box<T> { //在类名后面加上泛型参数声明接口。泛型参数声明格式:<T>
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
泛型接口类似,在接口名字后面加上泛型声明
- public interface Generator<T> {
- public T next();
- }
3、泛型通配符(?)
泛型通配符一般是使用?代替具体的类型参数。
其中要注意:泛型的上下限的设置,设置数据类型的范围。
- <? extends T>表示该通配符所代表的类型是T类型的子类。
- <? super T>表示该通配符所代表的类型是T类型的父类。
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}