基础总结–String和集合
一、关于String类的使用
字符创广泛应用在java编程中,在java中字符串属于对象,java提供了String类来创建和操作字符
- 创建字符串:
String greeting="这是一个字符串";
//在代码中遇到字符串常量时,这里值是“这是一个字符串”,编译器会使用该值创建一个String对象
-
注意:String类是不可以改变的,多以一旦创建了String对象,那么它的值就无法改变—如需修改则可以使用StringBuffer或者StringBuilder
-
字符串的长度:
-
String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数。
-
public class StringDemo { public static void main(String args[]) { String site = "www.haohaoxuexi.com"; int len = site.length(); System.out.println( "网址长度 : " + len ); } }
-
-
连接字符串:
-
String提供了两种方法连接两个字符串:
-
//1、一般使用+操作符来连接字符串 "我的名字是"+"Runoob"
-
//2、string1.concat(string2); "我的名字是 ".concat("Runoob");
-
-
创建格式化字符串:
-
我们知道输出格式化数字可以使用 printf() 和 format() 方法。
String 类使用静态方法 format() 返回一个String 对象而不是 PrintStream 对象。
String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。
-
System.out.printf("浮点型变量的值为 " + "%f, 整型变量的值为 " + " %d, 字符串变量的值为 " + "is %s", floatVar, intVar, stringVar);
-
StringBuffer和StringBuilder
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
public class Test{
public static void main(String args[]){
StringBuffer sBuffer = new StringBuffer("网址:");
sBuffer.append("www");
sBuffer.append(".haohaoxuexi");
sBuffer.append(".com");
System.out.println(sBuffer);
}
}
-
三者异同:
-
String:不可变的字符序列,底层使用char[]存储。final修饰;
-
//源码分析 String str=new String();//new char[0]; String str=new String("abc");//new char[]{'a','b','c'}
-
-
StringBuffer:可变的字符序列,线程安全,但是效率低下,底层使用char[]存储
-
//源码分析 StringBuffer sb1=new StringBuffer();//char[] value=new char[16];底层创建了一个长度是16的数组 sb1.append('a');//value[0]='a'; sb1.append('b');//value[1]='b'; -------------------------------- StringBuffer sb2=new StringBuffer("abc"); //char[] value=new char["abc".length()+16];
-
扩容问题:如果要添加的数据底层数组盛不下,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍+2,同时将原有的数组中的元素复制到新的数组中。
-
开发中建议大家使用:StringBuffer(int capacity),指定数组的长度
-
-
StringBuilder:可变的字符序列,jdk5.0新增的,线程不安全,效率高,底层使用char[]存储
-
三者运行效率:StringBuilder > StringBuffe r > String
-
二、集合和数组
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
一、集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:
- 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
- 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
- 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
二、集合和数组的区别:
三、常用集合分类:
1、Collection接口
无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的
不可重复性:保证添加的元素按照equals()方法判断时,不能返回true,即:相同元素只能添加一个
Set接口
-
实例存储的是无序的,不重复的数据
-
检索效率低下,删除和插入效率高
-
插入和删除不会引起元素位置的变化
-
Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
-
HashSet:主要实现类
- 实现了Set集合,实际为一个HashMap的实例,底层的数据结构采用哈希表
- 元素无序且唯一
- 线程不安全,效率高
- 允许null值
- 对集合的迭代次序没有任何保证
-
TreeSet:
- 不能有重复的元素,有序的集合,它的作用是提供有序的Set集合
- 向TreeSet中添加的数据,要求是相同类的对象
- TreeSet的底层实际使用的存储容器就是TreeMap,底层数据结构采用二叉树(红黑树)
- 元素唯一且已经排好序
- TreeSet中的元素必须实现Comparable接口并重写compareTo()方法,TreeSet判断元素是否重复 、以及确定元素的顺序靠的都是这个方法
-
LinkedHashSet:HashSet的子类
- 集合不重复,同时具有可预测的迭代顺序
- 一个非线程安全的集合
- 底层是链表和哈希表共同实现的,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性
- 是set集合中唯一一个能保证怎么存就怎么取的集合对象、
List接口
-
实例存储的是有序的,可以重复的元素
-
List和数组类似,可以动态的增长,根据实际存储的数据的长度自动增长List的长度
-
查找元素效率高,插入删除效率低,因为会引起其他元素位置的改变
-
LinkedList:
-
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
-
LinkedList底层使用双向链表存储。
-
线程不安全,效率高
-
查询慢,增删快
-
链表可分为单向链表和双向链表。
-
LinkedList 继承了 AbstractSequentialList 类。
-
LinkedList 实现了 Queue 接口,可作为队列使用。
-
LinkedList 实现了 List 接口,可进行列表的相关操作。
-
LinkedList 实现了 Deque 接口,可作为队列使用。
-
LinkedList 实现了 Cloneable 接口,可实现克隆。
-
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
-
public class Test { public static void main(String[] args) { LinkedList<String> sites = new LinkedList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); System.out.println(sites); // 使用 addFirst() 在头部添加元素 sites.addFirst("Wiki"); System.out.println(sites); // 使用 addLast() 在尾部添加元素 sites.addLast("Wiki"); System.out.println(sites); // 使用 removeFirst() 移除头部元素 sites.removeFirst(); System.out.println(sites); // 使用 getFirst() 获取头部元素 System.out.println(sites.getFirst()); } }
-
-
ArrayList:
-
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素,底层使用Object[]存储。
-
查询快,增删慢
-
线程不安全,效率高
-
默认情况下:扩容为原来的1.5倍,同时需要将原有的数组复制到新的数组中
-
ArrayList 继承了 AbstractList ,并实现了 List 接口。
-
ArrayList<E> objectName =new ArrayList<>(); // 初始化 //E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。 //objectName: 对象名。
-
ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
-
public class Test { public static void main(String[] args) { ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); System.out.println(sites); System.out.println(sites.get(1)); // sites.set(2, "Wiki"); // 第一个参数为索引位置,第二个为要修改的值 System.out.println(sites); sites.remove(3); // 删除第四个元素 System.out.println(sites); System.out.println(sites.size());//计算元素数量 //迭代数组数列 for (int i = 0; i < sites.size(); i++) { System.out.println(sites.get(i)); } }
-
-
Vector:
-
Vector 类实现了一个动态数组。和 ArrayList 很相似
-
Vector是线程安全的,但是效率低
-
查询快,增删慢
-
底层创建了长度为10的数组,在扩容方面,默认扩容为原来的数组长度的2倍
-
Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况
-
//第一种构造方法创建一个默认的向量,默认大小为 10: Vector() //第二种构造方法创建指定大小的向量。 Vector(int size) //第三种构造方法创建指定大小的向量,并且增量用 incr 指定。增量表示向量每次增加的元素数目。 Vector(int size,int incr) //第四种构造方法创建一个包含集合 c 元素的向量: Vector(Collection c)
-
2、Map:
Map的结构理解:
-
Map中的key:无序的,不可重复的,使用Set存储所有的key
-
Map中的value:无序的,可重复的,使用collection存储所有的value
-
一个键值对:kry-value构成一个Entry对象
-
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
-
遍历Map:
通过:keySet、entrySet方法
-
-
public class Test { public static void main(String[] args) { Map<Integer,String> map=new HashMap<>(); map.put(1,"dashan"); map.put(2,"2343"); map.put(3,"sdsaa"); Set k=map.keySet(); Set e=map.entrySet(); Collection<String> c=map.values(); System.out.println(c); System.out.println(e); Iterator iterator = e.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
-
HashMap:性能最好
-
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
-
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,但是可以有无数个value值为null
-
线程不安全,是异步的
-
HashMap 是无序的,即不会记录插入的顺序。
-
HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。
-
扩容:是新建了一个HashMap的底层数组,而后调用transfer方法,将就HashMap的全部元素添加到新的HashMap中(要重新计算元素在新的数组中的索引位置),比较耗时的操作。
-
public class RunoobTest { public static void main(String[] args) { // 创建 HashMap 对象 Sites HashMap<Integer, String> Sites = new HashMap<Integer, String>(); // 添加键值对 Sites.put(1, "Google"); Sites.put(2, "Runoob"); Sites.put(3, "Taobao"); Sites.put(4, "Zhihu"); System.out.println(Sites); //迭代HashMap for (Integer i : Sites.keySet()) { System.out.println("key: " + i + " value: " + Sites.get(i)); //使用 get(key) 方法来获取 key 对应的 value: System.out.println(Sites.get(3)); //使用 remove(key) 方法来删除 key 对应的键值对(key-value) Sites.remove(4); //删除所有键值对(key-value)可以使用 clear 方法: Sites.clear(); } }
-
LinkedHashMap:
- 有序的,且默认为插入顺序
- LinkedHashMap就是HashMap+双向链表
- 迭代顺序与键值对的插入顺序一样
-
ConcurrentHashMap:
- 线程安全,推荐使用
- 线程安全是通过cas+synchronized+volatile来实现的
- 锁是分段锁,所以它的性能相对来说是比较好的
HashTable:性能较差
- 是同步的,加入了synchronized关键字,保证了线程安全,支持序列化
- 基于哈希表实现,内部通过单链表解决
- 不可以放入空值
TreeMap
- 基于红黑树(是一种自平衡二叉查找树)
- 线程不安全
- 元素默认按照keys的自然排序排列