数组在内存存储方面的特点
数组初始化后,长度就固定了。
数组声明类型后,就决定了元素初始化时的类型。
数组在存储数据方面的弊端
数组初始化后,长度不可变,不利于扩展。
数组提供的方法和属性太少,不便于增删插等操作,且效率低。同时无法直接获取存储元素的个数。
数组存储的数据是有序的、可重复的。–>存储数据的特点单一。
Java集合类可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组。
Collection没有提供具体的实现类,下面还有很多细分。而Map接口提供了。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
public class Demo1{
public static void main(String[] args) {
//Collection接口中的方法使用
Collection coll=new ArrayList();
//add(Object e)
coll.add("AA");
coll.add(123);
coll.add(new Date());
//size()
System.out.println(coll.size());//3
System.out.println(coll);
//addAll():将coll集合中的元素添加到当前集合中
Collection coll1=new ArrayList();
coll1.add(4);
coll1.addAll(coll);
System.out.println(coll1.size());//4
System.out.println(coll1);
//isEmpty()
System.out.println(coll.isEmpty());//false
//clear()清空集合元素
coll.clear();
}
}
import java.util.*;
public class Demo1{
public static void main(String[] args) {
Collection coll=new ArrayList();
coll.add("ss");
coll.add(456);
coll.add(false);
coll.add(new String("s"));
//contains(Object e):判断当前集合中是否包含e
//我们在判断时会调用e对象所在类的equals()方法
//通常自定义的类都要重写equals()方法,比较的是内容,否则比较的是地址
boolean contains=coll.contains(123);
System.out.println(contains);//false
System.out.println(coll.contains(new String("s")));//true
System.out.println(coll.contains("s"));//true
System.out.println(coll);//[ss, 456, false, s]
//2.cotainsAll(Collection coll1):判断coll1中的所有元素是否都存在于当前集合中
Collection c1= Arrays.asList(123,456);
System.out.println(coll.containsAll(c1));//false
//3.remove(Object obj):
Collection c2=new ArrayList();
c2.add(13);
c2.add("644");
c2.add(new String("dsds"));
c2.add(false);
c2.remove(13);//返回值是:是否移除成功
System.out.println(c2);//[644, dsds, false]
c2.remove("dsds");
System.out.println(c2);//[644, false]
//4.removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素
c2.add(13);
c2.add("dsds");
System.out.println(c2);//[644, false, 13, dsds]
Collection c3=Arrays.asList(123,13,"dsds",456);
c2.removeAll(c3);
System.out.println(c2);//[644, false]
//5.交集 retainAll(Collection coll1)
//获取当前集合和coll1集合的交集,并返回给当前集合
Collection c4=new ArrayList();
c4.add(13);
c4.add("644");
c4.add(new String("dsds"));
c4.add(false);
System.out.println(c3);//[123, 13, dsds, 456]
System.out.println(c4);//[13, 644, dsds, false]
c4.retainAll(c3);
System.out.println(c4);//[13, dsds]
//6.equals(Object obj):比较两个集合中的元素是否一样(注意List是有序的)
//所以不仅要元素一样,元素的排列顺序也一样才能true
//如果是Set那么就只需要判断里面的元素是否一样,无序的
Collection c5=new ArrayList();
Collection c6=Arrays.asList(123,456);
c5.add(123);
c5.add(456);
System.out.println(c5.equals(c6));//true
Collection c7=new ArrayList();
c7.add(456);
c7.add(123);
System.out.println(c7.equals(c6));//false
//8.集合-->数组:toArray()
//这里的数组元素是Object类型的
c7.add(new Date());
c7.add("sdfsa");
Object[] arr=c7.toArray();
for(int i=0;i集合:调用Arrays类的静态方法aList()
List list=Arrays.asList(new String[]{"aa","dd","dfaf"});
System.out.println(list);//[aa, dd, dfaf]
//注意Arrays类中的aList()
List arr1=Arrays.asList(new int[]{123,456});
//这里把new int[]{123,456}整体当一个元素存入了
System.out.println(arr1.size());//1
//而这样写才把123,456当元素存入了
List arr2=Arrays.asList(new Integer[]{123,456});
System.out.println(arr2.size());//2
//hashCode():返回当前对象的哈希值(了解一下就行)
System.out.println(c7.hashCode());
}
}
遍历Collection
import java.util.*;
public class Demo1{
public static void main(String[] args) {
//iterator():返回Iterator接口的实例,用于遍历集合元素,放在IteratorTest.java中
//遍历集合:使用迭代器Iterator接口
Collection c=new ArrayList();
c.add(123);
c.add(5);
c.add(55);
c.add("ds");
Iterator iterator=c.iterator();
//不推荐使用这种方法
for(int i=0;i
迭代器执行原理
先下移再返回值
错误方式一
Collection c=new ArrayList();
c.add(123);
c.add(5);
c.add(55);
c.add("ds");
Iterator iterator=c.iterator();
while (iterator.next()!=null)//这样指针会下移,然后判断
//会跳着输出元素且数组越界报错
{
System.out.println(iterator.next());
}
错误方式二
Collection c=new ArrayList();
c.add(123);
c.add(5);
c.add(55);
c.add("ds");
Iterator iterator=c.iterator();
while (c.iterator().hasNext())//出现死循环,一直输出第一个元素
//每次都重新new了一个迭代器
{
System.out.println(iterator.next());
}
迭代器remove方法的使用
import java.util.*;
public class Demo1{
public static void main(String[] args) {
Collection c=new ArrayList();
c.add(123);
c.add(5);
c.add(55);
c.add("ds");
c.add("Tom");
Iterator iterator=c.iterator();
while (iterator.hasNext()){
Object obj=iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
//迭代器的remove方法,可以在遍历的时候删除集合中的元素
//此方法不同于集合直接调用remove
iterator=c.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+" ");//123 5 55 ds
}
}
}
注意
也就是说一开始先要让迭代器下移一个位置才可以删,还有就是删完该迭代器所指位置存的元素后不能再删一次,否则都会报错。
使用foreach循环遍历集合元素
也叫增强for循环。
Collection c=new ArrayList();
c.add(123);
c.add(5);
c.add(55);
c.add("ds");
c.add("Tom");
//内部仍然调用了迭代器
for(Object obj:c){
System.out.println(obj);
}
增强for循环和普通for循环区别
String[] arr=new String[]{"mm","mm","mm"};
//普通for赋值
for(int i=0;i
Collection子接口之一:List接口
List接口的三个实现类:
ArrayList:作为List接口的主要实现类(常用)线程不安全,效率高
底层使用Object[] elementDate存储
LinkedList:底层使用双向链表存储
对于频繁的插入删除操作,使用此类效率比ArrayList高
Vector:作为List接口的古老实现类,线程安全,效率低,
底层使用Object[]存储
三者的相同点:三个类都实现类List接口,存储数据的特点相同:
存储有序的、可重复的数据
ArrayList源码分析
底层创建了长度是10的Object[]数组elementDate
ArrayList list=new ArrayList();
list.add(123);//elementDate[0]=new Integeer(123)
当添加到第11个元素
如果此次的添加导致底层elementDate数组容量不够,则扩容
默认情况下,扩容为原来容量的1.5倍
同时需要将原有数组中的数据赋值到新的数组中
结论:建议开发中使用代餐的构造器自定义容量
ArrayList list=new ArrayList(int capacity)
jdk 8.0中ArrayList的变化:
ArrayList list=new ArrayList();
底层Object[] elementDate初始化为{},并没有创建长度为10的数组
第一次调用add()时,底层才创建了长度为10的数组
并将数据123添加到elementDate,后续的添加和扩容操作与jdk 7无异
list.add(123);
小结:jdk7中ArrayList的对象的创建类似于单例的饿汉式,二jdk8中的ArrayList
的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
LinkedList的源码分析
LinkedList list=new LinkedList();
内部声明了Node类型的first和last属性,默认值为null
list.add(123);
将123封装到Node中,创建了Node对象
其中Node定义为:体现了LinkedList的双向链表说法
private static class Node{
E item;
Node next;
Node prev;
Node(Node prev,E element,Node next){
this.item=element;
this.next=next;
this.prev=prev;
}
}
Vector的源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。每次扩容为之前容量的二倍。已经不常用了。稍微了解即可。
List接口中的常用方法
import java.util.*;
public class Demo1{
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add("sdf");
list.add(464);
list.add(646);
list.add(464);
System.out.println(list);//[sdf, 464, 646, 464]
//1.void add(int index,Object ele):在index位置插入ele元素
list.add(1,"BB");
System.out.println(list);//[sdf, BB, 464, 646, 464]
//2.boolean addAll(int index,Collection eles):
//从index位置开始将eles中的所有元素添加到当前数组中
List list1=Arrays.asList(1,2,3);
list.addAll(list1);
System.out.println(list);//[sdf, BB, 464, 646, 464, 1, 2, 3]
//注意 如果是list.add(list1)那么只添加了一个元素
//因为把list1整体当成一个元素了
//3.Object get(int index):获取指定index位置的元素
System.out.println(list.get(0));//sdf
//4.int indexOf(Object obj):返回obj在当前集合中首次出现的位置
//不存在则返回-1
int index=list.indexOf(464);
System.out.println(index);//2
//5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
//不存在则返回-1
System.out.println(list.lastIndexOf(464));//4
//6.Object remove(int index):移除指定index位置的元素,并返回此元素
Object obj=list.remove(0);
System.out.println(obj);//sdf
System.out.println(list);//[BB, 464, 646, 464, 1, 2, 3]
//7.Object set(int index,Object ele):修改指定index位置的元素为ele
list.set(1,"ddd");
System.out.println(list);//[BB, ddd, 646, 464, 1, 2, 3]
//8.list subList(int formIndex,int toIndex)
//返回从formIndex到toIndex位置左闭右开区间的子串
System.out.println(list.subList(1,5));//[ddd, 646, 464, 1]
}
}
总结:常用方法
增:add(Object obj)
删:remove(int index) / remove(Object ele)
改:set(int index,Object ele)
查:get(int index)
插:add(int index,Object ele)
长度:size()
遍历:1.Iterator迭代器方式 2.增强for循环 3.普通for循环
List遍历方式
import java.util.*;
public class Demo1{
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add("sdf");
list.add(464);
list.add(646);
list.add(464);
//方式一:
Iterator iterator=list.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+" ");
}
System.out.println();
//方式二:
for(Object obj:list){
System.out.print(obj+" ");
}
System.out.println();
//方式三:
for(int i=0;i
面试题
如何辨别remove删除的是指定元素还是指定索引处的元素呢。
例:
import java.util.*;
public class Demo1{
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.remove(2);
}
}
这里删除的其实是指定索引2处的元素,也就是3.
若想删除指定元素?也就是2,装箱new一个Integer对象,(Integer)2也行
import java.util.*;
public class Demo1{
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.remove(new Integer(2));
}
}
Collection子接口之二:Set接口
Set接口:存储无序的、不可重复的数据。没有额外定义新的方法,使用的都是Collection中声明过的方法。
HashSet:作为Set接口的主要实现类:线程线程不安全,可以存储null值
LinkedHashSet:HashSet的子类:遍历器内部数据时,可按照添加的顺序遍历
TreeSet:可以按照添加元素/对象指定属性排序
什么是无序性?
不是随机性。以HashSet为例说明:存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
HashSet
import java.util.*;
public class Demo1{
public static void main(String[] args) {
//无序性:不等于随机性,每次遍历结果依旧是固定的
//只是与添加的顺序可能不同
Set s=new HashSet();
s.add(46);
s.add(4846);
s.add(46);
s.add(46);
s.add("sdgr");
s.add("dgegr");
System.out.println(s);//[sdgr, dgegr, 46, 4846]
}
}
不可重复性
保证添加的元素按照equals()判断时不能返回true,即相同的元素只能添加一个。
当添加的元素是new了几个参数相同的自定义对象时,需要在自定义对象中重新equals方法和hashCode比较,hashCode值是根据对象属性来计算的,这样才不会重复添加,否则比较的是两个对象的地址值,都会被添加进去。
在自定义类中重写这两个方法。
添加元素的过程:以HashSet为例:
向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存在位置(即为索引位置),判断数组此位置上是否已经有元素:
1.没有其他元素,则元素a添加成功。-----(情况一)
2.若有其他元素b或以链表形式存在的多个元素,则比较元素a与元素b的hash值:如果hash值不同,则添加成功。-----(情况二)
若hash值相同,进而需要调用元素a所在类的equals()方法:
equals返回true,元素a添加失败
返回false则添加成功。------(情况三)
对于添加成功的情况2和情况3:元素a与已经存在指定索引上的数据以链表的方式存储。
以“七上八下”来记忆
jdk7中:
jdk8中:
HashSet底层:数组+链表结构。
LinkedHashSet
Set s=new LinkedHashSet();
s.add(46);
s.add(4846);
s.add(46);
s.add(46);
s.add("sdgr");
s.add("dgegr");
//遍历顺序和添加顺序一致
System.out.println(s);//[46, 4846, sdgr, dgegr]
TreeSet
举例:
Set s=new TreeSet();
s.add(46);
s.add(4846);
s.add(8);
s.add(5);
//s.add("dsag")报错,要同一类
System.out.println(s);//[5, 8, 46, 4846]按从小到大排好序
TreeSet两种排序方式
自然排序(实现Comparable接口)和定制排序(Comparator)。
自然排序:比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()。
import java.util.*;
public class Demo1{
public static void main(String[] args) {
Set s=new TreeSet();
s.add(new Person("a",58));
s.add(new Person("p",55));
s.add(new Person("c",8));
s.add(new Person("b",18));
for(Object obj:s){
System.out.println(obj.toString());
}
}
}
class Person implements Comparable{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
//按照姓名从小到大 年龄从小到大
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p=(Person) o;
int compare = this.name.compareTo(p.name);
if(compare!=0){
return compare;
}else{
return Integer.compare(this.age,p.age);
}
}else {
throw new RuntimeException("输入类型不匹配");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
定制排序:比较两个对象是否相同的标准为:compare()返回0,不再是equals()。
import java.util.*;
public class Demo1{
public static void main(String[] args) {
//按照年龄从小到大排 只要年龄一样的话就不会存储进去
Comparator com=new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person&&o2 instanceof Person){
Person p1=(Person) o1;
Person p2=(Person) o2;
return Integer.compare(p1.age,p2.age);
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
Set s=new TreeSet(com);//加了参数就按定制的规则来
s.add(new Person("a",58));
s.add(new Person("p",55));
s.add(new Person("c",8));
s.add(new Person("b",18));
s.add(new Person("d",18));
for(Object obj:s){
System.out.println(obj.toString());
}
}
}
class Person implements Comparable{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
//按照姓名从小到大 年龄从小到大
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p=(Person) o;
int compare = this.name.compareTo(p.name);
if(compare!=0){
return compare;
}else{
return Integer.compare(this.age,p.age);
}
}else {
throw new RuntimeException("输入类型不匹配");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
输出:
Map接口
Map:双列接口,存储key-value对的数据
HashMap的底层:数组+链表(jdk7及之前),数组+链表+红黑树(jdk8)。
HashMap:作为Map的主要实现类,线程不安全的,效率高,可以存储null的key和value
LinkedHashMap:按照添加的顺序遍历元素,因为添加了一对指针,一个指前一个指后
频繁的遍历操作,此类操作效率高于HashMap
TreeMap:用key来自然排序或者定制排序。底层使用红黑树。
Hashtable:古老实现类,线程安全的,效率低,不能存储null的key和value
Properties:Hashtable的子类常用来处理配置文件,key和value都是String类型。
存入Map中的元素是一个个的,是图中的Entry(x,y):
Map结构的理解:
Map中的key:无序、不可重复,使用Set存储所有的key,具体什么Set就看是什么Map
要求key所在的类要重写equals()和hashCode().(以HashMap为例)
Map中的value:无序、可重复,使用Collection存储所有的value
value所在类要重写equals()
一个键值对:key-value构成了一个Entry对象
Map中的Entry:无序、不可重复,使用Set存储所有的entry
HashMap的底层实现原理
HashMap的底层实现原理 以jdk7为例说明:
HashMap map=new HashMap();
实例化后,底层创建了长度是16的一维数组Entry[] table
map.put(key1,value1);
先调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法后得到
key1在Entry数组中存放的位置。
如果此位置为空,则key1-value1(Entry)添加成功。--->情况1
若不为空,(意味着此位置存在一个或多个数据,以链表形式存在),比较key1和
已经存在的多个数据的哈希值,如果key1与已存在的数据的哈希值都不相同,则添加成功。--->情况2
若key1的哈希值与已经存在的某数据(key2-value2)的哈希值相同,
则调用key1所在类的equals()方法,若返回false则添加成功。--->情况3
若返回true:使用value1替换key2的value2值(相当于put有修改的功能)
关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
添加过程中可能有扩容问题,默认的扩容方式为扩大到当前的2倍。
jdk8相比jdk7底层实现方面不同:
1.new HashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[],而非Entry[]
3.首次调用put()时,底层创建长度为16的数组
4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
当数组的某一索引位置上的元素以链表形式存在的数据个数>8且当前的数组长度>64时
此时这个索引位置上的所有数据改为使用红黑树存储
Map中常用方法
以HashMap为例:
import java.util.*;
public class Demo1{
public static void main(String[] args) {
Map map=new HashMap();
Map map1=new HashMap();
map1.put(55,69);
map.put("12",5);
map.put(5,6);
map.put(58,99);
map.put("12",99);//修改
System.out.println(map);//{12=99, 5=6, 58=99}
//remove(Object key)
Object value=map.remove(5);//返回括号内key值对应的value值
System.out.println(value);//6 若没有该key值,则返回null
//get(Object key) 获取对应的value值,若没有该key则返回null
System.out.println(map.get("12"));//99
//containsKey(Object key)判断是否含有该key
System.out.println(map.containsKey(555));//false
//containsValue(Object value)判断是否含有该value
System.out.println(99);//true
//int size():返回map中key-value对的个数
System.out.println(map.size());//2
//boolean equals(Object obj):判断当前的map和参数对象obj是否相等
System.out.println(map.equals(map1));//false
//clear()清空元素
map.clear();//与map=null操作不同
System.out.println(map.size());
System.out.println(map);//{}
System.out.println(map.isEmpty());//true
// 但不是空指针,在空间中的结构还存在,只是元素清空了
}
}
Map的遍历
import java.util.*;
public class Demo1{
public static void main(String[] args) {
Map map=new HashMap();
map.put("99",54);
map.put("12",5);
map.put(5,6);
map.put(58,99);
//遍历所有的key集:KeySet()
Set set=map.keySet();
Iterator iterator=set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集
Collection values=map.values();
for(Object obj:values){
System.out.println(obj);
}
//遍历所有的key-value
//方式一:entrySet()
Set entrySet=map.entrySet();
Iterator iterator1=entrySet.iterator();
while (iterator1.hasNext()){
Map.Entry entry=(Map.Entry) iterator1.next();
System.out.println(entry.getKey()+"---"+entry.getValue());
}
//方式二
Set KeySet=map.keySet();
Iterator iterator2=KeySet.iterator();
while (iterator2.hasNext()){
Object key=iterator2.next();
Object value=map.get(key);
System.out.println(key+"---"+value);
}
}
}
常用方法
增:put(Object key,Object value)
删:remove(Object key)
改:put(Object key,Object value)
查:get(Object key)
长度:size()
遍历: map.keySet() / map.values() / entrySet()
自然排序
import java.util.*;
public class Demo1{
public static void main(String[] args) {
//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
//因为要按照key进行排序:自然排序、定制排序
TreeMap map=new TreeMap();
map.put(new Person("aa",85),85);
map.put(new Person("c",8),89);
map.put(new Person("b",45),82);
map.put(new Person("e",65),90);
Set keySet=map.keySet();
Iterator iterator=keySet.iterator();
while (iterator.hasNext()){
Object key=iterator.next();
Object value=map.get(key);
System.out.println(key+" "+value);
}
}
}
class Person implements Comparable{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
//姓名从小到大 年龄从大到小
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p=(Person) o;
int compare=this.name.compareTo(p.name);
if(compare!=0){
return compare;
}else {
return Integer.compare(this.age,p.age);
}
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
定制排序(推荐使用)
import java.util.*;
public class Demo1{
public static void main(String[] args) {
TreeMap map=new TreeMap(new Comparator() {
//按年龄从小到大
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person&&o2 instanceof Person){
Person p1=(Person) o1;
Person p2=(Person) o2;
return Integer.compare(p1.age,p2.age);
}
throw new RuntimeException("输入的类型有误");
}
});
map.put(new Person("aa",85),85);
map.put(new Person("c",8),89);
map.put(new Person("b",45),82);
map.put(new Person("e",65),90);
Set keySet=map.keySet();
Iterator iterator=keySet.iterator();
while (iterator.hasNext()){
Object key=iterator.next();
Object value=map.get(key);
System.out.println(key+" "+value);
}
}
}
class Person{
String name;
int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
TreeMap应用少一些,更多关注HashMap。



