string
- 构造方法每创建一次对象,都会在堆内存中开辟不同的对象空间。所以,每new一次,都是不同的对象。
- 直接定义的字符串内容会存储到堆内存中的常量池里。字符串内容如果相同,则直接共享使用。
字符串的比较
- ==号如果比较的是基本数据类型,比较的是具体的值,==号如果比较的是引用数据类型,比较的是对象的地址值
- 比较字符串内容是否相同,使用equals()方法
字符串的比较
- 记住俩个重要方法:length(),charAt(); 用for循环实现
StringBuilder
- 每拼接一个字符串,都会产生新的字符串对象。这种操作会比较浪费内存空间!StringBuilder来解决这个问题
- Stringbuilder是可变的字符串对象
1 public static String arryToString(int[] array){
2 StringBuilder stringBuilder = new StringBuilder();
3
4 stringBuilder.append("[");
5
6 for (int i = 0; i < array.length; i++) {
7 if(i==array.length-1){
8 stringBuilder.append(array[i]);
9 }else {
10 stringBuilder.append(array[i]).append(",");
11 }
12 }
13
14 stringBuilder.append("]");
15
16 String s = stringBuilder.toString();
17 return s;
18 }
String&StringBuffer&StringBuilder
- String:不可变字符序列,底层使用char[]存储。
- StringBuffer:可变的字符序列,线程安全的,效率低,底层使用char[]存储
- StringBuilder:可变字符串序列,线程不安全的,效率高,底层使用char[]存储。
Collection
- JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如Set和List 。
- 用多态的方式创建collection对象。(具体实现类ArrayList)
- collection用迭代器遍历,迭代器是一个内部类。
- 迭代器创建的时候指向的不是第一个元素,而是第一个元素的上方空元素。判断是否有下一个元素是相对于当前迭代器指向的地方而言的。
- 集合存储的是引用数据类型。不能存储基本数据类型。
List
- 有序:存储和取出的是顺序一致。
- 可重复:存储的元素可以重复。
- 并发修改异常 ConcurrentModificationException
1//并发修改异常 ConcurrentModificationException
2 List<String> list = new ArrayList<String>();
3 list.add("hello");
4 list.add("word");
5
6 Iterator<String> iterator = list.iterator();
7
8 while (iterator.hasNext()){
9 String s = iterator.next();
10 if(s.equals("word")){
11 list.add("JavaSE");
12 }
13 }
14
15 System.out.println(list);
16 }
17
18
19Exception in thread "main" java.util.ConcurrentModificationException
20 at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
21 at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
22 at com.Gao.ArrayList.List_Demo.main(List_Demo.java:22)
- 分析:源码分析之后可以看到迭代器遍历过程中,丢改了集合的长度,导致实际修改次数和预期修改次数不一致,从而抛出了异常
- 解决方案: 在if之后家break跳出循环,防止执行checkForComodification();或者用for循环的方式。
- 数组是查询快,增删慢的数据结构,链表是查询慢,增删快的数据结构。
list的常用子类ArrayList&linkedList
- ArrayList底层是数组
- linkedList底层是链表
- 3种遍历方式 1.迭代器 2.for循环遍历 3. for增强遍历
linkedList集合特有功能
- 因为是linkedList特有功能,所以不能用多态的当时创建(父类不能调用子类的方法)
- addFirst()
- getFirst()
- removeFirst()
set集合
HashSet
- 不包含重复元素
- HashSet该类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代顺序不作任何保证; 特别是,它不能保证订单在一段时间内保持不变。 这个类允许null元素。
- 底层数据结构是哈希表
哈希值
- JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- Object类中HashCode()方法可以获取对象的哈希值。
- 同一个对象的哈希值是一样的。
- 默认情况下不同对象的哈希值是不同的。(重写hashCode方法可以实现让不同字符串的哈希值相同)
保证元素唯一性的源码分析
1public boolean add(E e) {
2 return map.put(e, PRESENT)==null;
3 }
4
5 static final int hash(Object key) {
6 int h;
7 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
8 }
9
10 public V put(K key, V value) {
11 return putVal(hash(key), key, value, false, true);
12 }
13 // hash值和元素的hashCode()相关
14 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
15 boolean evict) {
16 Node<K,V>[] tab; Node<K,V> p; int n, i;
17 //如果哈希表未初始化,就对其进行初始化
18 if ((tab = table) == null || (n = tab.length) == 0)
19 n = (tab = resize()).length;
20
21 //根据对象的哈希值计算对象的存储位置,如果该位置没有元素就存储元素
22 if ((p = tab[i = (n - 1) & hash]) == null)
23 tab[i] = newNode(hash, key, value, null);
24 else {
25 Node<K,V> e; K k;
26 /*
27 存入的元素和以前的元素比较哈希值
28 如果哈希值不同,会继续向下执行,把元素添加进去,
29 如果哈希值相同,会调用equals方法
30 返回true,元素重复不存储
31 返回false 则存储
32
33 */
34
35 if (p.hash == hash &&
36 ((k = p.key) == key || (key != null && key.equals(k))))
37 e = p;
38 else if (p instanceof TreeNode)
39 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
40 else {
41 for (int binCount = 0; ; ++binCount) {
42 if ((e = p.next) == null) {
43 p.next = newNode(hash, key, value, null);
44 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
45 treeifyBin(tab, hash);
46 break;
47 }
48 if (e.hash == hash &&
49 ((k = e.key) == key || (key != null && key.equals(k))))
50 break;
51 p = e;
52 }
53 }
54 if (e != null) { // existing mapping for key
55 V oldValue = e.value;
56 if (!onlyIfAbsent || oldValue == null)
57 e.value = value;
58 afterNodeAccess(e);
59 return oldValue;
60 }
61 }
62 ++modCount;
63 if (++size > threshold)
64 resize();
65 afterNodeInsertion(evict);
66 return null;
67 }
哈希表保证元素唯一性实现方法
- 计算出带存入对象的哈希值
- 将哈希值对象对16求余。
- 分别存入0-15的地址
- 若地址一样,比较哈希值和内容,如果都不一样,则用链表插入该元素的后面。
LinkedHashSet
- 底层实现是哈希表和链表
- 由链表保证元素顺序,存入和取出的顺序一致。
- 由哈希表保证元素的唯一
TreeSet
- 元素有序(不是存取顺序),按照一定的规则进行排序,具体排序方法取决于构造方法。
- 没带索引,不能用普通for方法遍历,需要用增强的for方法。
- 由于是set集合所以元素没有重复的
- 用treeset存储自定义对象时,该对象的类要实现Comparable<>(自然排序)接口。
1 @Override
2 public int compareTo(Student o) {
3 int num = this.age-o.age;
4 num = (num == 0) ? this.name.compareTo(o.name) : num;
5 return (num);
6 }
- 比较器comparator的使用
1 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
2 @Override
3 public int compare(Student o1, Student o2) {
4 int num = o1.getAge()-o2.getAge();
5 num = (num==0)?o1.getName().compareTo(o2.getName()):num;
6 return num;
7 }
8 });
- 比较器排序就是让集合构造方法接受比较器的实现类对象,重写compare方法。
- 不重复的随机数
1 Random random = new Random();
2 TreeSet<Integer> set = new TreeSet<Integer>();
3 while (set.size()<10){
4 set.add(random.nextInt(20)+1);
5 }
6
7 for (Integer integer : set) {
8 System.out.println(integer);
9 }
泛型
- 提供了编译时类型安全检测机制,本质时将类型参数化。
- 优点:将运行时期的异常提前到了编译期间,避免了强制类型转换
- 指定一种类型的格式,这个类型可以看成时形参
- 将来具体调用的时候给定的类型可以看成时实参,并且实参的类型只能是引用类型。
泛型方法
1 public <T> void show(T t){
2 System.out.println(t);
3 }
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:
List: 表示元素类型未知的List,它的元素可以匹配任何的类型 - 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List是任何泛型ist的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限 - 类型通配符 上限:
List:它表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限 - 类型通配符下限:
List:它表示的类型是Number或者其父类型
Map集合
- 将键映射到值的对象。 Map不能包含重复的键; 每个键可以映射到最多一个值。
- HashMap<>(),的key和value的类型,不能是基本数据类型,需要时引用类型,所以要是基本类型的包装类,int-->Integer,char---->Character等等。
俩种遍历方式
1//遍历map方式1
2 Set<String> keySet = map.keySet();
3 for (String s : keySet) {
4 System.out.println(s+":"+map.get(s));
5 }
6
7 //遍历map方式2
8 Set<Map.Entry<String, String>> entrySet = map.entrySet();
9 for (Map.Entry<String, String> mapEntry : entrySet) {
10 //mapEntry是每一个map对象
11 System.out.println(mapEntry.getKey()+"-->"+mapEntry.getValue());
12 }