JAVA集合问答

HashMap 的扩容过程

当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组的长度乘以加载因子的值的时候,就要自动扩容啦。

扩容( resize )就是重新计算容量,向 HashMap 对象里不停的添加元素,而 HashMap 对象内部的数组无法装载更 多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。

当然 Java 里的数组是无法自动扩容的,方法 是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。

HashMap hashMap=new HashMap(cap);

cap =3, hashMap 的容量为4;

cap =4, hashMap 的容量为4;

cap =5, hashMap 的容量为8;

cap =9, hashMap 的容量为16;

如果 cap 是2的n次方,则容量为 cap ,否则为大于 cap 的第一个2的n次方的数。

数组扩容的过程?

创建一个新的数组,其容量为旧数组的两倍,并重新计算旧数组中结点的存储位置。结点在新数组中的位置只有两种,原下标位置或原下标+旧数组的大小。

对红黑树的见解?
  • 1、每个节点非红即黑

  • 2、根节点总是黑色的

  • 3、如果节点是红色的,则它的子节点必须是黑色的(反之不一定)

  • 4、每个叶子节点都是黑色的空节点(NIL节点)

  • 5、从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)

jdk8中对HashMap做了哪些改变?
  • 在java 1.8中,如果链表的长度超过了8,那么链表将转换为红黑树。(桶的数量必须大于64,小于64的时候只会扩容)

  • 发生hash碰撞时,java 1.7 会在链表的头部插入,而java 1.8会在链表的尾部插入

  • 在java 1.8中,Entry被Node替代(换了一个马甲)。

HashMap,LinkedHashMap,TreeMap 有什么区别?

HashMap 参考其他问题;

LinkedHashMap 保存了记录的插入顺序,在用 Iterator 遍历时,先取到的记录肯定是先插入的;遍历比 HashMap 慢;

TreeMap 实现 SortMap 接口,能够把它保存的记录根据键排序(默认按键值升序排序,也可以指定排序的比较器)

HashMap & TreeMap & LinkedHashMap 使用场景?

一般情况下,使用最多的是 HashMap。

HashMap:在 Map 中插入、删除和定位元素时;

TreeMap:在需要按自然顺序或自定义顺序遍历键的情况下;

LinkedHashMap:在需要输出的顺序和输入的顺序相同的情况下。

HashMap 和 HashTable 有什么区别?

①、HashMap 是线程不安全的,HashTable 是线程安全的;

②、由于线程安全,所以 HashTable 的效率比不上 HashMap;

③、HashMap最多只允许一条记录的键为null,允许多条记录的值为null,而 HashTable不允许;

④、HashMap 默认初始化数组的大小为16,HashTable 为 11,前者扩容时,扩大两倍,后者扩大两倍+1;

⑤、HashMap 需要重新计算 hash 值,而 HashTable 直接使用对象的 hashCode

Java 中的另一个线程安全的与 HashMap 极其类似的类是什么?同样是线程安全,它与 HashTable 在线程同步上有什么不同?

ConcurrentHashMap 类(是 Java并发包 java.util.concurrent 中提供的一个线程安全且高效的 HashMap 实现)。

HashTable 是使用 synchronize 关键字加锁的原理(就是对对象加锁);

而针对 ConcurrentHashMap,在 JDK 1.7 中采用 分段锁的方式;JDK 1.8 中直接采用了CAS(无锁算法)+ synchronized。

HashMap & ConcurrentHashMap 的区别?

除了加锁,原理上无太大区别。另外,HashMap 的键值对允许有null,但是ConCurrentHashMap 都不允许。

为什么 ConcurrentHashMap 比 HashTable 效率要高?

HashTable 使用一把锁(锁住整个链表结构)处理并发问题,多个线程竞争一把锁,容易阻塞;

ConcurrentHashMap

  • JDK 1.7 中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。锁粒度:基于 Segment,包含多个 HashEntry。

  • JDK 1.8 中使用 CAS + synchronized + Node + 红黑树。锁粒度:Node(首结点)(实现 Map.Entry<K,V>)。锁粒度降低了。

ConcurrentHashMap 在 JDK 1.8 中,为什么要使用内置锁 synchronized 来代替重入锁 ReentrantLock?

①、粒度降低了;

②、JVM 开发团队没有放弃 synchronized,而且基于 JVM 的 synchronized 优化空间更大,更加自然。

③、在大量的数据操作下,对于 JVM 的内存压力,基于 API 的 ReentrantLock 会开销更多的内存。

ConcurrentHashMap 简单介绍?

①、重要的常量:

private transient volatile int sizeCtl;

当为负数时,-1 表示正在初始化,-N 表示 N - 1 个线程正在进行扩容;

当为 0 时,表示 table 还没有初始化;

当为其他正数时,表示初始化或者下一次进行扩容的大小。

②、数据结构:

Node 是存储结构的基本单元,继承 HashMap 中的 Entry,用于存储数据;

TreeNode 继承 Node,但是数据结构换成了二叉树结构,是红黑树的存储结构,用于红黑树中存储数据;

TreeBin 是封装 TreeNode 的容器,提供转换红黑树的一些条件和锁的控制。

③、存储对象时(put() 方法):

1.如果没有初始化,就调用 initTable() 方法来进行初始化;

2.如果没有 hash 冲突就直接 CAS 无锁插入;

3.如果需要扩容,就先进行扩容;

4.如果存在 hash 冲突,就加锁来保证线程安全,两种情况:一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入;

5.如果该链表的数量大于阀值 8,就要先转换成红黑树的结构,break 再一次进入循环

6.如果添加成功就调用 addCount() 方法统计 size,并且检查是否需要扩容。

④、扩容方法 transfer():默认容量为 16,扩容时,容量变为原来的两倍。

helpTransfer():调用多个工作线程一起帮助进行扩容,这样的效率就会更高。

⑤、获取对象时(get()方法):

1.计算 hash 值,定位到该 table 索引位置,如果是首结点符合就返回;

2.如果遇到扩容时,会调用标记正在扩容结点 ForwardingNode.find()方法,查找该结点,匹配就返回;

3.以上都不符合的话,就往下遍历结点,匹配就返回,否则最后就返回 null。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Lucene 是一个开源的全文检索引擎,可以用来构建高效的搜索应用程序。在 Java 中使用 Lucene 构建问答引擎可以实现以下功能: 1. 对问题进行分词和索引,以便快速定位相关答案。 2. 对答案进行分词和索引,并使用相关性算法来计算答案与问题的相似度。 3. 提供自然语言查询接口,让用户可以输入问题并获取相关答案。 4. 支持多种数据源,如数据库、文件等。 下面是一个简单的 Lucene 问答引擎示例: 1. 首先需要创建一个问答索引,包括问题、答案和其他相关信息: ```java public class QaIndexer { private Directory directory; private Analyzer analyzer; private IndexWriterConfig config; private IndexWriter writer; public QaIndexer(Path indexPath) throws IOException { directory = FSDirectory.open(indexPath); analyzer = new StandardAnalyzer(); config = new IndexWriterConfig(analyzer); writer = new IndexWriter(directory, config); } public void indexQuestion(String question, String answer, String category) throws IOException { Document doc = new Document(); doc.add(new TextField("question", question, Field.Store.YES)); doc.add(new TextField("answer", answer, Field.Store.YES)); doc.add(new StringField("category", category, Field.Store.YES)); writer.addDocument(doc); } public void commit() throws IOException { writer.commit(); } public void close() throws IOException { writer.close(); directory.close(); } } ``` 2. 然后创建一个搜索器,用于搜索问题并返回相关答案: ```java public class QaSearcher { private Directory directory; private IndexReader reader; private IndexSearcher searcher; public QaSearcher(Path indexPath) throws IOException { directory = FSDirectory.open(indexPath); reader = DirectoryReader.open(directory); searcher = new IndexSearcher(reader); } public List<String> search(String query, int numHits) throws IOException { QueryParser parser = new QueryParser("question", new StandardAnalyzer()); Query q = parser.parse(query); TopDocs docs = searcher.search(q, numHits); List<String> results = new ArrayList<>(); for (ScoreDoc scoreDoc : docs.scoreDocs) { Document doc = searcher.doc(scoreDoc.doc); String answer = doc.get("answer"); results.add(answer); } return results; } public void close() throws IOException { reader.close(); directory.close(); } } ``` 3. 最后,将索引和搜索器结合起来,实现一个简单的问答引擎: ```java public class QaEngine { private QaIndexer indexer; private QaSearcher searcher; public QaEngine(Path indexPath) throws IOException { indexer = new QaIndexer(indexPath); searcher = new QaSearcher(indexPath); } public void indexQuestion(String question, String answer, String category) throws IOException { indexer.indexQuestion(question, answer, category); } public List<String> search(String query, int numHits) throws IOException { return searcher.search(query, numHits); } public void commit() throws IOException { indexer.commit(); } public void close() throws IOException { indexer.close(); searcher.close(); } } ``` 这个问答引擎可以用来回答用户输入的问题,例如: ```java QaEngine engine = new QaEngine(Paths.get("index")); engine.indexQuestion("What is Lucene?", "Lucene is a full-text search engine library written in Java", "technology"); engine.commit(); List<String> results = engine.search("What is Lucene?", 10); for (String result : results) { System.out.println(result); } engine.close(); ``` 输出: ``` Lucene is a full-text search engine library written in Java ``` ### 回答2: Java Lucene问答引擎是一个使用Java编写的,基于Lucene搜索引擎的问答系统案例。该系统旨在通过自动化处理问题和提供相关答案来帮助用户解决问题。 该问答引擎案例的实现过程如下: 1. 数据收集:首先,系统需要收集大量的问题和答案数据。可以从已有的问答社区、论坛或其他数据源中获取问题和对应的答案。 2. 数据预处理:收集到数据后,需要对其进行预处理。这包括文本清洗、分词和词干提取等处理,以便为后续的检索和匹配做准备。 3. 索引构建:使用Lucene的倒排索引技术,将问题和答案数据构建成一个索引库。这将提高后续的搜索效率。 4. 问题解析:当用户提出一个问题时,系统需要对该问题进行解析,以确定用户的意图和需要的答案类型。这可以通过自然语言处理技术,如词性标注、依存句法分析等来实现。 5. 答案检索:通过用户提出的问题,使用索引库进行问题匹配和检索,找出与问题最相关的答案。这可以使用Lucene提供的查询和检索功能来实现。 6. 答案排序:根据答案的相关性和质量,对检索到的答案进行排序,并选择最佳的答案作为结果返回给用户。排序可以使用相关性算法,如TF-IDF、BM25等来实现。 7. 用户界面:为用户提供一个友好的界面,让用户输入问题并显示搜索结果。这可以通过Web界面或命令行界面来实现。 通过以上步骤,Java Lucene问答引擎可以实现自动化的问题回答功能。用户可以输入问题,系统可以自动解析用户意图并给出相关答案。这在知识库问答、智能客服、语义搜索等领域都有广泛的应用前景。 ### 回答3: Java Lucene问答引擎是一个基于Java语言和Lucene搜索引擎的案例,旨在提供用户使用自然语言进行询问并获得准确答案的功能。 该问答引擎的实现过程主要包括以下几个步骤: 1. 数据准备:首先,需要准备一个问题和答案的数据集,一般可以使用已有的知识库或者文档集合,将其进行预处理和索引构建,以便于快速检索。 2. 分析和预处理:使用Lucene的分词器对问题和答案进行分词处理,并进行相关的预处理操作,如去除停用词、词干化等,以便于能够更好地匹配和搜索。 3. 构建索引:使用Lucene的索引功能,将预处理后的问题和答案构建成索引结构,以便于后续的查询和匹配操作。 4. 用户查询:用户通过输入自然语言的问题,问答引擎将对其进行分析、预处理,并根据索引结构进行查询。 5. 匹配和排序:根据用户查询的关键词和索引中的问题进行匹配,使用相应的算法对匹配结果进行排序,以便于展示最为相关和准确的答案。 6. 结果展示:将排序后的答案进行展示,并提供相应的界面和交互形式,以便于用户能够直观地获得问题的答案。 Java Lucene问答引擎案例的实现可以帮助用户更方便地获取问题的答案,尤其在一些知识库比较庞大和复杂的情况下,能够大大提高问题解答的效率和准确性。同时,可以根据具体的需求和业务场景对该引擎进行定制和扩展,以适应不同的功能和应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值