集合
集合知识点
10.1 List接口
10.2 Map接口
10.3 Set接口
10.4 泛型
10.5 Iterator接口
数组可以存储多个数据,但数组是定长的,不会自动扩充。如果想要对定长的数据操作,可以使用数组。如果想要对不定长的数据操作,可以使用集合。
合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是一定的。
List:底层数据结构为双向链表,支持快速增删。
Vector:底层数据结构为数组,支持快速随机访问。
Map:底层数据结构为红黑树,除了hashmap无序,其他实现结构有序,不重复。
Set:底层数据结构为红黑树,除了hashset无序,其他结构有序,不重复。
Java中数组与集合的比较:
数组也是容器,它是定长的,访问较快,但是数组不会自动扩充。
数组可以包含基本数据类型或引用类型的对象,而集合中只能包含引用类型的对象。
10.1List接口
|-> ArrayList:底层使用数组和列表实现,允许包括null在内的所有元素
| (连续存储,查询性能高,有序存储,多线程不安全)
|
List---------->Vector:功能与ArrayList相同,是多线程安全的,保存有序数据的接口,可以保存相同数据。
|
|
|->LinkedList:底层的数据结构是基于双向链表,允许包括null在内的所有元素。
List接口是Collection的子接口,List接口用于存放有序数据,集合中的元素可以重复,也可以把同一个对象保存到集合中多次,即对象可重复。
可以通过循环遍历或get方法,得到集合中的元素,再修改元素的属性值(set)方法。元素不可以修改,元素内部的属性值可以修改。
例1:把同一对象保存到list中。
List list = new ArrayList();
Computer computer = new Computer("HP",5000);
Computer computer2 = new Computer("HW",5500);
list.add(computer);
list.add(computer);
list.add(computer2);
System.out.println(list.size());
结果为:3。证明list中已经添加了三次数据。
ArrayList和LinkedList:
List有两种主要的集合实现类:
ArrayList,以数组的形式保存集合中的元素,元素之间内存地址是连续的。ArrayList数据插入或删除时都需要移动数据,效率低;但查询时不需要寻址,可以快速查找。
LinkedList,以链表结构保存集合中的元素,元素之间内存地址不连续。LinkedList数据插入或删除不需要移动数据,具有较高的修改性能;查询数据需要进行寻址操作。
ArrayList的构造方法:List 变量名 = new ArrayList();
LinkedList的构造方法:List 变量名 = new LinkedList();
ArrayList与LinkedList的比较:
存储结构:ArrayList是线性顺序存储,LinkedList对象间彼此串连起来的一个链表。
操作性能:ArrayList适合随机查询的场合,LinkedList元素的插入和删除操作性高。
功能:LinkedList要多一些。
例1:新建ArrayList实现类,并添加数据。
public class ListDemo {
public static void main(String[] args) {
List li = new ArrayList();
li.add("one");
li.add("two");
li.add(3);
li.add(new Float(4.0F));
li.add(new Integer(5));
System.out.print(li+" ");
}
}
例2:新建LinkedList实现类,练习基本操作。
public class LinkedListDemo {
public static void main(String[] args) {
List li = new LinkedList();
for(int i=0;i<=5;i++){
li.add("a"+i);
}
//在li的索引为的位置添加数据
li.add(3,"a100");
/**
* LinkedList is a raw type. References to generic
* type LinkedList<E> should be parameterized.
* LinkedList是一种原始类型。泛型类型LinkedList<E>的引用应该参数化
*/
((LinkedList) li).addFirst("a00");
//移除里中索引0的元素
li.remove(0);
System.out.println("指定index="+li.get(4)+",firstindex="+((LinkedList) li).getFirst()
+",lastindex="+((LinkedList)li).getLast());
System.out.println(li);
}
}
Stack栈结构:
push:压栈,存数据;
peek:取栈顶数据不删除
remove:移除数据
pop:去除栈顶数据并删除
10.2Map接口
|->HashMap:(key-value)底层使用数组实现,基于哈希表的Map接口的非同步实现,允许使用null值和null键,但不保证映射的顺序。在JDK8以后,HashMap的数组结构是数组+链表+红黑树。
|
Map-------->Hashtable:底层使用数组实现,数组中每一项是个单链表:数组和链表的结合体
| 哈希表的Map接口的同步实现,不允许使用null值和null键。
|
|->ConcurrentHashMap:底层使用数组实现,允许多个修改操作并发进行,其关键在于使用了锁分离技术。与HashMap不同的是,ConcurrentHashMap使用多个子Hash表,也就是段(Segment) 。
|
|->LinkedHashMap:继承于HashMap,底层使用哈希表和双向链表来保存所有元素,并且它是非同步,允许使用null值和null键。
Map集合用于保存有映射关系的数组,存储数据时是通过key value的对应关系进行保存的。但不能有null key,且不能有相同的key,如果存在相同的key,后面的数据会覆盖前面的数据。Map接口有两个实现类:HashMap和TreeMap。
HashMap和Hashtable的区别:
HashMap允许null为key和value。Value值可以重复,key值不可以重复,线程不安全。
Hashtable不允许null为key和value,value值可以重复,key不能重复,线性安全。
HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中可以使用其它数据结构或加锁来规避此风险。
例:Map接口常用方法
public class MapTest {
public static void main(String[] args) {
Map m1 = new HashMap();
Map m2 = new TreeMap();
m1.put("one",new Integer(1));
m1.put("two",new Integer(2));
m2.put("A",new Integer(1));
m2.put("B",new Integer(2));
//int size()返回映射中键值映射对数
System.out.println("m1size="+m1.size());
//boolean containsKey(Object key)若此映射包含指定键的映射关系,返回true
System.out.println("onekey="+m1.containsKey("one"));
//boolean containsValue(Object value)
//若此映射为指定值映射一个或多个键,返回true
System.out.println("1value="+m2.containsValue(new Integer(1)));
if(m1.containsKey("two")){
//get(Object key)返回映射到指定键的值
int i = ((Integer)m1.get("two")).intValue();
System.out.println("i="+i);
}
//Map 变量名 = new HashMap(Map m)
//构造一个映射关系与指定的Map相同的新的HashMap
Map m3 = new HashMap(m1);
//将映射m2中所有映射关系赋值到m3映射中
m3.putAll(m2);
System.out.println("m3="+m3);
}
}
10.3Set接口
|->HashSet:HashSet由哈希表(实际上是一个HashMap实例)支持,不保证set的迭代顺序,并允许使用null元素。(唯一无序性,保存数据时性能非常高,取数据会慢一些)。
|
Set------->TreeSet:底层使用数组实现,自动升序排序(唯一性)。
|
|->LinkedHashSet:LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同。
Set接口是Collection的子接口,用来包含一组无序无重复的对象。无序:是指元素加入集合中的顺序和集合内存的顺序不同;无重复:是两个对象e1和e2,如果e1.equals(e2)返回true,则认为e1和e2重复,在set中只保留一个。
利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。
Set会调用equals方法进行判断,默认判断两个对象的引用是否相同。
Set接口的实现类:
HashSet:在遍历集合中元素时,不关系元素的顺序;
TreeSet:按照升序排列遍历结合中的元素。
示例:
public class HashSetTest {
public static void main(String[] args) {
Set a = new HashSet();
a.add("hello");
a.add("world");
a.add(new Integer(100));
//相同元素不会被加入
a.add("hello");
System.out.println(a);
}
}
结果为:[world, 100, hello]
10.4泛型
泛型,又称参数化类型,能像方法一样接受不同类型的参数。在大部分情况下,一个集合只用于保存一类数据。为了避免在获取数据时进行类型检查,可以使用泛型集合,在保存数据时进行类型检查。
使用泛型集合语法,在定义集合对象时,进行类型的声明。
例:List list = new ArrayList();
在jdk1.7后,可以前面声明变量,后面为空。
例:List list = new ArrayList<>();
使用泛型集合后,在获取数据时不需要类型转换,可以保障数据不会发生数据类型转换异常。
例:定义计算机类型的名称和价格。
public class GenericCollection {
public static void main(String[] args) {
List<Computer> list = new ArrayList<>();
list.add(new Computer("HP",6000));
list.add(new Computer("HW",6500));
list.add(new Computer("DELL",5000));
for(Computer computer:list){
System.out.println(computer);
}
Computer com = list.get(0);
Iterator<Computer> iterator = list.iterator();
while(iterator.hasNext()){
Computer com2 = iterator.next();
}
}
}
使用泛型的好处:
1.类型安全
泛型的主要目标是提高java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在更高的程度上验证类型假设。
2.消除强制类型转换
泛型的一个附带好处是,消除源代码中的许多强制类型转换。提高代码可读性,减少出错率。
3.潜在的性能收益
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。更多类型信息用于编译器,为未来版本的JVM的优化带来可能。其中,由于泛型的实现方式,支持泛型不需要JVM或类文件更改。
10.5Iterator接口
Iterator对象称为迭代器,用于对集合内的元素进行遍历操作。所有Collection接口的集合类都有一个iterator()方法,iterator在使用中只能单向移动。
Iterator常用方法:
方法 | 含义 |
---|---|
Object next() | 返回游标右边的元素并将游标移动到下一个位置 |
Boolean hasNext() | 判断游标右边是否有元素 |
Void remove() | 删除游标左边的元素,在执行完next后,该操作只能执行一次 |
实例:
public class IteratorDemo {
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<Integer>();
for(int i=1;i<6;i++){
set.add(new Integer(i));
}
//获得游标
Iterator<Integer> it = set.iterator();
//hasNext()判断右边右边是否有元素
while(it.hasNext()){
//next()返回游标游标的元素,并将游标移动到下一个位置
Integer j = (Integer)it.next();
System.out.print(j+" ");
//it.remove();
}
System.out.println();
System.out.println(set);
}
}
结果为:
1 2 3 4 5
[1, 2, 3, 4, 5]
如果在next()后运行,it.remove();结果为:
1 2 3 4 5
[]