Optimize insertion from ArrayList to HashMap

huangapple go评论103阅读模式
英文:

Optimize insertion from ArrayList to HashMap

问题

I'm trying to insert data from ArrayList<Item> to HashMap<String, Language> optimally.

尝试将ArrayList<Item>中的数据最优地插入到HashMap<String, Language>中。

Many items may have the same language_name (code below), so I need to group items having the same language in Language class and store languages in a HashMap with the name of the language as a Key.

许多项可能具有相同的language_name(下面是代码),因此我需要将具有相同语言的项目分组到Language类中,并将语言存储在一个HashMap中,其中语言的名称作为键。

Item

  1. String name;
  2. String language_name;

Language

  1. String language_name;
  2. int numberItems;
  3. LinkedList&lt;String&gt; Items;

项目

  1. String name;
  2. String language_name;

语言

  1. String language_name;
  2. int numberItems;
  3. LinkedList&lt;String&gt; Items;

I solved this as follows:

我解决了这个问题,如下所示:

  1. ArrayList&lt;Item&gt; items; // given array of items
  2. HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();
  3. items.forEach(item -&gt; {
  4. /** case 1: language isn&#39;t specified */
  5. if (item.getLanguageName() == null) {
  6. item.setLanguageName(&quot;unknown&quot;);
  7. }
  8. /** case 2: language already added */
  9. if (languages.containsKey(item.getLanguageName())) {
  10. languages.get(item.getLanguageName()).getItems().add(item.getName());
  11. languages.get(item.getLanguageName())
  12. .setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
  13. } else {
  14. /** case 3: language isn&#39;t added yet */
  15. LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
  16. languageItems.add(item.getName());
  17. Language language = new Language(item.getLanguageName(), 1, languageItems);
  18. languages.put(item.getLanguageName(), language);
  19. }
  20. });

我如下解决了这个问题:

  1. ArrayList&lt;Item&gt; items; // 给定的项目数组
  2. HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();
  3. items.forEach(item -&gt; {
  4. /** 情况1:语言未指定 */
  5. if (item.getLanguageName() == null) {
  6. item.setLanguageName(&quot;unknown&quot;);
  7. }
  8. /** 情况2:语言已添加 */
  9. if (languages.containsKey(item.getLanguageName())) {
  10. languages.get(item.getLanguageName()).getItems().add(item.getName());
  11. languages.get(item.getLanguageName())
  12. .setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
  13. } else {
  14. /** 情况3:语言尚未添加 */
  15. LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
  16. languageItems.add(item.getName());
  17. Language language = new Language(item.getLanguageName(), 1, languageItems);
  18. languages.put(item.getLanguageName(), language);
  19. }
  20. });

Any help would be appreciated!
任何帮助将不胜感激!

英文:

I'm trying to insert data from ArrayList<Item> to HashMap<String, Language> optimally.

Many items may have the same languge_name (code below), so I need to group items having the same language in Language class and store languages in a HashMap with the name of the language as a Key.

Item

  1. String name;
  2. String language_name;

Language

  1. String language_name;
  2. int numberItems;
  3. LinkedList&lt;String&gt; Items;

I solved this as follows:

  1. ArrayList&lt;Item&gt; items; // given array of items
  2. HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();
  3. items.forEach(item -&gt; {
  4. /** case 1: language isn&#39;t specified */
  5. if (item.getLanguageName() == null) {
  6. item.setLanguageName(&quot;unknown&quot;);
  7. }
  8. /** case 2: language already added */
  9. if (languages.containsKey(item.getLanguageName())) {
  10. languages.get(item.getLanguageName()).getItems().add(item.getName());
  11. languages.get(item.getLanguageName())
  12. .setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
  13. } else {
  14. /** case 3: language isn&#39;t added yet */
  15. LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
  16. languageItems.add(item.getName());
  17. Language language = new Language(item.getLanguageName(), 1, languageItems);
  18. languages.put(item.getLanguageName(), language);
  19. }
  20. });

Any help would be appreciated!

答案1

得分: 1

Assuming you're using Java 8 or later, this can be accomplished nicely with built-in stream functions.

  1. HashMap&lt;String, List&lt;Items&gt;&gt; itemsGroupedByLanguage =
  2. items.stream().collect(Collectors.groupingBy(Items::getLanguage));
英文:

Assuming you're using Java 8 or later, this can be accomplished nicely with built-in stream functions.

  1. HashMap&lt;String, List&lt;Items&gt;&gt; itemsGroupedByLanguage =
  2. items.stream().collect(Collectors.groupingBy(Items::getLanguage));

答案2

得分: 0

tl;dr

使用Java(8+)内置的收集器无法实现您的要求,但您可以编写自定义收集器并编写以下代码来收集到一个映射中 -

  1. Map<String, Language> languages = items.stream().collect(LanguageCollector.toLanguage());

首先让我们看看Collector<T, A, R>接口

  1. public interface Collector<T, A, R> {
  2. // ... 其他方法 ...
  3. }

其中,T是流中要收集的项目的通用类型,A是累加器的类型,它在收集过程中用于累积部分结果,R是收集操作的结果对象的类型(通常是集合)。

现在让我们看看自定义的LanguageCollector

  1. public class LanguageCollector
  2. implements Collector<Item, Map<String, Language>, Map<String, Language>> {
  3. // ... 其他方法和代码 ...
  4. }

运行代码

  1. public static void main(String[] args) {
  2. List<Item> items =
  3. Arrays.asList(
  4. new Item("ItemA", "Java"),
  5. new Item("ItemB", "Python"),
  6. new Item("ItemC", "Java"),
  7. new Item("ItemD", "Ruby"),
  8. new Item("ItemE", "Python"));
  9. Map<String, Language> languages = items.stream().collect(LanguageCollector.toLanguage());
  10. System.out.println(languages);
  11. }

打印结果

  1. {Java=Language(languageName=Java, numberItems=2, items=[ItemA, ItemC]), Ruby=Language(languageName=Ruby, numberItems=1, items=[ItemD]), Python=Language(languageName=Python, numberItems=2, items=[ItemB, ItemE])}

有关更多信息,请阅读书籍'现代Java实战:Lambda、流、函数式和响应式编程'第6.5章或查看此链接

英文:

tl;dr

It's not possible to achieve what you desire using Java (8+) inbuilt collector, but you can write your own custom collector and write code like below to collect into a map as -

Map&lt;String, Language&gt; languages = items.stream().collect(LanguageCollector.toLanguage());

Let's first look at Collector&lt;T, A, R&gt; interface

  1. public interface Collector&lt;T, A, R&gt; {
  2. /**
  3. * A function that creates and returns a new mutable result container.
  4. */
  5. Supplier&lt;A&gt; supplier();
  6. /**
  7. * A function that folds a value into a mutable result container.
  8. */
  9. BiConsumer&lt;A, T&gt; accumulator();
  10. /**
  11. * A function that accepts two partial results and merges them. The
  12. * combiner function may fold state from one argument into the other and
  13. * return that, or may return a new result container.
  14. */
  15. BinaryOperator&lt;A&gt; combiner();
  16. /**
  17. * Perform the final transformation from the intermediate accumulation type
  18. */
  19. Function&lt;A, R&gt; finisher();
  20. /**
  21. * Returns a Set of Collector.Characteristics indicating
  22. * the characteristics of this Collector. This set should be immutable.
  23. */
  24. Set&lt;Characteristics&gt; characteristics();
  25. }

Where T is the generic type of the items in the stream to be collected.
A is the type of the accumulator, the object on which the partial result will be accumulated during the collection process.
R is the type of the object (typically, but not always, the collection) resulting
from the collect operation

Now let's look at the custom LanguageCollector

  1. public class LanguageCollector
  2. implements Collector&lt;Item, Map&lt;String, Language&gt;, Map&lt;String, Language&gt;&gt; {
  3. /**
  4. * The supplier method has to return a Supplier of an empty accumulator - a parameterless
  5. * function that when invoked creates an instance of an empty accumulator used during the
  6. * collection process.
  7. */
  8. @Override
  9. public Supplier&lt;Map&lt;String, Language&gt;&gt; supplier() {
  10. return HashMap::new;
  11. }
  12. /**
  13. * The accumulator method returns the function that performs the reduction operation. When
  14. * traversing the nth element in the stream, this function is applied with two arguments, the
  15. * accumulator being the result of the reduction (after having collected the first n–1 items of
  16. * the stream) and the nth element itself. The function returns void because the accumulator is
  17. * modified in place, meaning that its internal state is changed by the function application to
  18. * reflect the effect of the traversed element
  19. */
  20. @Override
  21. public BiConsumer&lt;Map&lt;String, Language&gt;, Item&gt; accumulator() {
  22. return (map, item) -&gt; {
  23. if (item.getLanguageName() == null) {
  24. item.setLanguageName(&quot;unknown&quot;);
  25. } else if (map.containsKey(item.getLanguageName())) {
  26. map.get(item.getLanguageName()).getItems().add(item.getName());
  27. map.get(item.getLanguageName())
  28. .setNumberItems(map.get(item.getLanguageName()).getNumberItems() + 1);
  29. } else {
  30. Language language = new Language(item.getLanguageName(), 1);
  31. language.add(item.getName());
  32. map.put(item.getLanguageName(), language);
  33. }
  34. };
  35. }
  36. /**
  37. * The combiner method, return a function used by the reduction operation, defines how the
  38. * accumulators resulting from the reduction of different subparts of the stream are combined
  39. * when the subparts are processed in parallel
  40. */
  41. @Override
  42. public BinaryOperator&lt;Map&lt;String, Language&gt;&gt; combiner() {
  43. return (map1, map2) -&gt; {
  44. map1.putAll(map2);
  45. return map1;
  46. };
  47. }
  48. /**
  49. * The finisher() method needs to return a function which transforms the accumulator to the
  50. * final result. In this case, the accumulator is the final result as well. Therefore it is
  51. * possible to return the identity function
  52. */
  53. @Override
  54. public Function&lt;Map&lt;String, Language&gt;, Map&lt;String, Language&gt;&gt; finisher() {
  55. return Function.identity();
  56. }
  57. /**
  58. * The characteristics, returns an immutable set of Characteristics, defining the behavior of
  59. * the collector—in particular providing hints about whether the stream can be reduced in
  60. * parallel and which optimizations are valid when doing so
  61. */
  62. @Override
  63. public Set&lt;Characteristics&gt; characteristics() {
  64. return Collections.unmodifiableSet(
  65. EnumSet.of(Characteristics.IDENTITY_FINISH));
  66. }
  67. /**
  68. * Static method to create LanguageCollector
  69. */
  70. public static LanguageCollector toLanguage() {
  71. return new LanguageCollector();
  72. }
  73. }

I have modified your classes at little bit to (to follow the naming convention and more for readable accumulator operation).

Class Item

  1. public class Item {
  2. private String name;
  3. private String languageName;
  4. public Item(String name, String languageName) {
  5. this.name = name;
  6. this.languageName = languageName;
  7. }
  8. //Getter and Setter
  9. }

Class Language

  1. public class Language {
  2. private String languageName;
  3. private int numberItems;
  4. private LinkedList&lt;String&gt; items;
  5. public Language(String languageName, int numberItems) {
  6. this.languageName = languageName;
  7. this.numberItems = numberItems;
  8. items = new LinkedList&lt;&gt;();
  9. }
  10. public void add(String item) {
  11. items.add(item);
  12. }
  13. // Getter and Setter
  14. public String toString() {
  15. return &quot;Language(languageName=&quot; + this.getLanguageName() + &quot;, numberItems=&quot; + this.getNumberItems() + &quot;, items=&quot; + this.getItems() + &quot;)&quot;;
  16. }
  17. }

Running code

  1. public static void main(String[] args) {
  2. List&lt;Item&gt; items =
  3. Arrays.asList(
  4. new Item(&quot;ItemA&quot;, &quot;Java&quot;),
  5. new Item(&quot;ItemB&quot;, &quot;Python&quot;),
  6. new Item(&quot;ItemC&quot;, &quot;Java&quot;),
  7. new Item(&quot;ItemD&quot;, &quot;Ruby&quot;),
  8. new Item(&quot;ItemE&quot;, &quot;Python&quot;));
  9. Map&lt;String, Language&gt; languages = items.stream().collect(LanguageCollector.toLanguage());
  10. System.out.println(languages);
  11. }

prints

  1. {Java=Language(languageName=Java, numberItems=2, items=[ItemA, ItemC]), Ruby=Language(languageName=Ruby, numberItems=1, items=[ItemD]), Python=Language(languageName=Python, numberItems=2, items=[ItemB, ItemE])}

For more information please read book 'Modern Java in Action: Lambdas, streams, functional and reactive programming' chapter 6.5 or check this link

huangapple
  • 本文由 发表于 2020年8月5日 22:29:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/63267373.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定