数组与集合
集合、数组都是对多个数据进行存储操作的结构。此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储。
- 数组存储的特点
一旦初始化以后,其长度就确定了。数组一旦定义好,其元素的类型也就确定了,我们也就只能操作指定类型的数据。 - 数组存储的弊端
1、一旦初始化以后,其长度就不可修改。
2、数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
3、获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用。
4、数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
使用集合存储数据便解决了使用数组存储数据方面的弊端。
Collection接口
- 概述
Collection接口是单列集合,用来存储一个一个的对象。主要有List和Set两个接口。
1)List接口:存储有序、可重复的数据。(实现类:ArrayList,LinkedList,Vector)。
2)Set接口:存储无序、不可重复的数据。(实现类:HashSet,LinkedHashSet,TreeSet)。 - 常用方法
方法 | 描述 |
---|---|
boolean add(Object obj) | 向集合中添加一个元素 |
boolean addAll(Collection<> coll) | 将集合coll中的所有元素添加到原有集合中 |
int size() | 返回当前集合中的元素个数 |
boolean isEmpty() | 判断当前集合是否为空 |
boolean contains(Object obj) | 判断当前集合中是否包含元素obj |
boolean containsAll(Collection<> coll) | 判断当前集合中是否包含集合coll中的所有元素 |
boolean remove(Object obj) | 从当前集合中删除元素obj |
boolean removeAll(Collection<> coll) | 从当前集合中删除集合coll中的所有元素 |
boolean retainAll(Collection<> coll) | 获取当前集合和集合coll的交集并保留到当前集合中。若当前集合中的内容发生了更改则返回true,否则返回false。 |
- Collection集合与数组之间的转换
1)集合—>数组:(toArray())
举例:
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
2)数组—>集合:调用Arrays类的静态方法asList()
举例:
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2
使用Collection集合存储对象须注意:
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()。
Iterator接口与foreach循环
遍历Collection的两种方式
1、Iterator遍历代码实现
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
//next():指针下移,将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
2、foreach遍历代码实现
for(Object obj : coll){
System.out.println(obj);
}
Collection子接口:List接口
- 存储数据特点
List存储有序的,可重复的数据。 - 常用方法
方法 | 描述 |
---|---|
void add(int index, E element) | 将元素element插入到当前集合中index指向的位置 |
boolean addAll(int index, Collection<> coll) | 将集合coll中的所有元素插入到当前集合中index指向的位置。 |
E remove(int index) | 将index位置的元素从当前集合移除,返回被删除的元素值,下标不合理时会产生下标越界异常。 |
E set(int index, E element) | 使用element元素替换当前集合中index位置的元素,并返回被替换的元素 |
E get(int index) | 返回当前集合中下标为index位置的元素 |
ListsubList(int fromIndex, int toIndex) | 返回当前集合中从fromIndex到toIndex位置的元素(左闭右开),返回的集合和当前集合共用同一块内存区域。 |
- List接口常用实现类
(1)ArrayList:作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData存储。
(2)LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高,底层使用双向链表存储。
(3)Vector:作为List接口的古老实现类,线程安全的,效率低,底层使用Object[] elementData存储。 - ArrayList源码分析
(1)jdk7的情况
1)ArrayList list = new ArrayList(),底层创建了长度是10的Object[]数组elementData。
2)若执行添加操作导致底层elementData数组容量不够,则扩容。默认情况下扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
3)常用带参构造器:ArrayList list = new ArrayList(int capacity)
(2)jdk8的情况
1)ArrayList list = new ArrayList(),底层Object[] elementData初始化为{},并没创建长度为10的数组。
2)第一次调用add()时,底层才创建了长度10的数组。 - LinkedList源码分析
(1)LinkedList list = new LinkedList(),内部声明了Node类型的first和last属性,默认值为null。
(2)执行添加操作时将元素封装到Node中,创建了Node对象。
(3)Node的定义体现了LinkedList的双向链表说法。 - Vector源码分析
通过Vector()构造器创建对象时,底层都创建了长度为10的数组。扩容时,默认扩容为原来的数组长度的2倍。
注意
添加对象时,对象所在的类须重写equals()方法。
Collection子接口:Set接口
- 存储数据特点
存储无序的,不可重复的元素。
具体以HashSet为例:
(1)无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的。
(2)不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。 - 元素添加过程(以HashSet为例)
(1)向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值。
(2)此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置)。
(3)如果此位置上没其他元素,则元素a添加成功。
(4)如果此位置上有其他元素b(或以链表形式存在的多个元素,则比较元素a与元素b的hash值。
(5)如果hash值不相同,则元素a添加成功。
(6)如果hash值相同,进而需要调用元素a所在类的equals()方法:equals()返回true,元素a添加失败, equals()返回false,则元素a添加成功。 - 常用方法
Set接口中没有额外定义新的方法,使用的都是Collection中声明过得方法。 - 常用实现类
(1)HashSet:作为Set接口的主要实现类,线程不安全,可以存储null值。
(2)LinkedHashSet:是HashSet的子类,可以按照添加顺序遍历其内部数据。其效率高于HashSet。
(3)TreeSet:可以按照对象的指定属性进行排序。 - 存储对象所在类的要求
Set(主要指:HashSet、LinkedHashSet)中添加的数据时,其所在的类一定要重写hashCode()和equals()方法,且重写的hashCode()和equals()尽可能保持一致性,即:相等的对象必须具有相等的散列码。 - TreeSet的使用
向TreeSet中添加的数据,要求是相同类的对象。
(1)自然排序:比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()。
举例:
public void test(){
TreeSet set = new TreeSet();
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
(2)定制排序:比较两个对象是否相同的标准为:compare()返回0.不再是equals()。
举例(没有使用泛型):
public void test(){
Comparator com = new Comparator() {
//照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
Map接口
- 常用实现类
Map:存储 key—value 对的数据。
(1)HashMap:是Map的主要实现类,线程不安全,效率高,可以存储null的key和value。
(2)LinkedHashMap:是HashMap的子类,可以按照添加的顺序实现遍历。原因:在HashMap的底层结构基础上添加了一对指向前一个和后一个元素的指针。
(3)TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。
(4)HashTable:作为古老的实现类,线程安全的,效率低,不能存储null的key和value。
(5)Properties:常用来处理配置文件,key和value都是String类型的。 - 存储结构
(1)Map中的key:无序的,不可重复的,使用Set存储所有的key(存储key时,key所在的类需要重写equals()和hashCode() )。
(2)Map中的value:无序的,可重复的,使用Collection存储所有的value(存储value时,value所在的类需要重写equals())。
(3)一个键值对:key-value构成了一个Entry对象。Map中的entry是无序的,不可重复的,使用Set存储所有的entry。 - 常用方法
方法 | 描述 |
---|---|
E put(K key, E value) | 用于将指定的key和value组成一对放入当前集合中。增加key和value时则返回null,修改key和value时则返回key之前对应的value。 |
E remove(Object key) | 从当前集合删除key关联的键值对。若key不存在则返回null,否则返回key对应的value。 |
boolean containsKey(Object key) | 用于判断当前集合中是否存在key |
boolean containsValue(Object value) | 用于判断当前集合中是否存在value |
E get(Object key) | 根据key返回其对应的value |
Set<Map.Entry<k, v>>entrySet() | 将Map转化为Set |
Set keySet() | 返回当前集合中包含key的Set视图 |
java.util.Map.Entry<k, v> | j接口代表键值对,提供的方法有: E getKey():获取当前键值对中key的数值并返回。E getValue():获取当前键值对中value的数值并返回。 |
- 内存结果说明
(1)jdk7的情况
1)HashMap map = new HashMap():HashMap在jdk7中的实现原理:在实例化以后,底层创建了长度是16的一维数组Entry[] table。
2)map.put(key1, value1):
a、首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
b、如果此位置上的数据为空,此时的key1-value1添加成功。
c、如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值。
d、如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。
e、如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,如果equals()返回false:此时key1-value1添加成功;如果equals()返回true:使用value1替换value2。
3)添加元素时,当元素个数超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原的数据复制过来。
(2)jdk8的情况
1)new HashMap():底层没创建一个长度为16的数组。
2)jdk 8底层的数组是:Node[],而非Entry[]。
3)首次调用put()方法时,底层创建长度为16的数组。
4)jdk7底层结构为:数组+链表。jdk8中底层结构为:数组+链表+红黑树。
5)当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。 - HashMap底层典型属性的说明:
(1)DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16。
(2)DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75。
(3)threshold:扩容的临界值 = 容量*填充因子:16 * 0.75 => 12。
(4)TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8。
(5)MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64 - TreeMap的使用
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为需要按照key进行排序(自然排序 、定制排序)。 - 使用Properties读取配置文件
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("user.properties");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Collections工具类的使用
- 作用
操作Collection和Map的工具类。 - 常用方法
方法 | 描述 |
---|---|
reverse(List) | 反转List元素的顺序 |
shuffle(List) | 对List集合元素进行随机排序 |
sort(List) | 根据元素的自然排序对List集合元素进行升序排序 |
sort(List, Comparator) | 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 |
swap(List, int i, int j | 将list 集合中的 i 处元素和 j 处元素进行交换 |
Object max(Collection coll) | 根据元素的自然顺序,返回给定集合中的最大元素 |
Object max(Collection coll,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最大元素 |
Object min(Collection coll) | 根据元素的自然顺序,返回给定集合中的最小元素 |
Object min(Collection coll,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最小元素 |
int frequency(Collection coll,Object obj) | 返回集合中指定元素的出现次数 |
void copy(List dest,List src) | 将集合src中的内容复制到集合dest中 |
boolean replaceAll(List list,Object oldVal,Object newVal) | 使用新值替换 List 对象的所有旧值 |
- Collection和Collectios的区别
(1)Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一 个工具类,服务于Java的Collection框架。
(2).Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。