B站视频指路:尚硅谷Java入门视频教程(在线答疑+Java面试真题)_哔哩哔哩_bilibili
写在前面:马上秋招,打算从0开始再学一遍Java,开个知识点记录贴,就当做课堂笔记吧.
Java结合框架概述:
1.一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储.另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像一种容器,可以动态的把多个对象的引用放入容器中
·数组在内存存储方面的特点:
①数组初始化后,长度就确定了
②数组声明的类型,就决定了进行元素初始化时的类型
·数组在存储数据方面的弊端:
①数组在初始化以后,长度就不可变了,不便于扩展
②数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高,同
时无法直接获取存储元素的个数.
③数组存储的数据是有序的、可以重复的---->存储数据的特点比较单一
④数组存储数据的特点:有序、可重复.对于无序、不可重复的需求,不能满足
2.Java集合类可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组
3.集合、数组都是对多个数据进行存储操作的结构,简称Java容器
tips:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg.avi,数据库中)
Java集合分为Collection和Map两种体系
·Collection接口:单例数据、定义了存取一组对象的方法的集合,用来存储一个一个的对象
①List:元素有序、可重复的集合--->"动态数组"
ArrayList、LinkedList、Vector
List接口中ArrayList、LinkedList都不是线程安全,Vector是线程安全
②Set:元素无序、不可重复的集合--->高中的"集合"概念
HashSet、LinkedHashSet、TreeSet
·Map接口:双列数据,保存具有映射关系"Key-value对"的集合 y=f(x)
HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
hashmap treemap 都是非线程安全的,currenthashmap, hashTable 是线程安全的
Collection接口中的方法的使用
import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.Date; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); Collection coll1 = new ArrayList(); //add():将元素e添加到集合coll中 coll.add("ZK"); coll.add("ZK"); coll.add(234234);//自动装箱 coll.add(new Date()); //size():获取添加的元素的个数 System.out.println(coll.size());//4 //addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中 coll1.add(123456); coll1.add('s'); coll.addAll(coll1); System.out.println(coll.size());//6 System.out.println(coll); //isEmpty():判断当前集合是否为空 System.out.println(coll.isEmpty()); } }
tips:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //contains(Object obj):判断当前集合中是否包含obj 调用的是obj对象所在类的equals方法 // equals没有重写 用的是object里的 == System.out.println(coll.contains(new String("ZAHNGKE")));//true // 这个new String("ZAHNGKE")和上面的new String("ZAHNGKE") 是两个对象 // 所以可以得出结论 contains判断的是内容 不是地址 调用的是equals 不是 == 因为String重写了equals System.out.println(coll.contains(new person("tjw",18)));//false 这里equals没有重写 用的是object里的 == //containsAll(Collecetion coll1):判断形参coll1中所有元素是否都存在于当前集合中 Collection coll1 = Arrays.asList(123455); System.out.println(coll.containsAll(coll1)); } }
import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //remove(Object obj):从当前集合中移除obj 也要重写equals 因为首先要判断 集合内有没有 //所以要从第一个元素开始挨个equals coll.remove(new person("tjw",18)); System.out.println(coll); //removeAll(Collection coll1):从当前集合中移除coll1中所有的元素 即 移除两个集合的交集 Collection coll1 = Arrays.asList(1,123455); coll.removeAll(coll1); System.out.println(coll); } }
import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //retainAll(Collection coll1):交集: 获取当前集合和coll1集合的交集 并返回给当前集合 // Collection coll1 = Arrays.asList(123,123455,true); // coll.retainAll(coll1); // System.out.println(coll); //equals(Object obj):若要返回true,需要当前集合和形参集合元素都相同 Collection coll1 = new ArrayList(); coll1.add(123455); coll1.add(1); coll1.add(new String("ZAHNGKE")); coll1.add(new person("tjw",18)); coll1.add(true); System.out.println(coll.equals(coll1)); } }
import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //hashCode():返回当前对象的hash值 System.out.println(coll.hashCode()); //集合-->数组:toArray() Object[] objects = coll.toArray(); for (int i = 0; i < objects.length; i++) { System.out.println(objects[i]); } //拓展:数组-->集合:调用Arrays类的静态方法asList() List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"}); System.out.println(strings); List ints = Arrays.asList(new int[]{123, 456});//基本数据类型不行 要用包装类 System.out.println(ints+","+ints.size()); List integers = Arrays.asList(new Integer[]{123, 456}); System.out.println(integers+","+integers.size()); } }
import org.junit.Test; import java.util.*; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //iterator():Iterator迭代器接口,返回Iterator接口的实例,用于遍历集合元素.放在IteratorTest.java中测试 Iterator iterator = coll.iterator(); //方式一: // System.out.println(iterator.next()); // System.out.println(iterator.next()); // System.out.println(iterator.next()); // System.out.println(iterator.next()); // System.out.println(iterator.next()); //方式二不推荐: // for (int i = 0; i < coll.size(); i++) { // System.out.println(iterator.next()); // } //方式三:推荐 //next():指针下移 将下移以后集合位置上的元素返回 while(iterator.hasNext()){ System.out.println(iterator.next()); } } } 错误方式2对应了Iterator iterator = coll.iterator(); 即 每次coll.iterator()都会生成一个新的迭代器对象,而我们要做的是使用同一个 然后不断下移
import org.junit.Test; import java.util.*; public class CollectionTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(new person("tjw",18)); coll.add(true); //iterator():Iterator迭代器接口,返回Iterator接口的实例,用于遍历集合元素.放在IteratorTest.java中测试 Iterator iterator = coll.iterator(); while (iterator.hasNext()){ Object o = iterator.next(); if("ZAHNGKE".equals(o)){ iterator.remove(); } } iterator = coll.iterator(); while (iterator.hasNext()){//需要重新从头开始 System.out.println(iterator.next()); } } }
使用foreeach循环遍历集合元素
·Java5.0提供了foreach循环迭代访问Collection和数组
·遍历操作不需要获取Collection或数组的长度,无需使用索引访问元素
·遍历集合的底层调用Iterator完成操作
·foreach还可以用来遍历数组
import org.junit.Test; import java.util.*; public class ForTest { @Test public void test(){ Collection coll = new ArrayList(); coll.add(123455); coll.add(1); coll.add(new String("ZAHNGKE")); coll.add(true); //for(集合中元素的类型 局部变量 : 集合对象) //内部仍然用了迭代器 for(Object obj : coll){ System.out.println(obj); } } @Test public void test2(){ int []arr = new int[]{1,2,3,4,5,6}; //for(数组元素的类型 局部变量 :数组对象) for(int obj : arr){ System.out.println(obj); } } @Test public void test3(){ String[] arr = new String[]{"MM","MM","MM"}; // //方式1:普通的for循环赋值 // for (int i = 0; i < arr.length; i++) { // arr[i] = "GG"; /// GG GG GG // } //方式2:增强for循环 for (String s:arr //这是把arr赋值给s arr本身不变 ) { s = "GG"; } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); MM MM MM } } }
Colletion子接口之一:List接口
·鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
·List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引.
·List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
·JDK API中List接口的实现类常用的就有:ArrayList、LinkedList和Vector
面试题:ArrayList、LinkedList、Vector的异同
相同点:三个类都实现类List接口,存储数据的特点相同:存储有序的、可重复的数据
不同点:
①ArrayList: 作为List接口的主要实现类.线程不安全的,效率高.底层使用Object[ ] elementData存储
②LinkedList: 对于频繁的插入、删除操作,使用此类效率比ArrarList高,底层使用双层链表存储
③Vector: 作为List接口的古老实现类.线程安全的,效率低. 底层使用Object[ ]elementData存储.
ArrayList的源码分析:
1.jdk7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[ ]数组elementData
lisd.add(123);//elementData[0]=new Integer(123);
若某次添加导致底层elementData数组容量不够,则扩容. 默认情况下扩容为原来容量的1.5倍,同时需要将原有数组中的数据赋值到新的数组中
tips:建议开发中使用带参的构造器ArrayList list = new ArrayList(int capacity);
2.jdk8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[ ]elementData初始化为{},并没有创建长度为10的数组
lisd.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将123添加到elementData[0]
若某次添加导致底层elementData数组容量不够,则扩容. 默认情况下扩容为原来容量的1.5倍,同时需要将原有数组中的数据赋值到新的数组中 即:后序的和7一样
3.小结
7中ArrayList的对象的创建有点类似于单例的饿汉式 而8有点像懒汉式 延迟的数组的创建,节省内存
LinkedList的源码分析:
LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,用于记录首末元素,默认值为null,同时定义内部类Node,作为LinkedList中保存数据的基本结构,Node除了保存数据还定义了两个变量:①prev变量记录前一个元素的位置 ②next变量记录下一个元素的位置
list.add(123);//将123封装到Node中,创建了Node对象
其中,Node定义为:体现了LinkedList的双向链表的说法
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; }
Vector的源码分析:
在jdk7和8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组长度的2倍
List接口常用方法
import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class ListTest { @Test public void test(){ ArrayList list = new ArrayList(); list.add(123455); list.add(1); list.add(new String("ZAHNGKE")); list.add(true); System.out.println(list); //void add(int index,Object ele):在index位置插入ele元素 list.add(1,"zhangtjw"); System.out.println(list); //boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来 Collection coll = new ArrayList(); coll.add(1); ArrayList list1 = new ArrayList(); list1.add(12323232); list.addAll(0,coll); list.addAll(0,list1); System.out.println(list); //Object get(int index):获取指定index位置的元素 System.out.println(list.get(0)); //int indexOf(Object obj):返回obj在集合中首次出现的位置 没有的话返回-1 System.out.println(list.indexOf(1)); //int LastIndexOf(Object obj):返回obj在集合中末次出现的位置 没有的话返回-1 System.out.println(list.lastIndexOf(1)); //Object remove(int index):移除指定index位置的元素,并返回此元素 System.out.println(list.remove(0)); System.out.println(list); //Object set(int index,Object obj):设置指定index位置的元素为ele list.set(0,'k'); System.out.println(list); //List sublist(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合 左闭右开 System.out.println(list.subList(0, 4)); } }
总结常用方法:
增: add(Object obj)
删: remove(int index) / remove(Object obj) -->Collection
改: set(int index,Object obj)
查: get(int index)
插: add(int index,Object ele)
长度: size()
遍历: ①Iterator迭代器 ②增强for循环 ③普通for循环
import org.junit.Test; import java.util.ArrayList; import java.util.Iterator; public class ListTest { @Test public void test(){ ArrayList list = new ArrayList(); list.add(123455); list.add(1); list.add(new String("ZAHNGKE")); list.add(true); //①Iterator迭代器 Iterator iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("**********************"); //②增强for循环 for (Object obj:list) { System.out.println(obj); } System.out.println("**********************"); //③普通for循环 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } }
面试题:
ans:1 2
如果改成list.remove(new Integer(2)) 那么ans:1 3
Colletion子接口之二:Set接口
Set接口概述:
·Set接口是Collection的子接口,set接口没有提供额外的方法,使用的都是Collection的方法
·Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加失败
·Set判断两个对象是否相同使用的是equals()方法 不是==运算符
一.以HashSet为例说明:
①无序性:不等于随机性 存储的数据在底层数组中并非按照数组的索引顺序添加.二十根据数据的哈希值决定的
②不可重复性: 保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个.
二.添加元素的过程:以HashSet为例说明
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
若此位置上没有元素,则添加成功-->情况1
若此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
若hash值不同,则元素a添加成功.-->情况2
若hash值相同,进而需要调用元素a所在类的equals()方法
equals()返回true,则元素a添加失败
equals()返回false,则元素a添加成功-->情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储
jdk 7 :元素a放到数组中 指向原来的元素
jdk 8 : 原来的元素在数组中 指向元素a
要求:向set中添加数据,其所在的类一定要重写equals()和hashCode(Object obj)方法
要求:重写equals()和hashCode(Object obj)方法要保持一致性 "相等的对象必须具有相等的散列码"
1.Set实现类之一:HashSet 作为Set接口的主要实现类
·HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类.
·HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能
·HashSet具有以下特点
①不能保证元素的排列顺序
②HashSet不是线程安全的
③集合元素可以是null
·HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等且两个对象的equals()方法返回值也相等
·对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则.即:"相等的对象必须具有相等的散列码"
·HashSet底层:数组+链表的结构
import org.junit.Test; import java.util.HashSet; import java.util.Iterator; public class SetTest { @Test public void test(){ HashSet hashSet = new HashSet(); hashSet.add(1); hashSet.add(2); hashSet.add(new p("zk",1)); hashSet.add(new p("zk",1)); //在不重写p的hashcode()的情况下 默认调用Object里的hashcode() // Object里的hashcode() 可以理解为 给你一个随机数 所以两个对象的hash值一定不同 所以可以添加成功 // 重写之后 两个内容完全相同的p对象 hash值相同 且equals 所以只能添加一个 hashSet.add('a'); Iterator iterator = hashSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } } class p{ private String name; private int age; public p() { } public p(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; p p = (p) o; if (age != p.age) return false; return name != null ? name.equals(p.name) : p.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; //neme是String String 重写过hashcode 他保证两个"zhangke" hash值一样 result = 31 * result + age; return result; } }
重写hashCode()方法的基本原则
·在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值.
·当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法返回值也应相等
·对象中用做equals()方法比较的Field,都应该用来计算hashCode值
重写equals()方法的基本原则
以自定义的p类为例子,何时需要重写equals()?
·当一个类有自己特有的"逻辑相等"概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象
·因此,违反了"相等的对象必须具有相等的散列码"
·结论:复写equals方法的时候一般都需要同时复写hashCode方法.通常参与计算hashCode的对象的属性也应该参与到equals中进行计算
2.Set实现类之二:LinkedHashSet
·LinkedHashSet是HashSet的子类
·LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的
·LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能.
·LinkedHashSet不允许集合元素重复
·对于频繁的遍历操作,LinkedHashSet效率高于HashSet
·遍历其内部数据时,可以按照添加的顺序遍历
3.Set实现类之二:TreeSet
·TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
·TreeSet底层使用红黑树结构存储数据
·可以按照添加对象的指定属性进行排序
·TreeSet两种排序方法:自然排序(实现Comparable接口)和定制排序(Comparator),默认情况下,TreeSet使用自然排序
TreeSet的自然排序中,比较两个对象是否为相同的标准为:compareTo()返回0.不再是equals().
TreeSet的定制排序中,比较两个对象是否为相同的标准为:compare()返回0.不再是equals().
·向TreeSet中添加的数据,要求是同一个类提供的
自然排序: import org.junit.Test; import java.util.Iterator; import java.util.TreeSet; public class SetTest { @Test public void test(){ TreeSet Set = new TreeSet(); //不能添加不同类的对象 // Set.add(1); // Set.add(2); // Set.add(new p("zk",1)); // Set.add(new p("zk",1)); // Set.add('a'); //举例1: // Set.add(1); // Set.add(2); // Set.add(13); // Set.add(-234); //举例2:要重写 compareTo Set.add(new p("zk4",1)); Set.add(new p("zk2",3)); Set.add(new p("zk1",2)); Set.add(new p("zk3",5)); Iterator iterator = Set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next().toString()); } } } class p implements Comparable{ private String name; private int age; public p() { } public p(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "p{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; p p = (p) o; if (age != p.age) return false; return name != null ? name.equals(p.name) : p.name == null; } @Override public int compareTo(Object o) { if(o instanceof p) { p p1 = (p)o; int x = this.name.compareTo(p1.name); if(x!=0){ return x; }else{ return Integer.compare(this.age,p1.age); } }else { throw new RuntimeException("No!"); } } }
定制排序:public class SetTest { @Test public void test(){ Comparator com = new Comparator() { //按照年龄从小到大排序 @Override public int compare(Object o1, Object o2) { if(o1 instanceof p && o2 instanceof p){ p p1 = (p) o1; p p2 = (p) o2; return Integer.compare(p1.getAge(),p2.getAge()); }else { throw new RuntimeException("No"); } } }; TreeSet Set = new TreeSet(com); Set.add(new p("zk4",13)); Set.add(new p("zk2",3)); Set.add(new p("zk1",3)); Set.add(new p("zk3",6)); Iterator iterator = Set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next().toString()); } } }
练习:
1.集合Collection中存储的如果是自定义类的对象,需要自定义类重写哪个方法
equals()方法 contains()\remove()/retainsAll()...要调用equals()
List:也要重写equals()方法.
Set:(HashSet、LinkedHashSet):equals()和hashCode()
(TreeSet):自然排序(compareTo(Object obj))
定制排序(compate(Object o1,Object o2))
练习:
mainTest .java
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class mainTest {
public static void main(String[] args) {
MyDate myDate1 = new MyDate(1996, 9, 19);
MyDate myDate2 = new MyDate(1997, 8, 12);
MyDate myDate3 = new MyDate(1998, 7, 11);
MyDate myDate4 = new MyDate(1999, 6, 13);
MyDate myDate5 = new MyDate(2000, 5, 14);
Employee employee1 = new Employee("dzk1",25,myDate1);
Employee employee2 = new Employee("czk2",24,myDate2);
Employee employee3 = new Employee("azk3",23,myDate3);
Employee employee4 = new Employee("bzk4",22,myDate4);
Employee employee5 = new Employee("fzk5",21,myDate5);
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Employee && o2 instanceof Employee){
Employee p1 = (Employee) o1;
Employee p2 = (Employee) o2;
MyDate d1 = p1.getBirthday();
MyDate d2 = p2.getBirthday();
int y = d1.getYear()-d2.getYear();
if(y!=0){
return y;
}
int m = d1.getMonth()-d2.getMonth();
if(m!=0){
return m;
}
return d1.getDay()- d2.getDay();
}
throw new RuntimeException("No");
}
};
//定制排序
TreeSet treeSet1 = new TreeSet(com);
treeSet1.add(employee1);
treeSet1.add(employee2);
treeSet1.add(employee3);
treeSet1.add(employee4);
treeSet1.add(employee5);
Iterator iterator = treeSet1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("__________________________");
//自然排序
TreeSet treeSet2 = new TreeSet();
treeSet2.add(employee1);
treeSet2.add(employee2);
treeSet2.add(employee3);
treeSet2.add(employee4);
treeSet2.add(employee5);
Iterator iterator2 = treeSet2.iterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
Mydate.java
public class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public MyDate() {
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}
Employee.java
import java.util.Date;
public class Employee implements Comparable{
private String name;
private int age;
private MyDate birthday;
public Employee() {
}
public Employee(String name, int age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
@Override
public int compareTo(Object o) {
if(o instanceof Employee){
Employee e = (Employee) o;
return this.name.compareTo(e.name);
}else{
throw new RuntimeException("No");
}
}
}
练习:
//set不允许有相同的元素 所以在addAll时候自动去除了相同的元素
练习:
//set.remove()是根据hashcode来删除的 刚开始存数据的时候p1以(1001,"AA")来计算hashcode,可remove的时候以p1(1001,"CC")来计算hashcode的 两者的哈希值很大概率不同 所以其实没有删除成功 相同的此时set里的p1(1001,"CC")是以(1001,"AA")来计算hashcode的 可是新添加的p以(1001,"CC")计算哈希值 所以可以添加成功 最后 添加p(1001,"AA")时,虽然和上面第一次添加的p1hashcode相同 但在随后的equals计算时 p(1001,"CC")和p(1001,"AA")不同 所以可以添加成功
Map接口
一、Map实现类的结构
Map:双列数据,存储key-value对的数据---类似于函数y=f(x);
1.HashMap:作为Map的主要实现类 线程不安全 效率高 可以存储null的key和value
①LinkedHashMap:保证遍历map元素时,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构上,添加了一堆指针,指向前一个和后一个元素,对于频繁的遍历操作 此类的执行效率高于HashMap
2.TreeMap:保证按照添加的key-value对进行排序 实现排序遍历 此时考虑key的自然排序和定制排序
底层使用红黑树
3.Hashtable:作为古老的实现类 线程安全 效率高 不可以存储null的key和value
①properties:常用来处理配置文件 key和value是String类型
HashMap的底层:数组+链表(jdk7及之前)
数组+链表+红黑树(jdk8)
面试题:
①HashMap的底层实现原理?
②HashMap和HashTable的异同?
③CurrentHashMap与Hashtable的异同?(暂时不讲)
二、Map结构的理解
Map中的Key: 使用Set存储所有的Key 无序 不可重复 -->key所在的类要重写equals()和hashCode()
Map中的Value: 使用Collection存储所有的Value 无序 可重复-->Value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象
Map中的Entry:无序的、不可重复的 使用Set存储所有的Entry
put()也是一个一个的放数据(Entry) 其中一个Entry中有两个属性 一个key 一个 value
三、HashMap的底层实现原理?以jdk7为例
HashMap map = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[ ] table;
....可能已经执行过多次put...
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置.若此位置上的数据为空,此时的key1-value1(Entry)添加成功, --情况1
若此位置上的数据不为空(意味着此位置上存在一个或者多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
①若key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功 --情况2
②若key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals()方法,比较:
若equals()返回false:此时key1-value1添加成功 --情况3
若equals()返true:使用value1替换相同value2
tips:情况2和3 此时key1-value1 和原来的数据以链表的方式存储
在不断的添加过程中 会涉及到扩容问题 当超出临界值(且要存放的位置非空) 默认的扩容方式:扩容为原来容量的2倍 并将原有的数据复制过来
jdk8相较于jdk7在底层实现方面的不同
1.new HashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[],而非Entry[]
3.首次调用put()方法时 底层创建长度为16的数组
4.jdk7的底层结构只有 数组+链表 jdk8中的底层结构 数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储
LinkedHashMap的底层实现原理(了解一下就行)
Map中的常用方法
import org.junit.Test;
import java.util.*;
public class MapMethodTest {
@Test
public void test(){
HashMap hashMap = new HashMap();
HashMap hashMap1 = new HashMap();
//添加
hashMap.put(1,1);
hashMap.put('Z','K');
hashMap.put("tian","jiawen");
System.out.println(hashMap.entrySet());
//修改
hashMap.put(1,3);
System.out.println(hashMap.entrySet());
hashMap1.put(2,2);
hashMap1.put('T','J');
hashMap1.put("zhang","ke");
hashMap.putAll(hashMap1);
System.out.println(hashMap);
hashMap.remove(1);
System.out.println(hashMap);
hashMap.clear();
System.out.println(hashMap);
System.out.println(hashMap1.get(2));
System.out.println(hashMap1.containsKey(2));
System.out.println(hashMap1.containsValue('J'));
System.out.println(hashMap1.size());
System.out.println(hashMap1.isEmpty());
System.out.println(hashMap1.equals(hashMap));
//遍历所有的key集:keyset()
Set set = hashMap1.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集:values()
Collection values = hashMap1.values();
for (Object obj:values
) {
System.out.println(obj);
}
//遍历所有的key-value:entrySet()
//方式1:
// Set entrySet = hashMap1.entrySet();
// Iterator iterator1 = entrySet.iterator();
// while (iterator1.hasNext()){
//
// Object obj = iterator1.next();
// //entrtySet集合中的元素都是entry
// Map.Entry entry = (Map.Entry) obj;
// System.out.println(entry.getKey()+"--->"+entry.getValue());
// }
//方式2:
Set keySet = hashMap1.keySet();
Iterator iterator1 = keySet.iterator();
while (iterator1.hasNext()){
Object key = iterator1.next();
Object value = hashMap1.get(key);
System.out.println(key+"--->"+value);
}
//这样的遍历是有意义的
System.out.println(hashMap1.values());
System.out.println(hashMap1.keySet());
System.out.println(hashMap1.entrySet());
//这三个方式 只能看到结果 取不出 key和value但是在开发中意义不大
}
}
TreeMapTest
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要按照key进行自然排序、定制排序
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class mainTest {
public static void main(String[] args) {
MyDate myDate1 = new MyDate(1996, 9, 19);
MyDate myDate2 = new MyDate(1997, 8, 12);
MyDate myDate3 = new MyDate(1998, 7, 11);
MyDate myDate4 = new MyDate(1999, 6, 13);
MyDate myDate5 = new MyDate(2000, 5, 14);
Employee employee1 = new Employee("dzk1",25,myDate1);
Employee employee2 = new Employee("czk2",24,myDate2);
Employee employee3 = new Employee("azk3",23,myDate3);
Employee employee4 = new Employee("bzk4",22,myDate4);
Employee employee5 = new Employee("fzk5",21,myDate5);
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Employee && o2 instanceof Employee){
Employee p1 = (Employee) o1;
Employee p2 = (Employee) o2;
MyDate d1 = p1.getBirthday();
MyDate d2 = p2.getBirthday();
int y = d1.getYear()-d2.getYear();
if(y!=0){
return y;
}
int m = d1.getMonth()-d2.getMonth();
if(m!=0){
return m;
}
return d1.getDay()- d2.getDay();
}
throw new RuntimeException("No");
}
};
//定制排序
TreeSet treeSet1 = new TreeSet(com);
treeSet1.add(employee1);
treeSet1.add(employee2);
treeSet1.add(employee3);
treeSet1.add(employee4);
treeSet1.add(employee5);
Iterator iterator = treeSet1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("__________________________");
//自然排序
TreeSet treeSet2 = new TreeSet();
treeSet2.add(employee1);
treeSet2.add(employee2);
treeSet2.add(employee3);
treeSet2.add(employee4);
treeSet2.add(employee5);
Iterator iterator2 = treeSet2.iterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
Properties处理数据:
·Properties类是Hashtable的子类 该对象用于处理属性文件
·由于属性文件里的key、value都是字符串累心 所以Properties里的key和value都是字符串类型
·存取数据时,建议采用setProperty(String key,String value)方法和getProperty(String key)方法
代码:
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
@Test
public void test() {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);
String name = pros.getProperty("name");
String passWord = pros.getProperty("pasWd");
System.out.println(name + "," + passWord);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
jdbc.properties
name=ZHANGKE张柯
pasWd=1234
Collections工具类 ---->操作数组的工具类:Arrays
·Collections是一个操作Set、List、Map等集合的工具类
·Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象 实现同步控制等方法
·排序方法:(均为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):返回指定集合中指定元素的出现次数
11.void copy(List dest.List src):将src中的内容复制到dest中
12.boolean replaceAII(List list,Object oldVal, Object newVal):使用新值替换
List对象的所有旧值
面试题:Collection和Collections的区别?
·Collections是一个操作Set、List、Map等集合的工具类
·Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象 实现同步控制等方法
·Collection接口:单例数据、定义了存取一组对象的方法的集合,用来存储一个一个的对象
①List:元素有序、可重复的集合--->"动态数组"
ArrayList、LinkedList、Vector
②Set:元素无序、不可重复的集合--->高中的"集合"概念
HashSet、LinkedHashSet、TreeSet
import org.junit.Test;
import java.util.*;
public class CollectionsTest {
@Test
public void test(){
List list = new ArrayList();
list.add(1);
list.add(-1);
list.add(13);
list.add(12);
list.add(21);
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.shuffle(list);
System.out.println(list);
Collections.sort(list);
System.out.println(list);//调用Integer里的排序方法
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Integer && o2 instanceof Integer){
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2-i1;
}else {
throw new RuntimeException("No!");
}
}
};
Collections.sort(list,com);
System.out.println(list);
Collections.swap(list,0,2);
System.out.println(list);
int frequency = Collections.frequency(list, 1);
System.out.println(frequency);
//错误写法 因为list2的size()不确定
// List list2 = new ArrayList();
// Collections.copy(list2,list);
List list2 = Arrays.asList(new Object[list.size()]);
Collections.copy(list2,list);
System.out.println(list2);
}
}
Collections常用方法:同步控制
List list1 = Collections.synchronizedList(list);//返回的list1 就是线程安全的
练习:
1.请从键盘随机输入10个整数保存到List中,并按倒序、从大到小的顺序显示出来
import org.junit.Test;
import java.util.*;
public class listTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
List list = new ArrayList();
for (int i = 0; i < 10; i++) {
list.add(sc.nextInt());
}
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Integer && o1 instanceof Integer) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return Integer.compare(i2, i1);
} else {
throw new RuntimeException("NO");
}
}
};
System.out.println(list);
Collections.reverse(list);//反转
System.out.println(list);
Collections.sort(list,com);//从大到小
System.out.println(list);
}
}
2.请把学生名与考试分数录入到集合中,并按照分数显示前三名成绩学员的名字
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class main1Test {
public static void main(String[] args) {
Student s1 = new Student("ZK4", 96);
Student s2 = new Student("ZK3", 100);
Student s3 = new Student("ZK2", 99);
Student s4 = new Student("ZK5", 97);
Student s5 = new Student("ZK1", 99);
List list = new ArrayList();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
Collections.sort(list);//要先排序 再定义iterator
Iterator iterator = list.iterator();
int count = 0;
while(count<3 && iterator.hasNext()){
System.out.println(iterator.next());
count++;
}
}
}
public class Student implements Comparable {
private String name;
private int score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public Student() {
}
@Override
public int compareTo(Object o) {
if(o instanceof Student){
Student s = (Student) o;
return -this.score+s.score;
}else{
throw new RuntimeException("NO!");
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
3.姓氏统计:一个文本文件中存储着北京所有高校在校生的姓名,格式如下:
每行一个名字,姓与名以空格分隔:
张 三
李 四
....
现在想统计所有姓氏在文件中出现的次数,请描述一下你的解决方案
4.对一个Java源文件中的关键字进行计数
提示:Java源文件中的每一个单词,需要确定该单词是否是一个关键字.为了高效处理这个问题,将所有的关键字保存在一个HashSet中.用contains()来测试
File file = new File("Test.java");
Scanner sc = new Scanner(file);
while(sc.hasNext()){
String word = sc.next();
System.out.print(word);
}
Enumeration
面试题: 负载因子值的大小 对HashMap有什么影响
高频面试题:
ArrayList和LinkedList的比较