Optimize insertion from ArrayList to HashMap

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

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

String name;
String language_name;

Language

String language_name;
int numberItems; 
LinkedList&lt;String&gt; Items;

项目

String name;
String language_name;

语言

String language_name;
int numberItems; 
LinkedList&lt;String&gt; Items;

I solved this as follows:

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

        ArrayList&lt;Item&gt; items; // given array of items
		HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();

		items.forEach(item -&gt; {
			/** case 1: language isn&#39;t specified */
			if (item.getLanguageName() == null) {
				item.setLanguageName(&quot;unknown&quot;);
			}
			/** case 2: language already added */
			if (languages.containsKey(item.getLanguageName())) {
				languages.get(item.getLanguageName()).getItems().add(item.getName());
				languages.get(item.getLanguageName())
						.setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
			} else {
				/** case 3: language isn&#39;t added yet */
				LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
				languageItems.add(item.getName());
				Language language = new Language(item.getLanguageName(), 1, languageItems);
				languages.put(item.getLanguageName(), language);
			}
		});

我如下解决了这个问题:

        ArrayList&lt;Item&gt; items; // 给定的项目数组
		HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();

		items.forEach(item -&gt; {
			/** 情况1:语言未指定 */
			if (item.getLanguageName() == null) {
				item.setLanguageName(&quot;unknown&quot;);
			}
			/** 情况2:语言已添加 */
			if (languages.containsKey(item.getLanguageName())) {
				languages.get(item.getLanguageName()).getItems().add(item.getName());
				languages.get(item.getLanguageName())
						.setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
			} else {
				/** 情况3:语言尚未添加 */
				LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
				languageItems.add(item.getName());
				Language language = new Language(item.getLanguageName(), 1, languageItems);
				languages.put(item.getLanguageName(), language);
			}
		});

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

String name;
String language_name;

Language

String language_name;
int numberItems; 
LinkedList&lt;String&gt; Items;

I solved this as follows:

        ArrayList&lt;Item&gt; items; // given array of items
		HashMap&lt;String, Language&gt; languages = new HashMap&lt;String, Language&gt;();

		items.forEach(item -&gt; {
			/** case 1: language isn&#39;t specified */
			if (item.getLanguageName() == null) {
				item.setLanguageName(&quot;unknown&quot;);
			}
			/** case 2: language already added */
			if (languages.containsKey(item.getLanguageName())) {
				languages.get(item.getLanguageName()).getItems().add(item.getName());
				languages.get(item.getLanguageName())
						.setNumberItems(languages.get(item.getLanguageName()).getNumberItems() + 1);
			} else {
				/** case 3: language isn&#39;t added yet */
				LinkedList&lt;String&gt; languageItems = new LinkedList&lt;String&gt;();
				languageItems.add(item.getName());
				Language language = new Language(item.getLanguageName(), 1, languageItems);
				languages.put(item.getLanguageName(), language);
			}
		});

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.

HashMap&lt;String, List&lt;Items&gt;&gt; itemsGroupedByLanguage =
 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.

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

答案2

得分: 0

tl;dr

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

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

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

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

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

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

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

运行代码

public static void main(String[] args) {
    List<Item> items =
        Arrays.asList(
            new Item("ItemA", "Java"),
            new Item("ItemB", "Python"),
            new Item("ItemC", "Java"),
            new Item("ItemD", "Ruby"),
            new Item("ItemE", "Python"));

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

    System.out.println(languages);
}

打印结果

{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

public interface Collector&lt;T, A, R&gt; {
    /**
     * A function that creates and returns a new mutable result container.
     */
    Supplier&lt;A&gt; supplier();

    /**
     * A function that folds a value into a mutable result container.
     */
    BiConsumer&lt;A, T&gt; accumulator();

    /**
     * A function that accepts two partial results and merges them.  The
     * combiner function may fold state from one argument into the other and
     * return that, or may return a new result container.
     */
    BinaryOperator&lt;A&gt; combiner();

    /**
     * Perform the final transformation from the intermediate accumulation type
     */
    Function&lt;A, R&gt; finisher();

    /**
     * Returns a  Set of Collector.Characteristics indicating
     * the characteristics of this Collector.  This set should be immutable.
     */
    Set&lt;Characteristics&gt; characteristics();
}

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

  public class LanguageCollector
      implements Collector&lt;Item, Map&lt;String, Language&gt;, Map&lt;String, Language&gt;&gt; {

    /**
     * The supplier method has to return a Supplier of an empty accumulator - a parameterless
     * function that when invoked creates an instance of an empty accumulator used during the
     * collection process.
     */
    @Override
    public Supplier&lt;Map&lt;String, Language&gt;&gt; supplier() {

      return HashMap::new;
    }

    /**
     * The accumulator method returns the function that performs the reduction operation. When
     * traversing the nth element in the stream, this function is applied with two arguments, the
     * accumulator being the result of the reduction (after having collected the first n–1 items of
     * the stream) and the nth element itself. The function returns void because the accumulator is
     * modified in place, meaning that its internal state is changed by the function application to
     * reflect the effect of the traversed element
     */
    @Override
    public BiConsumer&lt;Map&lt;String, Language&gt;, Item&gt; accumulator() {

      return (map, item) -&gt; {
        if (item.getLanguageName() == null) {
          item.setLanguageName(&quot;unknown&quot;);
        } else if (map.containsKey(item.getLanguageName())) {
          map.get(item.getLanguageName()).getItems().add(item.getName());
          map.get(item.getLanguageName())
              .setNumberItems(map.get(item.getLanguageName()).getNumberItems() + 1);
        } else {
          Language language = new Language(item.getLanguageName(), 1);
          language.add(item.getName());
          map.put(item.getLanguageName(), language);
        }
      };
    }

    /**
     * The combiner method, return a function used by the reduction operation, defines how the
     * accumulators resulting from the reduction of different subparts of the stream are combined
     * when the subparts are processed in parallel
     */
    @Override
    public BinaryOperator&lt;Map&lt;String, Language&gt;&gt; combiner() {
      return (map1, map2) -&gt; {
          map1.putAll(map2);
          return map1;
       };
    }

    /**
     * The finisher() method needs to return a function which transforms the accumulator to the
     * final result. In this case, the accumulator is the final result as well. Therefore it is
     * possible to return the identity function
     */
    @Override
    public Function&lt;Map&lt;String, Language&gt;, Map&lt;String, Language&gt;&gt; finisher() {
      return Function.identity();
    }

    /**
     * The characteristics, returns an immutable set of Characteristics, defining the behavior of
     * the collector—in particular providing hints about whether the stream can be reduced in
     * parallel and which optimizations are valid when doing so
     */
    @Override
    public Set&lt;Characteristics&gt; characteristics() {
      return Collections.unmodifiableSet(
          EnumSet.of(Characteristics.IDENTITY_FINISH));
    }

    /**
     * Static method to create LanguageCollector
     */
    public static LanguageCollector toLanguage() {
      return new LanguageCollector();
    }
  }

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

Class Item

public class Item {
    private String name;
    private String languageName;

    public Item(String name, String languageName) {
      this.name = name;
      this.languageName = languageName;
    }
    //Getter and Setter
  }

Class Language

public class Language {
    private String languageName;
    private int numberItems;
    private LinkedList&lt;String&gt; items;

    public Language(String languageName, int numberItems) {
      this.languageName = languageName;
      this.numberItems = numberItems;
      items = new LinkedList&lt;&gt;();
    }

    public void add(String item) {
      items.add(item);
    }

    // Getter and Setter

    public String toString() {
      return &quot;Language(languageName=&quot; + this.getLanguageName() + &quot;, numberItems=&quot; + this.getNumberItems() + &quot;, items=&quot; + this.getItems() + &quot;)&quot;;
    }
  }

Running code

public static void main(String[] args) {
    List&lt;Item&gt; items =
        Arrays.asList(
            new Item(&quot;ItemA&quot;, &quot;Java&quot;),
            new Item(&quot;ItemB&quot;, &quot;Python&quot;),
            new Item(&quot;ItemC&quot;, &quot;Java&quot;),
            new Item(&quot;ItemD&quot;, &quot;Ruby&quot;),
            new Item(&quot;ItemE&quot;, &quot;Python&quot;));

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

    System.out.println(languages);
  }

prints

{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:

确定