集合
和数组一样,对数据进行操作的结构简称容器,这里的存储是内存层面的,存储的数据无序,不可重复。
集合分为两类:Collection和Map
Collection接口:单列数据
List接口:元素有序可重复的集合(动态数组)
实现类有ArrayList,LinkedList,Vector
Set接口:元素无序不可重复的集合
实现类有HashSet、LinkedHashSet、TreeSet
Map:双列数据,具有映射关系,数据成对存在
实现类:HashMap、LinkedHashMap、TreeMAp、Hashtable、Properties
常用方法 List
Collection coll = new ArrayList();
Collection coll2 = new ArrayList();
coll.add("abc");
coll.add('f');
coll.add(123);
coll.add(true);
System.out.println(coll);//[abc, f, 123, true]
System.out.println(coll.size());//4
coll.add(new String("hello"));
coll.add(new Person(20));
System.out.println(coll.contains(new String("hello")));//比较的是内容
System.out.println(coll.contains(new Person(20)));// 重写Person的equals方法后 System.out.println(coll.isEmpty());//false
coll2.addAll(coll);
System.out.println(coll2); //[abc, f, 123, true]
coll.clear();
System.out.println(coll.isEmpty());//true
向Collection接口的实现类对象总添加obj数据时,要求重写obj所在类地equals()方法。
//调用remove方法会调用equals(),因为需要找到这个对象
coll.remove(new Person(20));
removeAll(coll);//移除coll中的所有元素
coll.retainAll(coll2);//获取两个集合的交集,返回给当前集合
//创建集合的方法,类型转换
Collection coll3 = Arrays.asList(123,345,756);
List<Integer> list = Arrays.asList(123, 345, 756);
Object[] array = list.toArray();
Iterator接口 迭代器
所有继承了该接口的类都有一个iterator()方法,该方法返回一个Iterator接口对象。
Iterator仅用于集合遍历,其本身不提供承装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的对象。
集合每次调用Iterator()都会得到一个新的迭代器,迭代器默认索引在第一个元素之前
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());//指针移动,将移动后所指的元素返回
}
迭代器中也有一个remove()方法,可删除集合中的元素,注意,在未使用next()之前不可使用该方法,不可连续使用两次该方法
// 增强for内部调用的还是迭代器
for (Object o : coll) {
System.out.println(o);
}
常用方法:
增:add(Object o)
删:remove(int index)/remove(Object o)
改:set(int index,Object o)
查:get(int index)
插:add(int index,Object o)
长度:size()
遍历:iterator迭代器
增强for
普通for
Set 接口
该接口没有定义新的方法,都是Collection中的
HashSet:主要实现类,线程不安全,可存储null值
LinkedHashSet:HashSet的子类,可按照元素添加的顺序进行排序,对于频繁的遍历操作,该方法效率较高
TreeSet:可按照添加对象的指定属性进行排序
Set接口的无序性:与List的有序性相对,二者都是用数组存储,List是按照从前往后的顺序进行存储,Set是按照元素的HAshCode的值存放在数组中,所以后来存放的元素可能在先存放的元素的前面,其遍历结果也是按照一定的顺序,并非随机性。
Set接口的不可重复性:添加的元素按照equals()方法判断,不能返回true,即相同元素只能保留一份。(重写equals和hashcode方法)
HashSet添加元素过程:
-
添加元素A时首先调用A的HashCode方法,计算出值,再使用特定算法计算出该值在底层数组中的位置索引,判断此位置是否有元素
-
若该位置无元素,直接存放
-
若有元素(或以链表的形式存放的多个元素),则比较A与该元素的hash值,若不同,则添加成功
-
若hash值相同进而调用A的equals方法进行比较,结果为true,添加失败,反之添加成功。
在jdk7中, 元素A放到数组中,指向原来的元素
jdk8中,原来的元素不变,A链接到元素下面,即数组中的元素指向A
结论:HashSet底层是数组加链表
向Set中添加的数据,一定要重写equals和hashcdoe方法,且二者要尽可能的保持一致性,相等的对象要有相同的hash值(散列码)
同一对象多次调用hashcode的返回值应该相等;
两个对象的equals返回true时,他们的hashcode值也应相等;
对象中用作比较的Filed(属性值),都应用来计算hash值
TreeSet
不能添加类型不同的对象
排序的两种方法:自然排序和自定义排序
自然排序中的标准为compareto()返回0,不再是equals()
二级排序
@Override
public int compareTo(Object o) {
if (o instanceof Person){
Person p = (Person)o;
int i = p.name.compareTo(this.name);
if (i!=0){
return i;
}else {
return Integer.compare(this.age,p.age);
}
}
其中的addAll(LIst list)会自动去掉重复的项,注意重写自定义类的equals()和hashcode()
hashset的注意点
public void test2(){
HashSet set = new HashSet();
Person p1 = new Person("AA",3);
Person p2 = new Person("BB",4);
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);//集合在删除时,先要找到值,这里是按照新的p1去算的hash值,所以找到的不是原p1的值,因此此处的删除无效,但原p1的name以被更改
System.out.println(set);//[Person{name='CC', age=3}, Person{name='BB', age=4}]
set.add(new Person("CC",3));//添加的新元素按照新元素去计算hash值,添加成功,因为之前没有同样的值
System.out.println(set);//[Person{name='CC', age=3}, Person{name='BB', age=4}, Person{name='CC', age=3}]
set.add(new Person("AA",3));// 也能添加成功,因为之前p1的值也被修改,会认为是两个不同的值,只是hash地址相同而已
System.out.println(set);//[Person{name='CC', age=3}, Person{name='BB', age=4}, Person{name='AA', age=3}, Person{name='CC', age=3}]
}
Map
主要实现类是HashMap,线程不安全,效率高,可存储null的key和value。jdk8后存储结构是:数组+链表+红黑树
Map map = new HashMap();
map.put(null,null);
LinkedHashMap:可按照添加顺序遍历元素,有指向前后的指针
TreeMap:按照添加的key进行排序,底层使用红黑树
HashTable是较早的实现类,线程安全,效率低,不能存储null的key和value
Properties:常用来处理配置文件,key和value都是String
map结构:
key:无序不可重复,使用set存储,key所在类要重写equals和hashcode方法。
value:无序可重复,使用Collection存储,value所在类要重写equals。
key-value:构成一个Entry对象,无序不可重复,使用set存储
HashMAp底层实现原理(jdk7)
HashMap map1 = new HashMap();//创建的是长度为16的一维数组,Entry[] table
map1.put(key,value);//调用key所在类的hashcode计算hash值,该值在经过计算得到在数组中的存储位置,如果该位置为空,则存储成功(以链表形式),若不空,则比较该位置上数据的哈希值,
//如果哈希值不等,则添加成功,反之,调用key所在类的equals()方法比较,若不同,则添加,反之不添加
// 默认扩容为原来的2倍
JDK8中的原理
在创建时没有创建长度为16的数组,8中的数组是Node[]
在首次调用Put()时,创建长度为16的数组
jdk7中结构是数组+链表
jdk8中是数组+链表+红黑树,当一个索引上的元素以链表的形式存在的个数超过8且当前数组长度大于64时,则该索引的位置的所有数据改为红黑树结构存储
map.clear();
Collections
可操作Map,Set,List
常用方法有:
注意copy方法
ArrayList list = new ArrayList();
list.add(123);
list.add(123);
list.add(123);
list.add(123);
list.add(123);
// 正确的copy方法写法
List arrayList = Arrays.asList(new Object[list.size()]);
Collections.copy(arrayList,list);
System.out.println(arrayList);
//可返回线程安全的对象
Collections.synchronizedCollection();
Collections.synchronizedList();