集合
集合与数组都是对多个Java对象进行操作的容器
现在的存储都是只涉及内存层面的,不涉及硬盘也就是说没有持久化,不能保存数据。
数组的特点与缺点:
- 数组一旦初始化、其长度就不可更改(缺点)
- 数组的类型在初始化时就确定了,不能存储非其类型的数据(中性特点)
- 数组定义的操作(属性、方法)相当少,尤其对于增加、删除效率很低(缺点)
- 没有提供具体的存放了多少数据的接口(length是长度,不是存储数据的个数)(缺点)
- 数组中数据有序、可重复,对于需要无序或要求不能重复的需求没有对应的实现接口(缺点)
Collection
Collection 单列对象:存储一个一个的数据对象
List:有序的,可重复的数据。类似于数组但可以增加长度(动态数组)
Set:无序的,不可重复的数据
ArrayList():一个Collection接口的实现类(线性表)。
LinkList():链表
Vector():
.add():向集合中添加一个元素。
.size():集合的大小。
.clear():清空集合中的元素。
.isEmpty():判断集合是否为空,为空则返回true、不为空返回false。
@Test
public void test(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add(new Date());
coll.add(567);
coll.add("Yao");
coll.add("MING");
System.out.println(coll.size());
Collection coll1 = new ArrayList();
coll1.add("Li");
coll1.add("Ning");
coll1.addAll(coll);
System.out.println(coll1.size());
System.out.println(coll1.toString());
coll.clear();
System.out.println(coll.isEmpty());
/*
5
7
[Li, Ning, AA, Wed Aug 31 00:01:19 CST 2022, 567, Yao, MING]
true
*/
}
.contains(Object o);判断o在不在该集合中,内层是遍历集合并调用equals()方法判断,若对象中重写过equals()方法(例如String重写过equals方法变成判断内容是否相等),则调用equals()方法,若没有重写过,则调用Object中的equals()方法,就是"=="就变成了比较地址
equals()方法的重写可以直接通过编辑器生成。
public void test(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(false); //bool类型的包装类、Boolean
coll.add(new Date());
coll.add(new String("Yao Ming"));
Person p = new Person("孔子", 564);
coll.add(p);
System.out.println(coll.contains(123));
System.out.println(coll.contains(new String("Yao Ming")));
System.out.println(coll.contains((new Person("孔子", 564))));
/*
true
true
false
*/
}
.containsAll(Collection col);
判断col中的元素是否都在该集合中
.remove(Object o)方法从集合中移除某个元素,会先调用equals()方法判断该元素是否在集合中,所以若有自定义类,则重写equals()方法的环节很重要
.removeAll(Collecton col)方法从集合中移除另一个集合中的所有元素,即另一个集合中有的元素都会在原集合中被移除,这时候也会调用equals()方法。
colT.retainAll(Collection col);计算colT与col的交集并存入colT中
equals()判断对象元素是否相等,注意ArraysList还需要元素的存储顺序也相等
.hashcode()返回对象的哈希值。
.toArray()集合–>数组
Object[] o = col.toArray();
调用Arrays中的静态方法.asList()可以使数组转换为集合
List<String> list = Arrays.asList(new String[]{"AA", "BB"});
,若存基本数据类型,则必须存储对应的包装类
使用Iterator接口实现对集合的遍历
Iterator(迭代器)
public void test(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(false); //bool类型的包装类、Boolean
coll.add(new Date());
coll.add(new String("Yao Ming"));
Person p = new Person("孔子", 564);
coll.add(p);
//创建一个接口
Iterator iterator = coll.iterator();
// System.out.println(iterator.next()); //若超出范围则抛异常
//常用遍历方法
while(iterator.hasNext()){ //hasNest()方法判断该元素后是否还有元素,若还有则返回true,没有元素了就返回false
System.out.println(iterator.next());
}
}
调用.next()方法会使指针下移、hasNext()方法单纯判断一下是否后面还有元素。
必须显示声明Iterator iterator,不能匿名,要不然指针不会下移,每次循环都是新的Iterator迭代器
iterator.remove()迭代器也可以调用remove()方法,用来删除集合中的元素,注意要先.next()获取到元素才可以进行删除。
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
if(o.equals("Yao Ming")){
iterator.remove();
}
}
foreach循环遍历集合元素
增强for循环
Collection coll = new ArrayList();
coll.add(123);
coll.add(false); //bool类型的包装类、Boolean
coll.add(new Date());
coll.add(new String("Yao Ming"));
Person p = new Person("孔子", 564);
coll.add(p);
//内部仍然调用了迭代器
for(Object o : coll){
System.out.println(o);
}
List接口
都存储有序的、可重复的数据。
- ArrayList
顺序表,线程不安全,故高效 - LinkedList
双向链表,利于插入删除 - Vector(不常用)
古老的线性表,线性安全,低效
ArrayList:
初始给10个地址,不够的话进行扩容,默认扩容为原来的1.5倍,并将原先的赋值过来(复制一份加过来),若还不够则直接将需要的大小复制过来,若超过对应元素的数据类型最大值,则抛出异常。
list.add[123]; //element[0] = new Integer(123);
为了避免多次扩容,建议使用带参构造器new ArrayList(int initialCapacity)指定初始化集合的大小。
在JDK7中,ArrayList list = new ArrayList()会创建长度为10的Object[] element;
而在JDK8中,不会在ArrayList list = new ArrayList()时创建数组,而是会在初次调用add方法时才创建,延迟了数组对象的创建时间,减少内存的占用时间。
List常用方法
void add(int index, Object ele);向索引为index位置上插入ele。
boolean addAll(int index, Collection col);向索引为index的位置上插入连续多个元素,这些元素在col中。
Object get(int index);返回index中的元素
int indexOf(Object obj);返回对象obj在集合中首次出现的位置(索引),若不存在则返回-1。
int lastIndexOf(Object obj);返回对象obj在集合中最后一次出现的位置(索引),若不存在则返回-1。
Object remove(int index);按索引删除,也有重载方法按内容删除,返回被删除的内容。
.set(int index, Object obj);将索引为index位置的元素替换为obj。
.subList(int fromIndex, int toIndex); 返回索引位置从fromIndex到toIndex位置上的所有对象的子集合。
Set接口
无序的、不可重复的
- HashSet:Set接口的主要实现类,线程不安全,可以存储null值
- LinkedHashSet:HashSet的子类,假装有序,可以按照添加元素的顺序进行遍历
- TreeSet:底层为红黑树的存储,要求放入TreeSet中的对象必须为相同对象,可以按照添加对象的指定属性进行排序
Set中没有定义新的方法,其方法都是Collection中继承而来(因为其不具有索引)
HashSet
HashSet底层其实也是数组存储,但其中元素不是按数组索引顺序存储的,按照(Hash算法)(散列表)进行存储。
保证添加的元素在添加时不能返回true(调用equals()方法),即相同元素只能添加一次
若要添加元素a,则先调用hashCode()方法计算元素的hash值,再通过hash值决定(散列函数)元素在数组中的存放位置,若hash值相同,则调用equals方法判断是否相等(不用一直调equals()方法,效率高),若再相等,则添加失败,否则添加成功,以链表形式加到那个数组索引位置的后面。(原先的元素在数组中,指向a)
其初始容量为16,底层为数组+链表。
向Set中添加的类必须重写equals方法和hashCode方法,相等的对象要有相同的hash值。
hashSet底层就是实现的hashMap
LinkedHashSet
HashSet的子类,添加数据的同时维护了每个数据的双向指针,但底层还是以HashSet的形式存储在数组中,给每个数据增加了双向链表类似的指针
遍历操作会比HashSet更高
TreeSet
TreeSet是为了排序而提供的表。故:
TreeSet中添加的对象必须是相同类的对象,不能是不同类的对象,否则不能进行比较。
其底层为红黑树。
自然排序:
TreeSet的输出会自动在底层进行排序,其会调用类中的CompareTo()方法,若没有重写CompareTo()方法则会抛出异常,注:在相等时,也需要另外的判断。
定制排序:
调用TreeSet(Comparator com)的带参构造器,需要重写Compare()方法
Map
Map 双列对象:存储一对一对的数据(key - value)
key是无序、不可重复的,使用Set存储,自定义类必须重写equals()和hashCode()方法。
注:hashCode()方法会生成一串数字、有相同内容的数据会有相同的hashCode值,可以便捷的进行插入操作,不同hashCode的元素必然不同,减少equals()的比较。
value是有序的、可重复的,使用Collection存储。要重写equals()方法。
Entry:一个键-值对构成了一个Entry,Entry也是无序的
HashMap(线程不安全、效率高)
Hashtable作为Map的古老实现类,其不能存储null,null会被认为是空指针。
数组+链表+红黑树
HashMap map = new HashMap();
在创建对象之后,底层会存储一个长度为16的Entry[]数组。
- 每次put(key、value)会首先计算key的hashcode值、再通过该hashCode值计算(散列函数)出这个元素的存储位置。
- 若hashCode值对应的位置没有元素、则直接存储。
- 若hashCode值对应的位置存在元素、则先比较hashCode的值、若hashCode值不相等(两元素内容一定不同)则存储、若相等,则调用equals()方法比较、若equals()不相等、则存储成功、否则则会将表中原有的key对应的value修改成这个value(修改功能)。
扩容:扩容为原先的2倍并将原先的数据复制过来
JDK8中的修改:
- new HashMap()时没有创建长度为16的数组。
- 在首次调用put()方法是才创建数组
- 数组不再是Entry[], 而是Node[]
- 底层增加了红黑树、在数组长度 > 64且某一个索引位置上的元素数量 > 8时会将那个索引位置上的元素以红黑树的方式进行存储。
扩容机制不是在无法存储更多元素时才进行扩容,有一个负载因子,由负载因子和当前长度计算出临界值,超过临界值、且生成新的链表时(新存储的位置原先有一个元素,在那个索引生成了一个新的链表、这样会使查找效率降低)就会进行扩容
hash值的计算中有调用hashCode().
默认加载因子:0.75.
List只会在存储满后进行扩容,Map会在扩容临界值达到时扩容。
LinkedHashMap
假装有序的HashMap,结点为Entry[],Entry继承与Node,并新增了成员变量Before和After用于记录元素的添加顺序。
Map中的方法
- 新增(.put(Object key, Object Value))、修改(同key元素put一下)、删除(.remove(Object key))、
- map.size()返回Map中键值对的数量。.clear()将map中元素全部清除,但不是将map = null;不会在调用类似方法时有空指针异常。
- .containsKey(Object ksy)、.containsValue(Object Value)方法,判断Map中是否有该Key,是否有该Value
对增删改的测试
HashMap map = new HashMap();
//添加
map.put("AA", 123);
map.put("BB", 123);
map.put("CC", 45);
map.put(45, 123);
//修改
map.put("AA", 878);
System.out.println(map);
HashMap map1 = new HashMap();
map1.put("59", 123);
map1.put("555", 123);
map.putAll(map1);
System.out.println(map);
map.remove("CC");
System.out.println(map);
遍历涉及到的方法,Map属于键值对,不能直接使用迭代器。
- Key值的遍历
使用KeySet()方法获取Set的对象,调用Set的迭代器(iterator)进行遍历
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
- Values值的遍历
调用.values()方法获取一个Collection对象,其中存储Map中的所有value值,其顺序与.KeySet()方法得到的元素顺序相一致。
Collection values = map.values();
for(Object o : values){
System.out.println(o);
}
- Entry的遍历
使用.entrySet()方法返回一个Set对象,其中以Entry形式存储map中所有的键值对,在经过迭代器和向下转型之后可以调用.getKey()与.getValue()方法获取具体的键值对。
Set set = map.entrySet(); //存储的时一个个Entry
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry e = (Map.Entry)iterator.next(); //iterator.next()返回的是一个Entry。
System.out.println(e.getKey() + "--->" + e.getValue());
}
Properties
Properties是Hashtable的子类,但一般使用Properties进行读取配置文件。
// jdbc.properties文件:
// 键值对形式,可以被Properties读取,不能有空格
name=QingHe
password=qh1234567
通过流与Properties对象实现对配置文件的读取:
.load();
.getProperties(String key);
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("jdbc.properties");
pro.load(fis);
System.out.println(pro.getProperty("name"));
注:根据Properties读取的文件Key必须是String。
Collections工具类
Collections是用来操作List、Set和Map的工具类,其中提供了便利的方法。
排序操作:(均为static方法)
- reverse(List):反转 List 中元素的顺序
- shuffle(List):对 List 集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
- sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序(定制排序)
- swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找、替换
-
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
-
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
-
Object min(Collection)
-
Object min(Collection,Comparator)
-
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
-
void copy(List dest,List src):将src中的内容复制到dest中(要求dest长度必须大于等于src长度)
//标准写法,直接创建list会导致list长度过小 List list1 = Arrays.asList(new Object[list.size()]); Collections.copy(list, list1); System.out.println(list1);
-
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换
List 对象的所有旧值
线程安全:
使用对应的.synchronizedXxx()方法,返回一个线程安全的对应对象。
List list2 = Collections.synchronizedList(list); //此时list2即为线程安全的ArrayList(线性表)