解決策 1:HashSet
ArrayList
一意性制約を使用してファイルを に読み込むという差し迫った問題に対する適切な解決策は、単純に表示されたHashSet
アイテムの を保持することです。行を処理する前に、そのキーがまだセットにないことを確認します。そうでない場合は、セットにキーを追加して完了としてマークし、結果に行データを追加しますArrayList
。
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
ArrayList<String[]> data = new ArrayList<>();
HashSet<String> seen = new HashSet<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
if (!seen.contains(key)) {
data.add(Arrays.copyOfRange(split, 2, split.length));
seen.add(key);
}
}
}
for (String[] row : data) {
System.out.println(Arrays.toString(row));
}
}
}
解決策 #2: LinkedHashMap
/LinkedHashSet
この特定のデータセットにはキーと値のペアがあるため、すべてをLinkedHashMap<String, ArrayList<String>>
(のドキュメントを参照LinkedHashMap
ArrayList<String>
) にまとめることができます。String[]
ここでは任意です。任意のデータ値である可能性があります)。このバージョンでは、最も古いキーではなく、最近見たキーを簡単に保存できることに注意してください (!data.containsKey(key)
テストを削除します)。
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
LinkedHashMap<String, ArrayList<String>> data = new LinkedHashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
if (!data.containsKey(key)) {
ArrayList<String> val = new ArrayList<>();
String[] sub = Arrays.copyOfRange(split, 2, split.length);
Collections.addAll(val, sub);
data.put(key, val);
}
}
}
for (Map.Entry<String, ArrayList<String>> e : data.entrySet()) {
System.out.println(e.getKey() + " => " + e.getValue());
}
}
}
解決策 #3:ArrayListSet
上記の例は、かなり狭いユース ケースを表しています。ArrayListSet
これは、一意性を維持しながら、通常のリストの動作 ( add
/ set
/remove
など) を維持する一般的なクラスのスケッチです。
基本的に、このクラスはこの投稿の解決策 #1 (HashSet
と組み合わせてArrayList
) の抽象化ですが、わずかに異なるフレーバーがあります (データ自体はキーではなく一意性を判断するために使用されますが、より正確な " ArrayList
" 構造です)。
このクラスは、効率の問題 (ArrayList#contains
線形であるため、些細な場合を除いてその解決策を拒否する必要があります)、順序付けの欠如 (すべてを a に直接格納することHashSet
は役に立ちません)、ArrayList
操作の欠如 (LinkedHashSet
それ以外の場合は最善の解決策ですが、にインデックスを付けることができないため、ArrayList
) の真の代替にはなりません。
a のHashMap<E, index>
代わりに aを使用すると、HashSet
速度remove(Object o)
とindexOf(Object o)
機能が向上します (ただし、速度は低下しますsort
)。リニアremove(Object o)
は、プレーンよりも主な欠点HashSet
です。
import java.util.*;
public class ArrayListSet<E> implements Iterable<E>, Set<E> {
private ArrayList<E> list;
private HashSet<E> set;
public ArrayListSet() {
list = new ArrayList<>();
set = new HashSet<>();
}
public boolean add(E e) {
return set.add(e) && list.add(e);
}
public boolean add(int i, E e) {
if (!set.add(e)) return false;
list.add(i, e);
return true;
}
public void clear() {
list.clear();
set.clear();
}
public boolean contains(Object o) {
return set.contains(o);
}
public E get(int i) {
return list.get(i);
}
public boolean isEmpty() {
return list.isEmpty();
}
public E remove(int i) {
E e = list.remove(i);
set.remove(e);
return e;
}
public boolean remove(Object o) {
if (set.remove(o)) {
list.remove(o);
return true;
}
return false;
}
public boolean set(int i, E e) {
if (set.contains(e)) return false;
set.add(e);
set.remove(list.set(i, e));
return true;
}
public int size() {
return list.size();
}
public void sort(Comparator<? super E> c) {
Collections.sort(list, c);
}
public Iterator<E> iterator() {
return list.iterator();
}
public boolean addAll(Collection<? extends E> c) {
int before = size();
for (E e : c) add(e);
return size() == before;
}
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public boolean removeAll(Collection<?> c) {
return set.removeAll(c) && list.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return set.retainAll(c) && list.retainAll(c);
}
public Object[] toArray() {
return list.toArray();
}
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
使用例:
public class ArrayListSetDriver {
public static void main(String[] args) {
ArrayListSet<String> fruit = new ArrayListSet<>();
fruit.add("apple");
fruit.add("banana");
fruit.add("kiwi");
fruit.add("strawberry");
fruit.add("apple");
fruit.add("strawberry");
for (String item : fruit) {
System.out.print(item + " "); // => apple banana kiwi strawberry
}
fruit.remove("kiwi");
fruit.remove(1);
fruit.add(0, "banana");
fruit.set(2, "cranberry");
fruit.set(0, "cranberry");
System.out.println();
for (int i = 0; i < fruit.size(); i++) {
System.out.print(fruit.get(i) + " "); // => banana apple cranberry
}
System.out.println();
}
}
解決策 4:ArrayListMap
ArrayListSet
このクラスは、保存したいデータとそれに関連付けられたキーが同じでない可能性があるという欠点を解決します。このクラスはput
、基になる に格納されているデータとは異なるオブジェクトに一意性を強制するメソッドを提供しますArrayList
。これは、このスレッドで提起された元の問題を解決するために必要なものです。これにより、 の順序付けと反復が可能になりますがArrayList
、高速なルックアップと の一意性プロパティが得られますHashMap
。HashMap
には、 内のインデックスの場所にマップされた一意の値が含まれています。これによりArrayList
、順序が強制され、反復が提供されます。
HashSet
このアプローチは、ソリューション #1で を使用する際のスケーラビリティの問題を解決します。このアプローチは、ファイルをすばやく読み取るには問題なく機能しますが、抽象化がなければ、すべての整合性操作を手動で処理し、複数の関数にまたがって時間の経過とともにそのコントラクトを強制する必要がある場合、複数の生データ構造を渡す必要があります。
と同様にArrayListSet
、これは完全な実装ではなく概念実証と見なすことができます。
import java.util.*;
public class ArrayListMap<K, V> implements Iterable<V>, Map<K, V> {
private ArrayList<V> list;
private HashMap<K, Integer> map;
public ArrayListMap() {
list = new ArrayList<>();
map = new HashMap<>();
}
public void clear() {
list.clear();
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return list.contains(value);
}
public V get(int i) {
return list.get(i);
}
public boolean isEmpty() {
return map.isEmpty();
}
public V get(Object key) {
return list.get(map.get(key));
}
public V put(K key, V value) {
if (map.containsKey(key)) {
int i = map.get(key);
V v = list.get(i);
list.set(i, value);
return v;
}
list.add(value);
map.put(key, list.size() - 1);
return null;
}
public V putIfAbsent(K key, V value) {
if (map.containsKey(key)) {
if (list.get(map.get(key)) == null) {
list.set(map.get(key), value);
return null;
}
return list.get(map.get(key));
}
return put(key, value);
}
public V remove(int i) {
V v = list.remove(i);
for (Map.Entry<K, Integer> entry : map.entrySet()) {
if (entry.getValue() == i) {
map.remove(entry.getKey());
break;
}
}
decrementMapIndices(i);
return v;
}
public V remove(Object key) {
if (map.containsKey(key)) {
int i = map.remove(key);
V v = list.get(i);
list.remove(i);
decrementMapIndices(i);
return v;
}
return null;
}
private void decrementMapIndices(int start) {
for (Map.Entry<K, Integer> entry : map.entrySet()) {
int i = entry.getValue();
if (i > start) {
map.put(entry.getKey(), i - 1);
}
}
}
public int size() {
return list.size();
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> es = new HashSet<>();
for (Map.Entry<K, Integer> entry : map.entrySet()) {
es.add(new AbstractMap.SimpleEntry<>(
entry.getKey(), list.get(entry.getValue())
));
}
return es;
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<V> values() {
return list;
}
public Iterator<V> iterator() {
return list.iterator();
}
public Object[] toArray() {
return list.toArray();
}
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
元の問題で実行中のクラスは次のとおりです。
import java.io.*;
public class Main {
public static void main(String[] args)
throws FileNotFoundException, IOException {
String file = "prova.txt";
ArrayListMap<String, String[]> data = new ArrayListMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
for (String line; (line = br.readLine()) != null;) {
String[] split = line.split("\\s+");
String key = split[0] + " " + split[1];
String[] sub = Arrays.copyOfRange(split, 2, split.length);
data.putIfAbsent(key, sub);
}
}
for (Map.Entry<String, String[]> e : data.entrySet()) {
System.out.println(e.getKey() + " => " +
java.util.Arrays.toString(e.getValue()));
}
for (String[] a : data) {
System.out.println(java.util.Arrays.toString(a));
}
}
}